That HW is ancient!

The question is really if that works reliable or if that could break anything?

I mean when UVD doesn't work after resume is ugly, but when resume doesn't work 
at all any more then that would be really bad.

Regards,
Christian.

On 10/21/26 15:29, Alex Deucher wrote:
> + 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