On Thu, 2026-09-03 at 20:43 -0400, Jake Steinman wrote:
> 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, PSR and LOBF, 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.

This patch doesn't help much ATM because we wont get rid of the quirk
disabling selective fetch anyways.

BR,
Jouni Högander

> 
> v3: also account for the fast wake in the LOBF guardband and vblank
>     checks (Sashiko review)
> 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..04f61fd 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;
>  }
> @@ -256,8 +278,9 @@ int intel_alpm_lobf_min_guardband(struct
> intel_crtc_state *crtc_state)
>        * is applicable. Currently this information is not readily
>        * available in crtc_state, so max will suffice for now.
>        */
> -     waketime_in_lines = max(crtc_state-
> >alpm_state.io_wake_lines,
> -                             crtc_state-
> >alpm_state.aux_less_wake_lines);
> +     waketime_in_lines = max3(crtc_state-
> >alpm_state.io_wake_lines,
> +                              crtc_state-
> >alpm_state.fast_wake_lines,
> +                              crtc_state-
> >alpm_state.aux_less_wake_lines);
>  
>       if (!crtc_state->has_lobf)
>               return 0;
> @@ -314,7 +337,8 @@ void intel_alpm_lobf_compute_config_late(struct
> intel_dp *intel_dp,
>        */
>       first_sdp_position = adjusted_mode->crtc_vtotal -
> adjusted_mode->crtc_vsync_start;
>       if (intel_alpm_aux_less_wake_supported(intel_dp))
> -             waketime_in_lines = crtc_state-
> >alpm_state.io_wake_lines;
> +             waketime_in_lines = max(crtc_state-
> >alpm_state.io_wake_lines,
> +                                     crtc_state-
> >alpm_state.fast_wake_lines);
>       else
>               waketime_in_lines = crtc_state-
> >alpm_state.aux_less_wake_lines;
>  
> 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