/* * Copyright 2011 Advanced Micro Devices, Inc. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. *
*/ /* * Authors: * Christian König <deathsimple@vodafone.de>
*/
/* These are common relative offsets for all asics, from uvd_7_0_offset.h, */ #define UVD_GPCOM_VCPU_CMD 0x03c3 #define UVD_GPCOM_VCPU_DATA0 0x03c4 #define UVD_GPCOM_VCPU_DATA1 0x03c5 #define UVD_NO_OP 0x03ff #define UVD_BASE_SI 0x3800
/* * amdgpu_uvd_cs_ctx - Command submission parser context * * Used for emulating virtual memory support on UVD 4.2.
*/ struct amdgpu_uvd_cs_ctx { struct amdgpu_cs_parser *parser; unsignedint reg, count; unsignedint data0, data1; unsignedint idx; struct amdgpu_ib *ib;
/* does the IB has a msg command */ bool has_msg_cmd;
/* * Limit the number of UVD handles depending on microcode major * and minor versions. The firmware version which has 40 UVD * instances support is 1.80. So all subsequent versions should * also have the same support.
*/ if ((version_major > 0x01) ||
((version_major == 0x01) && (version_minor >= 0x50)))
adev->uvd.max_handles = AMDGPU_MAX_UVD_HANDLES;
for (i = 0; i < AMDGPU_MAX_UVD_ENC_RINGS; ++i)
amdgpu_ring_fini(&adev->uvd.inst[j].ring_enc[i]);
}
amdgpu_bo_free_kernel(&adev->uvd.ib_bo, NULL, &addr);
amdgpu_ucode_release(&adev->uvd.fw);
return 0;
}
/** * amdgpu_uvd_entity_init - init entity * * @adev: amdgpu_device pointer * @ring: amdgpu_ring pointer to check * * Initialize the entity used for handle management in the kernel driver.
*/ int amdgpu_uvd_entity_init(struct amdgpu_device *adev, struct amdgpu_ring *ring)
{ if (ring == &adev->uvd.inst[0].ring) { struct drm_gpu_scheduler *sched = &ring->sched; int r;
r = drm_sched_entity_init(&adev->uvd.entity, DRM_SCHED_PRIORITY_NORMAL,
&sched, 1, NULL); if (r) {
DRM_ERROR("Failed setting up UVD kernel entity.\n"); return r;
}
}
return 0;
}
int amdgpu_uvd_prepare_suspend(struct amdgpu_device *adev)
{ unsignedint size; void *ptr; int i, j, idx;
cancel_delayed_work_sync(&adev->uvd.idle_work);
/* only valid for physical mode */ if (adev->asic_type < CHIP_POLARIS10) { for (i = 0; i < adev->uvd.max_handles; ++i) if (atomic_read(&adev->uvd.handles[i])) break;
if (i == adev->uvd.max_handles) return 0;
}
for (j = 0; j < adev->uvd.num_uvd_inst; ++j) { if (adev->uvd.harvest_config & (1 << j)) continue; if (adev->uvd.inst[j].vcpu_bo == NULL) continue;
adev->uvd.inst[j].saved_bo = kvmalloc(size, GFP_KERNEL); if (!adev->uvd.inst[j].saved_bo) return -ENOMEM;
if (drm_dev_enter(adev_to_drm(adev), &idx)) { /* re-write 0 since err_event_athub will corrupt VCPU buffer */ if (amdgpu_ras_intr_triggered())
memset(adev->uvd.inst[j].saved_bo, 0, size); else
memcpy_fromio(adev->uvd.inst[j].saved_bo, ptr, size);
drm_dev_exit(idx);
}
}
return 0;
}
int amdgpu_uvd_suspend(struct amdgpu_device *adev)
{ if (amdgpu_ras_intr_triggered())
DRM_WARN("UVD VCPU state may lost due to RAS ERREVENT_ATHUB_INTERRUPT\n");
return 0;
}
int amdgpu_uvd_resume(struct amdgpu_device *adev)
{ unsignedint size; void *ptr; int i, idx;
for (i = 0; i < adev->uvd.num_uvd_inst; i++) { if (adev->uvd.harvest_config & (1 << i)) continue; if (adev->uvd.inst[i].vcpu_bo == NULL) return -EINVAL;
lo = amdgpu_ib_get_value(ctx->ib, ctx->data0);
hi = amdgpu_ib_get_value(ctx->ib, ctx->data1);
addr = ((uint64_t)lo) | (((uint64_t)hi) << 32);
return addr;
}
/** * amdgpu_uvd_cs_pass1 - first parsing round * * @ctx: UVD parser context * * Make sure UVD message and feedback buffers are in VRAM and * nobody is violating an 256MB boundary.
*/ staticint amdgpu_uvd_cs_pass1(struct amdgpu_uvd_cs_ctx *ctx)
{ struct ttm_operation_ctx tctx = { false, false }; struct amdgpu_bo_va_mapping *mapping; struct amdgpu_bo *bo;
uint32_t cmd;
uint64_t addr = amdgpu_uvd_get_addr_from_ctx(ctx); int r = 0;
r = amdgpu_cs_find_mapping(ctx->parser, addr, &bo, &mapping); if (r) {
DRM_ERROR("Can't find BO for addr 0x%08llx\n", addr); return r;
}
if (!ctx->parser->adev->uvd.address_64_bit) { /* check if it's a message or feedback command */
cmd = amdgpu_ib_get_value(ctx->ib, ctx->idx) >> 1; if (cmd == 0x0 || cmd == 0x3) { /* yes, force it into VRAM */
uint32_t domain = AMDGPU_GEM_DOMAIN_VRAM;
if (dpb_size < min_dpb_size) {
DRM_ERROR("Invalid dpb_size in UVD message (%d / %d)!\n",
dpb_size, min_dpb_size); return -EINVAL;
}
buf_sizes[0x1] = dpb_size;
buf_sizes[0x2] = image_size;
buf_sizes[0x4] = min_ctx_size; /* store image width to adjust nb memory pstate */
adev->uvd.decode_image_width = width; return 0;
}
/** * amdgpu_uvd_cs_msg - handle UVD message * * @ctx: UVD parser context * @bo: buffer object containing the message * @offset: offset into the buffer object * * Peek into the UVD message and extract the session id. * Make sure that we don't open up to many sessions.
*/ staticint amdgpu_uvd_cs_msg(struct amdgpu_uvd_cs_ctx *ctx, struct amdgpu_bo *bo, unsignedint offset)
{ struct amdgpu_device *adev = ctx->parser->adev;
int32_t *msg, msg_type, handle; void *ptr; long r; int i;
if (offset & 0x3F) {
DRM_ERROR("UVD messages must be 64 byte aligned!\n"); return -EINVAL;
}
r = amdgpu_bo_kmap(bo, &ptr); if (r) {
DRM_ERROR("Failed mapping the UVD) message (%ld)!\n", r); return r;
}
switch (msg_type) { case 0: /* it's a create msg, calc image size (width * height) */
amdgpu_bo_kunmap(bo);
/* try to alloc a new handle */ for (i = 0; i < adev->uvd.max_handles; ++i) { if (atomic_read(&adev->uvd.handles[i]) == handle) {
DRM_ERROR(")Handle 0x%x already in use!\n",
handle); return -EINVAL;
}
case 2: /* it's a destroy msg, free the handle */ for (i = 0; i < adev->uvd.max_handles; ++i)
atomic_cmpxchg(&adev->uvd.handles[i], handle, 0);
amdgpu_bo_kunmap(bo); return 0;
default:
DRM_ERROR("Illegal UVD message type (%d)!\n", msg_type);
}
/* first round only required on chips without UVD 64 bit address support */ if (!parser->adev->uvd.address_64_bit) { /* first round, make sure the buffers are actually in the UVD segment */
r = amdgpu_uvd_cs_packets(&ctx, amdgpu_uvd_cs_pass1); if (r) return r;
}
/* second round, patch buffer addresses into the command stream */
r = amdgpu_uvd_cs_packets(&ctx, amdgpu_uvd_cs_pass2); if (r) return r;
if (!ctx.has_msg_cmd) {
DRM_ERROR("UVD-IBs need a msg command!\n"); return -EINVAL;
}
if (direct) {
r = amdgpu_job_submit_direct(job, ring, &f); if (r) goto err_free;
} else {
r = drm_sched_job_add_resv_dependencies(&job->base,
bo->tbo.base.resv,
DMA_RESV_USAGE_KERNEL); if (r) goto err_free;
if (fence)
*fence = dma_fence_get(f);
dma_fence_put(f);
return 0;
err_free:
amdgpu_job_free(job); return r;
}
/* multiple fence commands without any stream commands in between can * crash the vcpu so just try to emmit a dummy create/destroy msg to * avoid this
*/ int amdgpu_uvd_get_create_msg(struct amdgpu_ring *ring, uint32_t handle, struct dma_fence **fence)
{ struct amdgpu_device *adev = ring->adev; struct amdgpu_bo *bo = adev->uvd.ib_bo;
uint32_t *msg; int i;
void amdgpu_uvd_ring_end_use(struct amdgpu_ring *ring)
{ if (!amdgpu_sriov_vf(ring->adev))
schedule_delayed_work(&ring->adev->uvd.idle_work, UVD_IDLE_TIMEOUT);
}
/** * amdgpu_uvd_ring_test_ib - test ib execution * * @ring: amdgpu_ring pointer * @timeout: timeout value in jiffies, or MAX_SCHEDULE_TIMEOUT * * Test if we can successfully execute an IB
*/ int amdgpu_uvd_ring_test_ib(struct amdgpu_ring *ring, long timeout)
{ struct dma_fence *fence; long r;
r = amdgpu_uvd_get_create_msg(ring, 1, &fence); if (r) goto error;
r = dma_fence_wait_timeout(fence, false, timeout);
dma_fence_put(fence); if (r == 0)
r = -ETIMEDOUT; if (r < 0) goto error;
r = amdgpu_uvd_get_destroy_msg(ring, 1, true, &fence); if (r) goto error;
r = dma_fence_wait_timeout(fence, false, timeout); if (r == 0)
r = -ETIMEDOUT; elseif (r > 0)
r = 0;
dma_fence_put(fence);
error: return r;
}
/** * amdgpu_uvd_used_handles - returns used UVD handles * * @adev: amdgpu_device pointer * * Returns the number of UVD handles in use
*/
uint32_t amdgpu_uvd_used_handles(struct amdgpu_device *adev)
{ unsignedint i;
uint32_t used_handles = 0;
for (i = 0; i < adev->uvd.max_handles; ++i) { /* * Handles can be freed in any order, and not * necessarily linear. So we need to count * all non-zero handles.
*/ if (atomic_read(&adev->uvd.handles[i]))
used_handles++;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.