On display version 20+ the extended fast wake time is programmed in ALPM_CTL and the IO buffer wake in PSR2_CTL. intel_alpm_compute_params() derives both from precharge + preamble + PHY wake + tFW exit latency (42 us here); the IO buffer wake only differs when io_buffer_wake_time is larger than the precharge, which is not the case on this panel. So both end up on the same number of scanlines, 11 on this mode.
On the Dell XPS 16 DA16260 (Panther Lake, LG panel with sink OUI 00:22:b9, 3200x2000@120, 4 x 2.7 Gbps, DSC) equal values make every ALPM link wake fail: 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. Changing the two fields at runtime shows that the relation between them matters, not the absolute time: fast wake 11 / IO wake 11 -> CRC error on every wake fast wake 12 / IO wake 11 -> clean fast wake 40 / IO wake 40 -> CRC error on every wake fast wake 40 / IO wake 20 -> clean fast wake 11 / IO wake 20 -> CRC error on every wake The fast wake sequence has to start strictly before the IO buffer wake. With the current values PSR2 on this panel only looked usable because the constant wake failures kept forcing the sink to resync. Make fast_wake_lines strictly greater than io_wake_lines on display 20+, or, when already at the maximum, put the IO buffer wake one line below it. Use the larger of the two in the vblank and guardband checks, which so far only looked at the IO buffer wake on display 20+. This is one of the two machines behind the eDP Panel Replay quirk in intel_quirks.c (Dell XPS 14 DA14260 / XPS 16 DA16260). The change does not make Panel Replay or PSR2 selective updates work there on its own: with the wake fixed the panel still never receives a selective update (sink 0x200a stays at PSR_STATE only) while the PSR2 engine sits in SU_STANDBY and consumes the TRANS_PUSH frame change events. That is reported separately on intel-gfx. Link: https://lore.kernel.org/intel-gfx/[email protected]/ Link: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/7521 Link: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7682 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..c244273 100644 --- a/drivers/gpu/drm/i915/display/intel_alpm.c +++ b/drivers/gpu/drm/i915/display/intel_alpm.c @@ -238,8 +238,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); + + /* + * On display 20+ the extended fast wake sequence programmed in + * ALPM_CTL has to start strictly before the IO buffer wake programmed + * in PSR2_CTL. Both are derived from the same precharge, preamble, + * PHY wake and tFW exit latency above, so they normally end up on the + * same number of lines. With equal values 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. One extra fast wake + * line is enough to fix it; when we are already at the maximum keep + * the IO buffer wake one line below the fast wake instead. + */ + if (DISPLAY_VER(display) >= 20 && 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;
