The LG panel (sink OUI 00:22:b9) in the Dell XPS 14 DA14260 and XPS 16
DA16260 (Panther Lake) fails every ALPM link wake when the extended fast
wake and the IO buffer wake are programmed to the same number of
scanlines, which the spec formulas produce for it (11/11 lines on
3200x2000@120, 4 x 2.7 Gbps, DSC). The sink sets the Link CRC error bit
in DP_PSR_ERROR_STATUS within ~25 ms of it being cleared, on every wake,
for as long as PSR2 is active.

Measured with DP_PSR_ERROR_STATUS polled every 200 ms and cleared after
each read, 15 s of PSR2 per configuration, number of polls that found
the bit set again:

  io/fast 11/11, guardband 60 (stock)          70 of 71
  io/fast 11/11, guardband 127 (full vblank)   70 of 70
  fast wake sync length 20 -> io/fast 12/12    68 of 69
  fast wake sync length 24 -> io/fast 13/13    69 of 70
  io/fast 11/12                                 0 of 71

A larger guardband or a longer fast wake sync pulse does not help, and
moving both wake times up together does not either. The fast wake has
to start strictly before the IO buffer wake on this panel.

Since the spec allows the IO buffer wake to be equal to or longer than
the fast wake, keep the formulas as they are and apply the relation as
a DPCD quirk for this panel on these two machines: one extra fast wake
line, or the IO buffer wake one line below the fast wake when already
at the maximum. Account for the larger of the two wake times in the
vblank and guardband checks, which only looked at the IO buffer wake on
display 20+.

This does not by itself make PSR2 selective updates or Panel Replay
usable on these machines; with the wake clean the panel still never
receives a selective update. That is tracked separately.

v2: quirk for the affected panel instead of a generic change (Jouni)

Link: 
https://lore.kernel.org/intel-gfx/[email protected]/
Link: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/7521
Cc: Jouni Högander <[email protected]>
Cc: Animesh Manna <[email protected]>
Cc: Ville Syrjälä <[email protected]>
Cc: Jani Nikula <[email protected]>
Cc: [email protected]
Cc: [email protected]
Signed-off-by: Jake Steinman <[email protected]>
---
diff --git a/drivers/gpu/drm/i915/display/intel_alpm.c 
b/drivers/gpu/drm/i915/display/intel_alpm.c
index f138376..79cce5b 100644
--- a/drivers/gpu/drm/i915/display/intel_alpm.c
+++ b/drivers/gpu/drm/i915/display/intel_alpm.c
@@ -15,6 +15,7 @@
 #include "intel_dp.h"
 #include "intel_dp_aux.h"
 #include "intel_psr.h"
+#include "intel_quirks.h"
 #include "intel_psr_regs.h"
 #include "intel_vrr.h"
 
@@ -238,8 +239,29 @@ bool intel_alpm_compute_params(struct intel_dp *intel_dp,
                io_wake_lines = fast_wake_lines = max_wake_lines;
 
        /* According to Bspec lower limit should be set as 7 lines. */
-       crtc_state->alpm_state.io_wake_lines = max(io_wake_lines, 7);
-       crtc_state->alpm_state.fast_wake_lines = max(fast_wake_lines, 7);
+       io_wake_lines = max(io_wake_lines, 7);
+       fast_wake_lines = max(fast_wake_lines, 7);
+
+       /*
+        * The LG panel (sink OUI 00:22:b9) in the Dell XPS 14/16
+        * DA14260/DA16260 reports a Link CRC error on every link wake when
+        * the extended fast wake and the IO buffer wake are programmed to
+        * the same number of lines, which the formulas above produce for it.
+        * Neither a larger guardband nor a longer fast wake sync pulse
+        * changes that; the fast wake has to start strictly before the IO
+        * buffer wake. Apply that as a quirk for this panel only.
+        */
+       if (DISPLAY_VER(display) >= 20 &&
+           intel_has_dpcd_quirk(intel_dp, QUIRK_ALPM_FAST_WAKE_AHEAD) &&
+           fast_wake_lines <= io_wake_lines) {
+               if (io_wake_lines < max_wake_lines)
+                       fast_wake_lines = io_wake_lines + 1;
+               else
+                       io_wake_lines = fast_wake_lines - 1;
+       }
+
+       crtc_state->alpm_state.io_wake_lines = io_wake_lines;
+       crtc_state->alpm_state.fast_wake_lines = fast_wake_lines;
 
        return true;
 }
diff --git a/drivers/gpu/drm/i915/display/intel_psr.c 
b/drivers/gpu/drm/i915/display/intel_psr.c
index c0933dd..ec80a2e 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.c
+++ b/drivers/gpu/drm/i915/display/intel_psr.c
@@ -1443,7 +1443,8 @@ static bool wake_lines_fit_into_vblank(struct intel_dp 
*intel_dp,
                wake_lines = DISPLAY_VER(display) < 20 ?
                        
psr2_block_count_lines(crtc_state->alpm_state.io_wake_lines,
                                               
crtc_state->alpm_state.fast_wake_lines) :
-                       crtc_state->alpm_state.io_wake_lines;
+                       max(crtc_state->alpm_state.io_wake_lines,
+                           crtc_state->alpm_state.fast_wake_lines);
 
        /*
         * Guardband has not been computed yet, so we conservatively check if 
the
@@ -4556,7 +4557,8 @@ void intel_psr_compute_config_late(struct intel_dp 
*intel_dp,
                wake_lines = DISPLAY_VER(display) < 20 ?
                             
psr2_block_count_lines(crtc_state->alpm_state.io_wake_lines,
                                                    
crtc_state->alpm_state.fast_wake_lines) :
-                            crtc_state->alpm_state.io_wake_lines;
+                            max(crtc_state->alpm_state.io_wake_lines,
+                                crtc_state->alpm_state.fast_wake_lines);
        else
                wake_lines = 0;
 
@@ -4612,7 +4614,8 @@ int intel_psr_min_guardband(struct intel_crtc_state 
*crtc_state)
                wake_lines = DISPLAY_VER(display) < 20 ?
                             
psr2_block_count_lines(crtc_state->alpm_state.io_wake_lines,
                                                    
crtc_state->alpm_state.fast_wake_lines) :
-                            crtc_state->alpm_state.io_wake_lines;
+                            max(crtc_state->alpm_state.io_wake_lines,
+                                crtc_state->alpm_state.fast_wake_lines);
        else
                return 0;
 
diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c 
b/drivers/gpu/drm/i915/display/intel_quirks.c
index 33245f4..a5d01ba 100644
--- a/drivers/gpu/drm/i915/display/intel_quirks.c
+++ b/drivers/gpu/drm/i915/display/intel_quirks.c
@@ -86,6 +86,14 @@ static void quirk_edp_limit_rate_hbr2(struct intel_display 
*display)
        drm_info(display->drm, "Applying eDP Limit rate to HBR2 quirk\n");
 }
 
+static void quirk_alpm_fast_wake_ahead(struct intel_dp *intel_dp)
+{
+       struct intel_display *display = to_intel_display(intel_dp);
+
+       intel_set_dpcd_quirk(intel_dp, QUIRK_ALPM_FAST_WAKE_AHEAD);
+       drm_info(display->drm, "Applying ALPM fast wake ahead of IO buffer wake 
quirk\n");
+}
+
 static void quirk_disable_edp_panel_replay(struct intel_dp *intel_dp)
 {
        struct intel_display *display = to_intel_display(intel_dp);
@@ -286,6 +294,21 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = 
{
                .sink_oui = SINK_OUI(0x00, 0x22, 0xb9),
                .hook = quirk_disable_edp_panel_replay,
        },
+       /* Dell XPS 14 DA14260 and XPS 16 DA16260, LG panel: ALPM link wake */
+       {
+               .device = DEVICE_ID_ANY,
+               .subsystem_vendor = 0x1028,
+               .subsystem_device = 0x0db9,
+               .sink_oui = SINK_OUI(0x00, 0x22, 0xb9),
+               .hook = quirk_alpm_fast_wake_ahead,
+       },
+       {
+               .device = DEVICE_ID_ANY,
+               .subsystem_vendor = 0x1028,
+               .subsystem_device = 0x0dba,
+               .sink_oui = SINK_OUI(0x00, 0x22, 0xb9),
+               .hook = quirk_alpm_fast_wake_ahead,
+       },
 };
 
 void intel_init_quirks(struct intel_display *display)
diff --git a/drivers/gpu/drm/i915/display/intel_quirks.h 
b/drivers/gpu/drm/i915/display/intel_quirks.h
index 970a4fe..8bbc2eb 100644
--- a/drivers/gpu/drm/i915/display/intel_quirks.h
+++ b/drivers/gpu/drm/i915/display/intel_quirks.h
@@ -23,6 +23,7 @@ enum intel_quirk_id {
        QUIRK_EDP_LIMIT_RATE_HBR2,
        QUIRK_DISABLE_EDP_PANEL_REPLAY,
        QUIRK_DISABLE_PSR2,
+       QUIRK_ALPM_FAST_WAKE_AHEAD,
 };
 
 void intel_init_quirks(struct intel_display *display);

Reply via email to