+ 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 {

Reply via email to