]> 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)
committerJouni Högander <jouni.hogander@intel.com>
Mon, 25 May 2026 10:02:58 +0000 (13:02 +0300)
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
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 3dc5ac75a98bef23a9a3d461490174d9ea397cf7..09ce25a6d4b11bfdb61a5f8c892f0c2028c98ad0 100644 (file)
@@ -494,6 +494,7 @@ struct intel_display {
                u8 vblank_enabled;
 
                int vblank_enable_count;
+               bool vblank_status_last_notified;
 
                struct work_struct vblank_notify_work;
 
index 899a38c0a7b750876a0308449238c5d8192eddca..4a821b0674fdaf1c648eaff0743f91c8054b81c8 100644 (file)
@@ -1786,8 +1786,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)
@@ -1800,10 +1804,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 1c0c32c4e43a50a509a1ea0e0e5f5d2ee43c10be..ac865b6557b65df651f01de600c835db1e00f182 100644 (file)
@@ -1791,6 +1791,8 @@ struct intel_psr {
        u8 active_non_psr_pipes;
 
        const char *no_psr_reason;
+
+       struct ref_tracker *vblank_wakeref;
 };
 
 struct intel_dp {
index 70108e0a4c0c050d2eea72e4e91210311fac99f3..19cfb23fe9f817f444b70243671a80a6e3dbce64 100644 (file)
@@ -4180,14 +4180,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)
@@ -4195,18 +4201,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