/* * Copyright 2014 Advanced Micro Devices, Inc. * * 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, sublicense, * 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 above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * 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 NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. * * Authors: Christian König <christian.koenig@amd.com>
*/
/** * uvd_v6_0_enc_ring_set_wptr - set enc write pointer * * @ring: amdgpu_ring pointer * * Commits the enc write pointer to the hardware
*/ staticvoid uvd_v6_0_enc_ring_set_wptr(struct amdgpu_ring *ring)
{ struct amdgpu_device *adev = ring->adev;
if (ring == &adev->uvd.inst->ring_enc[0])
WREG32(mmUVD_RB_WPTR,
lower_32_bits(ring->wptr)); else
WREG32(mmUVD_RB_WPTR2,
lower_32_bits(ring->wptr));
}
/** * uvd_v6_0_enc_ring_test_ring - test if UVD ENC ring is working * * @ring: the engine to test on *
*/ staticint uvd_v6_0_enc_ring_test_ring(struct amdgpu_ring *ring)
{ struct amdgpu_device *adev = ring->adev;
uint32_t rptr; unsigned i; int r;
for (i = 0; i < adev->usec_timeout; i++) { if (amdgpu_ring_get_rptr(ring) != rptr) break;
udelay(1);
}
if (i >= adev->usec_timeout)
r = -ETIMEDOUT;
return r;
}
/** * uvd_v6_0_enc_get_create_msg - generate a UVD ENC create msg * * @ring: ring we should submit the msg to * @handle: session handle to use * @bo: amdgpu object for which we query the offset * @fence: optional fence to return * * Open up a stream for HW test
*/ staticint uvd_v6_0_enc_get_create_msg(struct amdgpu_ring *ring, uint32_t handle, struct amdgpu_bo *bo, struct dma_fence **fence)
{ constunsigned ib_size_dw = 16; struct amdgpu_job *job; struct amdgpu_ib *ib; struct dma_fence *f = NULL;
uint64_t addr; int i, r;
r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4,
AMDGPU_IB_POOL_DIRECT, &job); if (r) return r;
ib->ptr[ib->length_dw++] = 0x00000008;
ib->ptr[ib->length_dw++] = 0x08000001; /* op initialize */
for (i = ib->length_dw; i < ib_size_dw; ++i)
ib->ptr[i] = 0x0;
r = amdgpu_job_submit_direct(job, ring, &f); if (r) goto err;
if (fence)
*fence = dma_fence_get(f);
dma_fence_put(f); return 0;
err:
amdgpu_job_free(job); return r;
}
/** * uvd_v6_0_enc_get_destroy_msg - generate a UVD ENC destroy msg * * @ring: ring we should submit the msg to * @handle: session handle to use * @bo: amdgpu object for which we query the offset * @fence: optional fence to return * * Close up a stream for HW test or if userspace failed to do so
*/ staticint uvd_v6_0_enc_get_destroy_msg(struct amdgpu_ring *ring,
uint32_t handle, struct amdgpu_bo *bo, struct dma_fence **fence)
{ constunsigned ib_size_dw = 16; struct amdgpu_job *job; struct amdgpu_ib *ib; struct dma_fence *f = NULL;
uint64_t addr; int i, r;
r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4,
AMDGPU_IB_POOL_DIRECT, &job); if (r) return r;
ib->ptr[ib->length_dw++] = 0x00000008;
ib->ptr[ib->length_dw++] = 0x08000002; /* op close session */
for (i = ib->length_dw; i < ib_size_dw; ++i)
ib->ptr[i] = 0x0;
r = amdgpu_job_submit_direct(job, ring, &f); if (r) goto err;
if (fence)
*fence = dma_fence_get(f);
dma_fence_put(f); return 0;
err:
amdgpu_job_free(job); return r;
}
/** * uvd_v6_0_enc_ring_test_ib - test if UVD ENC IBs are working * * @ring: the engine to test on * @timeout: timeout value in jiffies, or MAX_SCHEDULE_TIMEOUT *
*/ staticint uvd_v6_0_enc_ring_test_ib(struct amdgpu_ring *ring, long timeout)
{ struct dma_fence *fence = NULL; struct amdgpu_bo *bo = ring->adev->uvd.ib_bo; long r;
r = uvd_v6_0_enc_get_create_msg(ring, 1, bo, NULL); if (r) goto error;
r = uvd_v6_0_enc_get_destroy_msg(ring, 1, bo, &fence); if (r) goto error;
r = dma_fence_wait_timeout(fence, false, timeout); if (r == 0)
r = -ETIMEDOUT; elseif (r > 0)
r = 0;
if (!(adev->flags & AMD_IS_APU) &&
(RREG32_SMC(ixCC_HARVEST_FUSES) & CC_HARVEST_FUSES__UVD_DISABLE_MASK)) return -ENOENT;
uvd_v6_0_set_ring_funcs(adev);
if (uvd_v6_0_enc_support(adev)) {
adev->uvd.num_enc_rings = 2;
uvd_v6_0_set_enc_ring_funcs(adev);
}
uvd_v6_0_set_irq_funcs(adev);
return 0;
}
staticint uvd_v6_0_sw_init(struct amdgpu_ip_block *ip_block)
{ struct amdgpu_ring *ring; int i, r; struct amdgpu_device *adev = ip_block->adev;
/* UVD TRAP */
r = amdgpu_irq_add_id(adev, AMDGPU_IRQ_CLIENTID_LEGACY, VISLANDS30_IV_SRCID_UVD_SYSTEM_MESSAGE, &adev->uvd.inst->irq); if (r) return r;
/* UVD ENC TRAP */ if (uvd_v6_0_enc_support(adev)) { for (i = 0; i < adev->uvd.num_enc_rings; ++i) {
r = amdgpu_irq_add_id(adev, AMDGPU_IRQ_CLIENTID_LEGACY, i + VISLANDS30_IV_SRCID_UVD_ENC_GEN_PURP, &adev->uvd.inst->irq); if (r) return r;
}
}
r = amdgpu_uvd_sw_init(adev); if (r) return r;
if (!uvd_v6_0_enc_support(adev)) { for (i = 0; i < adev->uvd.num_enc_rings; ++i)
adev->uvd.inst->ring_enc[i].funcs = NULL;
ring = &adev->uvd.inst->ring;
sprintf(ring->name, "uvd");
r = amdgpu_ring_init(adev, ring, 512, &adev->uvd.inst->irq, 0,
AMDGPU_RING_PRIO_DEFAULT, NULL); if (r) return r;
r = amdgpu_uvd_resume(adev); if (r) return r;
if (uvd_v6_0_enc_support(adev)) { for (i = 0; i < adev->uvd.num_enc_rings; ++i) {
ring = &adev->uvd.inst->ring_enc[i];
sprintf(ring->name, "uvd_enc%d", i);
r = amdgpu_ring_init(adev, ring, 512,
&adev->uvd.inst->irq, 0,
AMDGPU_RING_PRIO_DEFAULT, NULL); if (r) return r;
}
}
return r;
}
staticint uvd_v6_0_sw_fini(struct amdgpu_ip_block *ip_block)
{ int i, r; struct amdgpu_device *adev = ip_block->adev;
r = amdgpu_uvd_suspend(adev); if (r) return r;
if (uvd_v6_0_enc_support(adev)) { for (i = 0; i < adev->uvd.num_enc_rings; ++i)
amdgpu_ring_fini(&adev->uvd.inst->ring_enc[i]);
}
return amdgpu_uvd_sw_fini(adev);
}
/** * uvd_v6_0_hw_init - start and test UVD block * * @ip_block: Pointer to the amdgpu_ip_block for this hw instance. * * Initialize the hardware, boot up the VCPU and do some testing
*/ staticint uvd_v6_0_hw_init(struct amdgpu_ip_block *ip_block)
{ struct amdgpu_device *adev = ip_block->adev; struct amdgpu_ring *ring = &adev->uvd.inst->ring;
uint32_t tmp; int i, r;
if (uvd_v6_0_enc_support(adev)) { for (i = 0; i < adev->uvd.num_enc_rings; ++i) {
ring = &adev->uvd.inst->ring_enc[i];
r = amdgpu_ring_test_helper(ring); if (r) goto done;
}
}
done: if (!r) { if (uvd_v6_0_enc_support(adev))
DRM_INFO("UVD and UVD ENC initialized successfully.\n"); else
DRM_INFO("UVD initialized successfully.\n");
}
return r;
}
/** * uvd_v6_0_hw_fini - stop the hardware block * * @ip_block: Pointer to the amdgpu_ip_block for this hw instance. * * Stop the UVD block, mark ring as not ready any more
*/ staticint uvd_v6_0_hw_fini(struct amdgpu_ip_block *ip_block)
{ struct amdgpu_device *adev = ip_block->adev;
cancel_delayed_work_sync(&adev->uvd.idle_work);
if (RREG32(mmUVD_STATUS) != 0)
uvd_v6_0_stop(adev);
/* * Proper cleanups before halting the HW engine: * - cancel the delayed idle work * - enable powergating * - enable clockgating * - disable dpm * * TODO: to align with the VCN implementation, move the * jobs for clockgating/powergating/dpm setting to * ->set_powergating_state().
*/
cancel_delayed_work_sync(&adev->uvd.idle_work);
/* boot up the VCPU */
WREG32(mmUVD_SOFT_RESET, 0);
mdelay(10);
for (i = 0; i < 10; ++i) {
uint32_t status;
for (j = 0; j < 100; ++j) {
status = RREG32(mmUVD_STATUS); if (status & 2) break;
mdelay(10);
}
r = 0; if (status & 2) break;
DRM_ERROR("UVD not responding, trying to reset the VCPU!!!\n");
WREG32_FIELD(UVD_SOFT_RESET, VCPU_SOFT_RESET, 1);
mdelay(10);
WREG32_FIELD(UVD_SOFT_RESET, VCPU_SOFT_RESET, 0);
mdelay(10);
r = -1;
}
if (r) {
DRM_ERROR("UVD not responding, giving up!!!\n"); return r;
} /* enable master interrupt */
WREG32_P(mmUVD_MASTINT_EN,
(UVD_MASTINT_EN__VCPU_EN_MASK|UVD_MASTINT_EN__SYS_EN_MASK),
~(UVD_MASTINT_EN__VCPU_EN_MASK|UVD_MASTINT_EN__SYS_EN_MASK));
/* clear the bit 4 of UVD_STATUS */
WREG32_P(mmUVD_STATUS, 0, ~(2 << UVD_STATUS__VCPU_REPORT__SHIFT));
/* set the write pointer delay */
WREG32(mmUVD_RBC_RB_WPTR_CNTL, 0);
/* set the wb address */
WREG32(mmUVD_RBC_RB_RPTR_ADDR, (upper_32_bits(ring->gpu_addr) >> 2));
/* program the RB_BASE for ring buffer */
WREG32(mmUVD_LMI_RBC_RB_64BIT_BAR_LOW,
lower_32_bits(ring->gpu_addr));
WREG32(mmUVD_LMI_RBC_RB_64BIT_BAR_HIGH,
upper_32_bits(ring->gpu_addr));
/* Initialize the ring buffer's read and write pointers */
WREG32(mmUVD_RBC_RB_RPTR, 0);
if (enable && (adev->cg_flags & AMD_CG_SUPPORT_UVD_MGCG)) {
data = RREG32_UVD_CTX(ixUVD_CGC_MEM_CTRL);
data |= 0xfff;
WREG32_UVD_CTX(ixUVD_CGC_MEM_CTRL, data);
orig = data = RREG32(mmUVD_CGC_CTRL);
data |= UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK; if (orig != data)
WREG32(mmUVD_CGC_CTRL, data);
} else {
data = RREG32_UVD_CTX(ixUVD_CGC_MEM_CTRL);
data &= ~0xfff;
WREG32_UVD_CTX(ixUVD_CGC_MEM_CTRL, data);
orig = data = RREG32(mmUVD_CGC_CTRL);
data &= ~UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK; if (orig != data)
WREG32(mmUVD_CGC_CTRL, data);
}
}
if (enable) { /* wait for STATUS to clear */ if (uvd_v6_0_wait_for_idle(ip_block)) return -EBUSY;
uvd_v6_0_enable_clock_gating(adev, true); /* enable HW gates because UVD is idle */ /* uvd_v6_0_set_hw_clock_gating(adev); */
} else { /* disable HW gating and enable Sw gating */
uvd_v6_0_enable_clock_gating(adev, false);
}
uvd_v6_0_set_sw_clock_gating(adev); return 0;
}
staticint uvd_v6_0_set_powergating_state(struct amdgpu_ip_block *ip_block, enum amd_powergating_state state)
{ /* This doesn't actually powergate the UVD block. * That's done in the dpm code via the SMC. This * just re-inits the block as necessary. The actual * gating still happens in the dpm code. We should * revisit this when there is a cleaner line between * the smc and the hw blocks
*/ struct amdgpu_device *adev = ip_block->adev; int ret = 0;
¤ 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.0.18Bemerkung:
(vorverarbeitet am 2026-05-01)
¤
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.