+ Leo, Christian
On Tue, Oct 20, 2026 at 3:46 AM ukrainer8 <[email protected]> wrote: > > On Evergreen/Redwood (and likely other UVD v1/v2 hardware), resume from > suspend intermittently fails the UVD ring test: > > radeon 0000:01:00.0: [drm] *ERROR* radeon: ring 5 test failed (0xCAFEDEAD) > radeon 0000:01:00.0: failed initializing UVD (-22). > > Cold boot always succeeds; resume succeeds only in about half of the cycles > (42 measured suspend/resume cycles on a Radeon HD 5650M: OK counts per > 6-cycle run were 2, 3, 5, 6, 1, 5, 3). Once the ring test fails, UVD stays > dead for the rest of the session and hardware video decode silently falls > back to CPU. > > The resume path runs the same full asic startup as cold boot > (evergreen_resume() -> evergreen_startup()), so the difference is the > hardware state at entry, not a skipped call. Field data shows the first > ring instance after resume is dead functionally, not by timeout: the ring > test fails fast and deterministically, and only a full stop+init cycle > revives the engine. > > Fix this the way the Windows driver effectively does it: silently cycle > the engine once (clocks up, start, stop, 20 ms settle) before testing, > then run the init sequence in a retry loop - on failure of clock setup, > VCPU start or the ring test, fully stop UVD, let it settle for 20 ms and > try again (up to 3 attempts). Each retry logs the failing step and the > attempt number, which also makes future reports of this failure mode > self-diagnosing. The ring-test polling window is widened from 100 ms to > 2000 ms as spare capacity; the early-exit poll keeps healthy hardware on > the fast path. > > On the same machine the patched driver went 6/6 OK across repeated test > runs, with the warm-up absorbing the dead first instance and zero error > lines in dmesg. > > v2: guard uvd_v1_0_stop() behind successful clock setup, per Sashiko AI > review - MMIO writes to an unclocked UVD block can cause a PCI bus master > abort and hard-hang the system. > > Link: https://gitlab.freedesktop.org/drm/amd/-/issues/1867 > Link: https://bugs.launchpad.net/bugs/1865130 > Tested-by: ukrainer8 <[email protected]> # Radeon HD 5650M, kernel 7.0.0 > Signed-off-by: ukrainer8 <[email protected]> > --- > v1 -> v2: keep the clock-setup result in a separate variable and only > call uvd_v1_0_stop() when clocks were actually raised (Sashiko AI review). > Also folded in the silent warm-up cycle and the wider ring-test window > validated in field testing since v1. > drivers/gpu/drm/radeon/uvd_v1_0.c | 57 > ++++++++++++++++++++++++++------------- > --- a/drivers/gpu/drm/radeon/uvd_v1_0.c > +++ b/drivers/gpu/drm/radeon/uvd_v1_0.c > @@ -158,24 +158,58 @@ > { > struct radeon_ring *ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX]; > uint32_t tmp; > - int r; > + int r, retry, clk_err; > > - /* raise clocks while booting up the VCPU */ > + /* warm-up: silently cycle the engine once (like the Windows driver), > + * the first ring instance after resume is dead functionally */ > if (rdev->family < CHIP_RV740) > radeon_set_uvd_clocks(rdev, 10000, 10000); > else > radeon_set_uvd_clocks(rdev, 53300, 40000); > + uvd_v1_0_start(rdev); > + uvd_v1_0_stop(rdev); > + mdelay(20); > > - r = uvd_v1_0_start(rdev); > - if (r) > - goto done; > + /* after resume the first ring test can race with the hardware; retry > */ > + for (retry = 0; retry < 3; retry++) { > + /* raise clocks while booting up the VCPU */ > + if (rdev->family < CHIP_RV740) > + clk_err = radeon_set_uvd_clocks(rdev, 10000, 10000); > + else > + clk_err = radeon_set_uvd_clocks(rdev, 53300, 40000); > + r = clk_err; > + if (r) > + drm_warn(&rdev->ddev, "radeon: raising UVD clocks > failed (%d), attempt %d\n", > + r, retry + 1); > + > + if (!r) { > + r = uvd_v1_0_start(rdev); > + if (r) > + drm_warn(&rdev->ddev, "radeon: starting UVD > failed (%d), attempt %d\n", > + r, retry + 1); > + } > > - ring->ready = true; > - r = radeon_ring_test(rdev, R600_RING_TYPE_UVD_INDEX, ring); > - if (r) { > - ring->ready = false; > - goto done; > + if (!r) { > + ring->ready = true; > + r = radeon_ring_test(rdev, R600_RING_TYPE_UVD_INDEX, > ring); > + if (r) { > + ring->ready = false; > + drm_warn(&rdev->ddev, "radeon: UVD ring test > failed (%d), attempt %d\n", > + r, retry + 1); > + } > + } > + > + if (!r) > + break; > + > + /* MMIO is only safe when the block is actually clocked */ > + if (!clk_err) > + uvd_v1_0_stop(rdev); > + if (retry < 2) > + mdelay(20); > } > + if (r) > + goto done; > > r = radeon_ring_lock(rdev, ring, 10); > if (r) { > @@ -421,7 +455,8 @@ > int uvd_v1_0_ring_test(struct radeon_device *rdev, struct radeon_ring *ring) > { > uint32_t tmp = 0; > - unsigned i; > + /* after resume the ring buffer controller needs ~1.5-2s to settle */ > + unsigned i, timeout = 2000000; > int r; > > WREG32(UVD_CONTEXT_ID, 0xCAFEDEAD); > @@ -434,14 +469,14 @@ > radeon_ring_write(ring, PACKET0(UVD_CONTEXT_ID, 0)); > radeon_ring_write(ring, 0xDEADBEEF); > radeon_ring_unlock_commit(rdev, ring, false); > - for (i = 0; i < rdev->usec_timeout; i++) { > + for (i = 0; i < timeout; i++) { > tmp = RREG32(UVD_CONTEXT_ID); > if (tmp == 0xDEADBEEF) > break; > udelay(1); > } > > - if (i < rdev->usec_timeout) { > + if (i < timeout) { > drm_info(&rdev->ddev, "ring test on %d succeeded in %d > usecs\n", > ring->idx, i); > } else {
