]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
drm/amd/display: Exit idle optimizations before programming
authorLeo Li <sunpeng.li@amd.com>
Tue, 28 Jul 2026 17:02:47 +0000 (13:02 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 29 Jul 2026 22:21:24 +0000 (18:21 -0400)
[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.

v2: Fix checkpatch formatting warning on
    drm_arm_vblank_event_pre_programming() arg alignment.

Fixes: 48ab86360af1 ("drm/amd/display: check GRPH_FLIP status before sending event")
Cc: stable@vger.kernel.org
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
Suggested-by: David Weber <weber.aulendorf@gmail.com>
Signed-off-by: Leo Li <sunpeng.li@amd.com>
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
(cherry picked from commit 05984e29520a28c27f5a2388742c957a6a87ee7a)

drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c

index eaf19ec843e8729df0f940315ab7d087e79d5d36..1820547b1dde8094bfe97b5e77af57348041f8e8 100644 (file)
@@ -9903,25 +9903,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 update_freesync_state_on_stream(
        struct amdgpu_display_manager *dm,
        struct dm_crtc_state *new_crtc_state,
@@ -10274,17 +10255,47 @@ 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,
@@ -10550,16 +10561,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);
                }
        }