From: Leo Li <[email protected]>

[Why]

HUBP fires the pflip interrupt when it consumes the last programmed flip
address. However, when it is in dynamic power gating (DPG), the pflip
interrupt can be masked. If the flip addr is programmed prior to HUBP
fully exiting DPG, the pflip interrupt may never fire.

This was observed on some PSR systems, where PSR exit in FW did not
fully complete before programming the flip address.

[How]

DC has a function for checking the flip status of a plane. DM can
consult it in one of the OTG interrupts -- which will fire regardless of
HUBP status -- as a backup for delivering events.

First, DM needs to track the dc_plane on which the flip was programmed.
Introduce a dc_plane_state flip_target on the acrtc to do so, following
the existing acrtc->pflip_status lifecycle.

Then, in the OTG interrupt handlers, check if the flip_target plane has
any pending flips. If a flip was armed, and there aren't any pending
flips (indicating that HUBP consumed the new flip addr but did not
deliver a pflip interrupt), then deliver and clear the armed flip.

Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141
Fixes: 58a261bfc967 ("drm/amd/display: use a more lax vblank enable policy for 
older ASICs")
Fixes: e45b6716de4b ("drm/amd/display: use a more lax vblank enable policy for 
DCN35+")
Signed-off-by: Leo Li <[email protected]>
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 63 +++++++++++++++++--
 .../display/amdgpu_dm/amdgpu_dm_irq_params.h  |  8 +++
 2 files changed, 67 insertions(+), 4 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 1daea5644d535..9df48ed69acb9 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -427,6 +427,23 @@ static inline bool update_planes_and_stream_adapter(struct 
dc *dc,
                                           stream_update);
 }
 
+/**
+ * clear_flip_isr - clear armed flip after it's been handled
+ *
+ * See also prepare_flip_isr.
+ */
+static void clear_flip_isr(struct amdgpu_crtc *acrtc)
+{
+       assert_spin_locked(&acrtc->base.dev->event_lock);
+
+       WARN_ON(acrtc->pflip_status == AMDGPU_FLIP_NONE);
+       WARN_ON(acrtc->dm_irq_params.flip_target == NULL);
+
+       acrtc->pflip_status = AMDGPU_FLIP_NONE;
+       dc_plane_state_release(acrtc->dm_irq_params.flip_target);
+       acrtc->dm_irq_params.flip_target = NULL;
+}
+
 /**
  * dm_pflip_high_irq() - Handle pageflip interrupt
  * @interrupt_params: ignored
@@ -523,7 +540,8 @@ static void dm_pflip_high_irq(void *interrupt_params)
        amdgpu_crtc->dm_irq_params.last_flip_vblank =
                amdgpu_get_vblank_counter_kms(&amdgpu_crtc->base);
 
-       amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE;
+       clear_flip_isr(amdgpu_crtc);
+
        spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
 
        drm_dbg_state(dev,
@@ -750,7 +768,37 @@ static void __dm_crtc_high_irq(void *interrupt_params,
                        acrtc->event = NULL;
                        drm_crtc_vblank_put(&acrtc->base);
                }
-               acrtc->pflip_status = AMDGPU_FLIP_NONE;
+               clear_flip_isr(acrtc);
+       }
+
+       /*
+        * If pflip irq failed to fire (possible if HW master update lock is
+        * held, or HUBP was in DPG when flip was programmed), check if HUBP
+        * consumed the fb addr. If so, send the event here.
+        *
+        * Not applicable to DCE HW with no pflip interrupts.
+        */
+       if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0 &&
+           acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED) {
+               const struct dc_plane_status *status;
+               union dc_plane_status_update_flags flags = {0};
+
+               flags.bits.address = 1;
+               /*
+                * Note that dc_plane_get_status() has a built-in IPS exit from
+                * non-sleep context. That's ok though, since if interrupts are
+                * active, then HW is not in IPS. Otherwise, this would have
+                * been illegal.
+                */
+               status = dc_plane_get_status(acrtc->dm_irq_params.flip_target,
+                                            flags);
+
+               if (!status->is_flip_pending && acrtc->event) {
+                       drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
+                       acrtc->event = NULL;
+                       drm_crtc_vblank_put(&acrtc->base);
+               }
+               clear_flip_isr(acrtc);
        }
 
        spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
@@ -9792,7 +9840,8 @@ static void remove_stream(struct amdgpu_device *adev,
        acrtc->enabled = false;
 }
 
-static void prepare_flip_isr(struct amdgpu_crtc *acrtc)
+static void prepare_flip_isr(struct amdgpu_crtc *acrtc,
+                            struct dc_plane_state *flip_target)
 {
 
        assert_spin_locked(&acrtc->base.dev->event_lock);
@@ -9806,6 +9855,9 @@ static void prepare_flip_isr(struct amdgpu_crtc *acrtc)
        /* Mark this event as consumed */
        acrtc->base.state->event = NULL;
 
+       dc_plane_state_retain(flip_target);
+       acrtc->dm_irq_params.flip_target = flip_target;
+
        drm_dbg_state(acrtc->base.dev,
                      "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n",
                      acrtc->crtc_id);
@@ -10162,6 +10214,7 @@ static void amdgpu_dm_commit_planes(struct 
drm_atomic_state *state,
        struct dm_crtc_state *acrtc_state = to_dm_crtc_state(new_pcrtc_state);
        struct dm_crtc_state *dm_old_crtc_state =
                        to_dm_crtc_state(drm_atomic_get_old_crtc_state(state, 
pcrtc));
+       struct dc_plane_state *flip_target = NULL;
        int planes_count = 0, vpos, hpos;
        unsigned long flags;
        u32 target_vblank, last_flip_vblank;
@@ -10269,6 +10322,8 @@ static void amdgpu_dm_commit_planes(struct 
drm_atomic_state *state,
                        continue;
                }
 
+               flip_target = dc_plane;
+
                fill_dc_plane_info_and_addr(
                        dm->adev, new_plane_state,
                        afb->tiling_flags,
@@ -10414,7 +10469,7 @@ static void amdgpu_dm_commit_planes(struct 
drm_atomic_state *state,
                        spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
 
                        WARN_ON(acrtc_attach->pflip_status != AMDGPU_FLIP_NONE);
-                       prepare_flip_isr(acrtc_attach);
+                       prepare_flip_isr(acrtc_attach, flip_target);
 
                        spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
                }
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq_params.h 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq_params.h
index f0c1b0c1faa9f..233a6462f0826 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq_params.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq_params.h
@@ -33,6 +33,14 @@ struct dm_irq_params {
        u32 last_flip_vblank;
        struct mod_vrr_params vrr_params;
        struct dc_stream_state *stream;
+
+       /*
+        * If not NULL, the plane for which a flip interrupt is expected. For
+        * multi-plane configurations, this should be a plane that had it's fb
+        * address updated. See also &prepare_flip_isr() and &clear_flip_isr()
+        */
+       struct dc_plane_state *flip_target;
+
        int active_planes;
        bool allow_sr_entry;
        struct mod_freesync_config freesync_config;
-- 
2.53.0

Reply via email to