Re: [PATCH] drm/amd/display: Exit idle optimizations before programming
Mario Limonciello <[email protected]> Tue, 28 Jul 2026 13:39:55 -0500
| Newsgroups | org.freedesktop.lists.amd-gfx,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
On 7/28/26 12:02, [email protected] wrote: > From: Leo Li <[email protected]> > > [Why] > > We need to exit PSR/IPS before programming. Before calling DC for > programming in amdgpu_dm_commit_planes(), there's a > vblank_control_workqueue flush. This waits for IPS and PSR exit. (See > drm_vblank_on/off() > amdgpu_dm_crtc_set_vblank() --queue_work()-> > amdgpu_dm_crtc_vblank_control_worker()) > > Prior to the tagged "Fixes:" change, drm_vblank_get() was called before > the workqueue flush. This ordering ensures that PSR exit occurred before > programming. After the "Fixes:" change, drm_vblank_get() is called after > the workqueue flush, leading to programming while idle optimizations are > still active. This can lead to incorrect flip_pending detection used by > vblank event delivery. > > [How] > > Split the vblank_get() component of `dm_arm_vblank_event()` into > `dm_arm_vblank_event_pre_programming()`, which is called before > programming. Call it before the vblank_control_workqueue flush. > > Includes a drive-by cleanup of prepare_flip_isr(): the only caller is > dm_arm_vblank_event() and it's simple enough to roll-in. > > Signed-off-by: Leo Li <[email protected]> > Fixes: 48ab86360af1 ("drm/amd/display: check GRPH_FLIP status before sending event") > Cc: [email protected] > Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141#note_3583205 > Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/5527 > Assisted-by: Codex:gpt-5.6-sol > Assisted-by: Claude:opus-5 Reviewed-by: Mario Limonciello (AMD) <[email protected]> > --- > Rebased patch for applying on 7.2-rc5: > https://gitlab.freedesktop.org/leoli/linux-asdn/-/commit/f05db644126540f1d237f846cc0512088551eb73 > > Thanks, > Leo > > .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 79 +++++++++++-------- > 1 file changed, 47 insertions(+), 32 deletions(-) > > diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c > index df6b59fb8d0d4..784587697bac8 100644 > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c > @@ -3576,25 +3576,6 @@ static void remove_stream(struct amdgpu_device *adev, > acrtc->enabled = false; > } > > -static void prepare_flip_isr(struct amdgpu_crtc *acrtc) > -{ > - > - assert_spin_locked(&acrtc->base.dev->event_lock); > - WARN_ON(acrtc->event); > - > - acrtc->event = acrtc->base.state->event; > - > - /* Set the flip status */ > - acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED; > - > - /* Mark this event as consumed */ > - acrtc->base.state->event = NULL; > - > - drm_dbg_state(acrtc->base.dev, > - "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n", > - acrtc->crtc_id); > -} > - > static void amdgpu_dm_commit_cursors(struct drm_atomic_commit *state) > { > struct drm_plane *plane; > @@ -3740,17 +3721,48 @@ static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc, > return; > > if (pflip_update) { > - drm_crtc_vblank_get(&acrtc->base); > WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE); > - /* Arm flip completion handling and event delivery after programming. */ > - prepare_flip_isr(acrtc); > + WARN_ON(acrtc->event); > + > + acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED; > + acrtc->event = acrtc->base.state->event; > + acrtc->base.state->event = NULL; > + > + drm_dbg_state(acrtc->base.dev, > + "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n", > + acrtc->crtc_id); > } else if (cursor_update) { > - drm_crtc_vblank_get(&acrtc->base); > acrtc->event = acrtc->base.state->event; > acrtc->base.state->event = NULL; > } > } > > +/** > + * dm_arm_vblank_event_pre_programming - Prepare for programming > + * @acrtc: The amdgpu CRTC to prepare > + * @acrtc_state: The new CRTC state > + * @pflip_update: Whether a page flip is being programmed > + * @cursor_update: Whether a cursor update is being programmed > + * > + * Grab a reference on the vblank counter if a page flip or cursor update is to > + * be programmed. Do this before programming so the HW is not in any > + * idle-optimized state (such as PSR). > + */ > +static void dm_arm_vblank_event_pre_programming( > + struct amdgpu_crtc *acrtc, > + struct dm_crtc_state *acrtc_state, > + bool pflip_update, > + bool cursor_update) > +{ > + assert_spin_locked(&acrtc->base.dev->event_lock); > + > + if (!acrtc->base.state->event || acrtc_state->active_planes == 0) > + return; > + > + if (pflip_update || cursor_update) > + drm_crtc_vblank_get(&acrtc->base); > +} > + > static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, > struct drm_device *dev, > struct amdgpu_display_manager *dm, > @@ -4013,16 +4025,19 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, > } > } > > - /* > - * DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for > - * event delivery. Only GRPH_FLIP handler can send pflip events, and it > - * only fires if HW latched to the flip. Maintain legacy behavior by > - * arming event before programming. > - */ > - if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) { > - scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) { > + scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) { > + dm_arm_vblank_event_pre_programming(acrtc_attach, acrtc_state, > + pflip_present, > + cursor_update); > + /* > + * DCE depends on a combination of GRPH_FLIP, VLINE0, and > + * VUPDATE for event delivery. Only GRPH_FLIP handler can send > + * pflip events, and it only fires if HW latched to the flip. > + * Maintain legacy behavior by arming event before programming. > + */ > + if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) { > dm_arm_vblank_event(acrtc_attach, acrtc_state, > - pflip_present, cursor_update); > + pflip_present, cursor_update); > } > } >