]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/i915/psr: Use DC_OFF wake reference to block DC6 on vblank enable
authorJouni Högander <jouni.hogander@intel.com>
Wed, 20 May 2026 10:49:44 +0000 (13:49 +0300)
committerTvrtko Ursulin <tursulin@ursulin.net>
Tue, 26 May 2026 08:31:48 +0000 (09:31 +0100)
We are observing following warnings:

*ERROR* power well DC_off state mismatch (refcount 0/enabled 1)

gen9_dc_off_power_well_enabled is considering target state DC_STATE_DISABLE
as DC_OFF power well being enabled. Fix this by using wakeref for the
purpose.

To achieve this we need to modify notification code as well. Currently it
is possible that PSR gets notified vblank enable/disable twice on same
status. This is currently not a problem as it is just triggering call to
intel_display_power_set_target_dc_state with same target state as a
parameter. When using wakeref this becomes a problem due to reference
counting. Fix this storing vbank status on last notification and use that
to ensure there are no more than one notification with same vblank status.

v2: ensure there is no subsequent notifications with same status

Fixes: aa451abcffb5 ("drm/i915/display: Prevent DC6 while vblank is enabled for Panel Replay")
Cc: <stable@vger.kernel.org> # v6.13+
Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
Reviewed-by: Michał Grzelak <michal.grzelak@intel.com>
Link: https://patch.msgid.link/20260520104944.239797-2-jouni.hogander@intel.com
(cherry picked from commit 35485ac56d878192a3829a58cb26503125ec7104)
Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
drivers/gpu/drm/i915/display/intel_display_core.h
drivers/gpu/drm/i915/display/intel_display_irq.c
drivers/gpu/drm/i915/display/intel_display_types.h
drivers/gpu/drm/i915/display/intel_psr.c

index d9baca2d5aaf893a8d6f430baa19aed2537bbaba..78afcd42f44c08ceffa48ec4e8a411c0a8d51cc2 100644 (file)
@@ -497,6 +497,7 @@ struct intel_display {
                u8 vblank_enabled;
 
                int vblank_enable_count;
+               bool vblank_status_last_notified;
 
                struct work_struct vblank_notify_work;
 
index 70c1bba7c0a8f0307d9226d42c97a6cc501e5162..aedf3928a08935a7191fc1d135dc23e47967bdf3 100644 (file)
@@ -1773,8 +1773,12 @@ static void intel_display_vblank_notify_work(struct work_struct *work)
        struct intel_display *display =
                container_of(work, typeof(*display), irq.vblank_notify_work);
        int vblank_enable_count = READ_ONCE(display->irq.vblank_enable_count);
+       bool vblank_status = !!vblank_enable_count;
 
-       intel_psr_notify_vblank_enable_disable(display, vblank_enable_count);
+       if (display->irq.vblank_status_last_notified != vblank_status) {
+               intel_psr_notify_vblank_enable_disable(display, vblank_status);
+               display->irq.vblank_status_last_notified = vblank_status;
+       }
 }
 
 int bdw_enable_vblank(struct drm_crtc *_crtc)
@@ -1787,10 +1791,10 @@ int bdw_enable_vblank(struct drm_crtc *_crtc)
        if (gen11_dsi_configure_te(crtc, true))
                return 0;
 
+       spin_lock_irqsave(&display->irq.lock, irqflags);
        if (crtc->vblank_psr_notify && display->irq.vblank_enable_count++ == 0)
                schedule_work(&display->irq.vblank_notify_work);
 
-       spin_lock_irqsave(&display->irq.lock, irqflags);
        bdw_enable_pipe_irq(display, pipe, GEN8_PIPE_VBLANK);
        spin_unlock_irqrestore(&display->irq.lock, irqflags);
 
index 9c7c357afb09e119f15b57819b5d87079e47bf70..2e6a857085558ff7cb46e2e1abe219003c5c3daa 100644 (file)
@@ -1790,6 +1790,8 @@ struct intel_psr {
        u8 active_non_psr_pipes;
 
        const char *no_psr_reason;
+
+       struct ref_tracker *vblank_wakeref;
 };
 
 struct intel_dp {
index bd5a8c6ac6ef17586176dd3aa8799bb2d3dee0ca..598fe769a4021784217cff3b548102a20984502f 100644 (file)
@@ -4151,14 +4151,20 @@ void intel_psr_notify_vblank_enable_disable(struct intel_display *display,
                                            bool enable)
 {
        struct intel_encoder *encoder;
-       bool block_dc_states = false;
 
        for_each_intel_encoder_with_psr(display->drm, encoder) {
                struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
 
                mutex_lock(&intel_dp->psr.lock);
-               if (CAN_PANEL_REPLAY(intel_dp))
-                       block_dc_states = true;
+               if (CAN_PANEL_REPLAY(intel_dp)) {
+                       if (enable)
+                               intel_dp->psr.vblank_wakeref =
+                                       intel_display_power_get(display,
+                                                               POWER_DOMAIN_DC_OFF);
+                       else
+                               intel_display_power_put(display, POWER_DOMAIN_DC_OFF,
+                                                       intel_dp->psr.vblank_wakeref);
+               }
 
                if (intel_dp->psr.enabled && !intel_dp->psr.panel_replay_enabled &&
                    intel_dp->psr.pkg_c_latency_used)
@@ -4166,18 +4172,6 @@ void intel_psr_notify_vblank_enable_disable(struct intel_display *display,
 
                mutex_unlock(&intel_dp->psr.lock);
        }
-
-       /*
-        * NOTE: intel_display_power_set_target_dc_state is used
-        * only by PSR code for DC3CO handling. DC3CO target
-        * state is currently disabled in * PSR code. If DC3CO
-        * is taken into use we need take that into account here
-        * as well.
-        */
-       if (block_dc_states)
-               intel_display_power_set_target_dc_state(display, enable ?
-                                                       DC_STATE_DISABLE :
-                                                       DC_STATE_EN_UPTO_DC6);
 }
 
 static void