diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_psr.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/intel_psr.c | 1771 |
1 files changed, 1226 insertions, 545 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 0b021acb330f..29904a037575 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -26,9 +26,9 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_damage_helper.h> #include <drm/drm_debugfs.h> +#include <drm/drm_print.h> +#include <drm/drm_vblank.h> -#include "i915_drv.h" -#include "i915_reg.h" #include "intel_alpm.h" #include "intel_atomic.h" #include "intel_crtc.h" @@ -36,14 +36,26 @@ #include "intel_ddi.h" #include "intel_de.h" #include "intel_display_irq.h" +#include "intel_display_regs.h" +#include "intel_display_rpm.h" #include "intel_display_types.h" +#include "intel_display_utils.h" +#include "intel_display_wa.h" +#include "intel_dmc.h" #include "intel_dp.h" +#include "intel_dpcd.h" #include "intel_dp_aux.h" +#include "intel_dsb.h" #include "intel_frontbuffer.h" #include "intel_hdmi.h" #include "intel_psr.h" #include "intel_psr_regs.h" +#include "intel_quirks.h" #include "intel_snps_phy.h" +#include "intel_step.h" +#include "intel_vblank.h" +#include "intel_vdsc.h" +#include "intel_vrr.h" #include "skl_universal_plane.h" /** @@ -154,7 +166,7 @@ * * Unfortunately CHICKEN_TRANS itself seems to be double buffered * and thus won't latch until the first vblank. So with DC states - * enabled the register effctively uses the reset value during DC5 + * enabled the register effectively uses the reset value during DC5 * exit+PSR exit sequence, and thus the bit does nothing until * latched by the vblank that it was trying to prevent from being * generated in the first place. So we should probably call this @@ -171,7 +183,7 @@ * CHICKEN_PIPESL_1[15]/HSW_UNMASK_VBL_TO_REGS_IN_SRD (hsw): * * On BDW without this bit is no vblanks whatsoever are - * generated after PSR exit. On HSW this has no apparant effect. + * generated after PSR exit. On HSW this has no apparent effect. * WaPsrDPRSUnmaskVBlankInSRD says to set this. * * The rest of the bits are more self-explanatory and/or @@ -185,7 +197,7 @@ * has_psr + has_panel_replay: Panel Replay * has_psr + has_panel_replay + has_sel_update: Panel Replay Selective Update * - * Description of some intel_psr varibles. enabled, panel_replay_enabled, + * Description of some intel_psr variables. enabled, panel_replay_enabled, * sel_update_enabled * * enabled (alone): PSR1 @@ -227,16 +239,12 @@ bool intel_psr_needs_aux_io_power(struct intel_encoder *encoder, static bool psr_global_enabled(struct intel_dp *intel_dp) { - struct intel_display *display = to_intel_display(intel_dp); struct intel_connector *connector = intel_dp->attached_connector; switch (intel_dp->psr.debug & I915_PSR_DEBUG_MODE_MASK) { case I915_PSR_DEBUG_DEFAULT: - if (display->params.enable_psr == -1) - return intel_dp_is_edp(intel_dp) ? - connector->panel.vbt.psr.enable : - true; - return display->params.enable_psr; + return intel_dp_is_edp(intel_dp) ? + connector->panel.vbt.psr.enable : true; case I915_PSR_DEBUG_DISABLE: return false; default: @@ -244,39 +252,23 @@ static bool psr_global_enabled(struct intel_dp *intel_dp) } } -static bool psr2_global_enabled(struct intel_dp *intel_dp) +static bool sel_update_global_enabled(struct intel_dp *intel_dp) { - struct intel_display *display = to_intel_display(intel_dp); - switch (intel_dp->psr.debug & I915_PSR_DEBUG_MODE_MASK) { case I915_PSR_DEBUG_DISABLE: case I915_PSR_DEBUG_FORCE_PSR1: return false; default: - if (display->params.enable_psr == 1) - return false; return true; } } -static bool psr2_su_region_et_global_enabled(struct intel_dp *intel_dp) -{ - struct intel_display *display = to_intel_display(intel_dp); - - if (display->params.enable_psr != -1) - return false; - - return true; -} - static bool panel_replay_global_enabled(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); - if ((display->params.enable_psr != -1) || - (intel_dp->psr.debug & I915_PSR_DEBUG_PANEL_REPLAY_DISABLE)) - return false; - return true; + return !(intel_dp->psr.debug & I915_PSR_DEBUG_PANEL_REPLAY_DISABLE) && + display->params.enable_panel_replay; } static u32 psr_irq_psr_error_bit_get(struct intel_dp *intel_dp) @@ -442,7 +434,6 @@ static void psr_event_print(struct intel_display *display, void intel_psr_irq_handler(struct intel_dp *intel_dp, u32 psr_iir) { struct intel_display *display = to_intel_display(intel_dp); - struct drm_i915_private *dev_priv = to_i915(display->drm); enum transcoder cpu_transcoder = intel_dp->psr.transcoder; ktime_t time_ns = ktime_get(); @@ -462,8 +453,8 @@ void intel_psr_irq_handler(struct intel_dp *intel_dp, u32 psr_iir) if (DISPLAY_VER(display) >= 9) { u32 val; - val = intel_de_rmw(dev_priv, - PSR_EVENT(dev_priv, cpu_transcoder), + val = intel_de_rmw(display, + PSR_EVENT(display, cpu_transcoder), 0, 0); psr_event_print(display, val, intel_dp->psr.sel_update_enabled); @@ -487,7 +478,7 @@ void intel_psr_irq_handler(struct intel_dp *intel_dp, u32 psr_iir) intel_de_rmw(display, psr_imr_reg(display, cpu_transcoder), 0, psr_irq_psr_error_bit_get(intel_dp)); - queue_work(dev_priv->unordered_wq, &intel_dp->psr.work); + queue_work(display->wq.unordered, &intel_dp->psr.work); } } @@ -505,80 +496,37 @@ static u8 intel_dp_get_sink_sync_latency(struct intel_dp *intel_dp) return val; } -static u8 intel_dp_get_su_capability(struct intel_dp *intel_dp) -{ - u8 su_capability = 0; - - if (intel_dp->psr.sink_panel_replay_su_support) - drm_dp_dpcd_readb(&intel_dp->aux, - DP_PANEL_PANEL_REPLAY_CAPABILITY, - &su_capability); - else - su_capability = intel_dp->psr_dpcd[1]; - - return su_capability; -} - -static unsigned int -intel_dp_get_su_x_granularity_offset(struct intel_dp *intel_dp) -{ - return intel_dp->psr.sink_panel_replay_su_support ? - DP_PANEL_PANEL_REPLAY_X_GRANULARITY : - DP_PSR2_SU_X_GRANULARITY; -} - -static unsigned int -intel_dp_get_su_y_granularity_offset(struct intel_dp *intel_dp) -{ - return intel_dp->psr.sink_panel_replay_su_support ? - DP_PANEL_PANEL_REPLAY_Y_GRANULARITY : - DP_PSR2_SU_Y_GRANULARITY; -} - -/* - * Note: Bits related to granularity are same in panel replay and psr - * registers. Rely on PSR definitions on these "common" bits. - */ -static void intel_dp_get_su_granularity(struct intel_dp *intel_dp) +static void _psr_compute_su_granularity(struct intel_dp *intel_dp, + struct intel_connector *connector) { struct intel_display *display = to_intel_display(intel_dp); ssize_t r; - u16 w; + __le16 w; u8 y; /* - * TODO: Do we need to take into account panel supporting both PSR and - * Panel replay? - */ - - /* * If sink don't have specific granularity requirements set legacy * ones. */ - if (!(intel_dp_get_su_capability(intel_dp) & - DP_PSR2_SU_GRANULARITY_REQUIRED)) { + if (!(connector->dp.psr_caps.dpcd[1] & DP_PSR2_SU_GRANULARITY_REQUIRED)) { /* As PSR2 HW sends full lines, we do not care about x granularity */ - w = 4; + w = cpu_to_le16(4); y = 4; goto exit; } - r = drm_dp_dpcd_read(&intel_dp->aux, - intel_dp_get_su_x_granularity_offset(intel_dp), - &w, 2); - if (r != 2) + r = drm_dp_dpcd_read(&intel_dp->aux, DP_PSR2_SU_X_GRANULARITY, &w, sizeof(w)); + if (r != sizeof(w)) drm_dbg_kms(display->drm, "Unable to read selective update x granularity\n"); /* * Spec says that if the value read is 0 the default granularity should * be used instead. */ - if (r != 2 || w == 0) - w = 4; + if (r != sizeof(w) || w == 0) + w = cpu_to_le16(4); - r = drm_dp_dpcd_read(&intel_dp->aux, - intel_dp_get_su_y_granularity_offset(intel_dp), - &y, 1); + r = drm_dp_dpcd_read(&intel_dp->aux, DP_PSR2_SU_Y_GRANULARITY, &y, 1); if (r != 1) { drm_dbg_kms(display->drm, "Unable to read selective update y granularity\n"); @@ -588,13 +536,97 @@ static void intel_dp_get_su_granularity(struct intel_dp *intel_dp) y = 1; exit: - intel_dp->psr.su_w_granularity = w; - intel_dp->psr.su_y_granularity = y; + connector->dp.psr_caps.su_w_granularity = le16_to_cpu(w); + connector->dp.psr_caps.su_y_granularity = y; +} + +static enum intel_panel_replay_dsc_support +compute_pr_dsc_support(struct intel_connector *connector) +{ + u8 pr_dsc_mode; + u8 val; + + val = connector->dp.panel_replay_caps.dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_CAPABILITY)]; + pr_dsc_mode = REG_FIELD_GET8(DP_PANEL_REPLAY_DSC_DECODE_CAPABILITY_IN_PR_MASK, val); + + switch (pr_dsc_mode) { + case DP_DSC_DECODE_CAPABILITY_IN_PR_FULL_FRAME_ONLY: + return INTEL_DP_PANEL_REPLAY_DSC_FULL_FRAME_ONLY; + case DP_DSC_DECODE_CAPABILITY_IN_PR_SUPPORTED: + return INTEL_DP_PANEL_REPLAY_DSC_SELECTIVE_UPDATE; + default: + MISSING_CASE(pr_dsc_mode); + fallthrough; + case DP_DSC_DECODE_CAPABILITY_IN_PR_NOT_SUPPORTED: + case DP_DSC_DECODE_CAPABILITY_IN_PR_RESERVED: + return INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED; + } +} + +static const char *panel_replay_dsc_support_str(enum intel_panel_replay_dsc_support dsc_support) +{ + switch (dsc_support) { + case INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED: + return "not supported"; + case INTEL_DP_PANEL_REPLAY_DSC_FULL_FRAME_ONLY: + return "full frame only"; + case INTEL_DP_PANEL_REPLAY_DSC_SELECTIVE_UPDATE: + return "selective update"; + default: + MISSING_CASE(dsc_support); + return "n/a"; + }; +} + +static void _panel_replay_compute_su_granularity(struct intel_connector *connector) +{ + u16 w; + u8 y; + + if (!(connector->dp.panel_replay_caps.dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_CAPABILITY)] & + DP_PANEL_REPLAY_SU_GRANULARITY_REQUIRED)) { + w = 4; + y = 4; + goto exit; + } + + /* + * Spec says that if the value read is 0 the default granularity should + * be used instead. + */ + w = le16_to_cpu(*(__le16 *)&connector->dp.panel_replay_caps.dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_X_GRANULARITY)]) ? : 4; + y = connector->dp.panel_replay_caps.dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_Y_GRANULARITY)] ? : 1; + +exit: + connector->dp.panel_replay_caps.su_w_granularity = w; + connector->dp.panel_replay_caps.su_y_granularity = y; } -static void _panel_replay_init_dpcd(struct intel_dp *intel_dp) +static void _panel_replay_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector) { struct intel_display *display = to_intel_display(intel_dp); + int ret; + + /* TODO: Enable Panel Replay on MST once it's properly implemented. */ + if (intel_dp->mst_detect == DRM_DP_MST) + return; + + if (intel_dp_is_edp(intel_dp) && + intel_has_dpcd_quirk(intel_dp, QUIRK_DISABLE_EDP_PANEL_REPLAY)) { + drm_dbg_kms(display->drm, + "Panel Replay support not currently available for this setup\n"); + return; + } + + ret = drm_dp_dpcd_read_data(&intel_dp->aux, DP_PANEL_REPLAY_CAP_SUPPORT, + &connector->dp.panel_replay_caps.dpcd, + sizeof(connector->dp.panel_replay_caps.dpcd)); + if (ret < 0) + return; + + if (!(connector->dp.panel_replay_caps.dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_SUPPORT)] & + DP_PANEL_REPLAY_SUPPORT)) + return; if (intel_dp_is_edp(intel_dp)) { if (!intel_alpm_aux_less_wake_supported(intel_dp)) { @@ -603,30 +635,48 @@ static void _panel_replay_init_dpcd(struct intel_dp *intel_dp) return; } - if (!(intel_dp->pr_dpcd & DP_PANEL_REPLAY_EARLY_TRANSPORT_SUPPORT)) { + if (!(connector->dp.panel_replay_caps.dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_SUPPORT)] & + DP_PANEL_REPLAY_EARLY_TRANSPORT_SUPPORT)) { drm_dbg_kms(display->drm, "Panel doesn't support early transport, eDP Panel Replay not possible\n"); return; } } + connector->dp.panel_replay_caps.support = true; intel_dp->psr.sink_panel_replay_support = true; - if (intel_dp->pr_dpcd & DP_PANEL_REPLAY_SU_SUPPORT) - intel_dp->psr.sink_panel_replay_su_support = true; + if (connector->dp.panel_replay_caps.dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_SUPPORT)] & + DP_PANEL_REPLAY_SU_SUPPORT) { + connector->dp.panel_replay_caps.su_support = true; + + _panel_replay_compute_su_granularity(connector); + } + + connector->dp.panel_replay_caps.dsc_support = compute_pr_dsc_support(connector); drm_dbg_kms(display->drm, - "Panel replay %sis supported by panel\n", - intel_dp->psr.sink_panel_replay_su_support ? - "selective_update " : ""); + "Panel replay %sis supported by panel (in DSC mode: %s)\n", + connector->dp.panel_replay_caps.su_support ? + "selective_update " : "", + panel_replay_dsc_support_str(connector->dp.panel_replay_caps.dsc_support)); } -static void _psr_init_dpcd(struct intel_dp *intel_dp) +static void _psr_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector) { struct intel_display *display = to_intel_display(intel_dp); + int ret; + + ret = drm_dp_dpcd_read_data(&intel_dp->aux, DP_PSR_SUPPORT, connector->dp.psr_caps.dpcd, + sizeof(connector->dp.psr_caps.dpcd)); + if (ret < 0) + return; + + if (!connector->dp.psr_caps.dpcd[0]) + return; drm_dbg_kms(display->drm, "eDP panel supports PSR version %x\n", - intel_dp->psr_dpcd[0]); + connector->dp.psr_caps.dpcd[0]); if (drm_dp_has_quirk(&intel_dp->desc, DP_DPCD_QUIRK_NO_PSR)) { drm_dbg_kms(display->drm, @@ -640,13 +690,14 @@ static void _psr_init_dpcd(struct intel_dp *intel_dp) return; } + connector->dp.psr_caps.support = true; intel_dp->psr.sink_support = true; - intel_dp->psr.sink_sync_latency = - intel_dp_get_sink_sync_latency(intel_dp); + + connector->dp.psr_caps.sync_latency = intel_dp_get_sink_sync_latency(intel_dp); if (DISPLAY_VER(display) >= 9 && - intel_dp->psr_dpcd[0] >= DP_PSR2_WITH_Y_COORD_IS_SUPPORTED) { - bool y_req = intel_dp->psr_dpcd[1] & + connector->dp.psr_caps.dpcd[0] >= DP_PSR2_WITH_Y_COORD_IS_SUPPORTED) { + bool y_req = connector->dp.psr_caps.dpcd[1] & DP_PSR2_SU_Y_COORDINATE_REQUIRED; /* @@ -660,35 +711,32 @@ static void _psr_init_dpcd(struct intel_dp *intel_dp) * Y-coordinate requirement panels we would need to enable * GTC first. */ - intel_dp->psr.sink_psr2_support = y_req && + connector->dp.psr_caps.su_support = y_req && intel_alpm_aux_wake_supported(intel_dp); drm_dbg_kms(display->drm, "PSR2 %ssupported\n", - intel_dp->psr.sink_psr2_support ? "" : "not "); + connector->dp.psr_caps.su_support ? "" : "not "); + } + + if (connector->dp.psr_caps.su_support) { + ret = drm_dp_dpcd_read_byte(&intel_dp->aux, + INTEL_DPCD_INTEL_WA_REGISTER_CAPS, + &connector->dp.psr_caps.intel_wa_dpcd); + if (ret < 0) + return; + _psr_compute_su_granularity(intel_dp, connector); } } -void intel_psr_init_dpcd(struct intel_dp *intel_dp) +void intel_psr_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector) { - drm_dp_dpcd_read(&intel_dp->aux, DP_PSR_SUPPORT, intel_dp->psr_dpcd, - sizeof(intel_dp->psr_dpcd)); - drm_dp_dpcd_readb(&intel_dp->aux, DP_PANEL_REPLAY_CAP, - &intel_dp->pr_dpcd); - - if (intel_dp->pr_dpcd & DP_PANEL_REPLAY_SUPPORT) - _panel_replay_init_dpcd(intel_dp); - - if (intel_dp->psr_dpcd[0]) - _psr_init_dpcd(intel_dp); + _psr_init_dpcd(intel_dp, connector); - if (intel_dp->psr.sink_psr2_support || - intel_dp->psr.sink_panel_replay_su_support) - intel_dp_get_su_granularity(intel_dp); + _panel_replay_init_dpcd(intel_dp, connector); } static void hsw_psr_setup_aux(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); - struct drm_i915_private *dev_priv = to_i915(display->drm); enum transcoder cpu_transcoder = intel_dp->psr.transcoder; u32 aux_clock_divider, aux_ctl; /* write DP_SET_POWER=D0 */ @@ -703,7 +751,7 @@ static void hsw_psr_setup_aux(struct intel_dp *intel_dp) BUILD_BUG_ON(sizeof(aux_msg) > 20); for (i = 0; i < sizeof(aux_msg); i += 4) - intel_de_write(dev_priv, + intel_de_write(display, psr_aux_data_reg(display, cpu_transcoder, i >> 2), intel_dp_aux_pack(&aux_msg[i], sizeof(aux_msg) - i)); @@ -723,8 +771,9 @@ static void hsw_psr_setup_aux(struct intel_dp *intel_dp) aux_ctl); } -static bool psr2_su_region_et_valid(struct intel_dp *intel_dp, bool panel_replay) +static bool psr2_su_region_et_valid(struct intel_connector *connector, bool panel_replay) { + struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_display *display = to_intel_display(intel_dp); if (DISPLAY_VER(display) < 20 || !intel_dp_is_edp(intel_dp) || @@ -732,9 +781,9 @@ static bool psr2_su_region_et_valid(struct intel_dp *intel_dp, bool panel_replay return false; return panel_replay ? - intel_dp->pr_dpcd & DP_PANEL_REPLAY_EARLY_TRANSPORT_SUPPORT : - intel_dp->psr_dpcd[0] == DP_PSR2_WITH_Y_COORD_ET_SUPPORTED && - psr2_su_region_et_global_enabled(intel_dp); + connector->dp.panel_replay_caps.dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_SUPPORT)] & + DP_PANEL_REPLAY_EARLY_TRANSPORT_SUPPORT : + connector->dp.psr_caps.dpcd[0] == DP_PSR2_WITH_Y_COORD_ET_SUPPORTED; } static void _panel_replay_enable_sink(struct intel_dp *intel_dp, @@ -793,31 +842,10 @@ static void _psr_enable_sink(struct intel_dp *intel_dp, drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG, val); } -static void intel_psr_enable_sink_alpm(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state) -{ - u8 val; - - /* - * eDP Panel Replay uses always ALPM - * PSR2 uses ALPM but PSR1 doesn't - */ - if (!intel_dp_is_edp(intel_dp) || (!crtc_state->has_panel_replay && - !crtc_state->has_sel_update)) - return; - - val = DP_ALPM_ENABLE | DP_ALPM_LOCK_ERROR_IRQ_HPD_ENABLE; - - if (crtc_state->has_panel_replay) - val |= DP_ALPM_MODE_AUX_LESS; - - drm_dp_dpcd_writeb(&intel_dp->aux, DP_RECEIVER_ALPM_CONFIG, val); -} - -void intel_psr_enable_sink(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state) +static void intel_psr_enable_sink(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state) { - intel_psr_enable_sink_alpm(intel_dp, crtc_state); + intel_alpm_enable_sink(intel_dp, crtc_state); crtc_state->has_panel_replay ? _panel_replay_enable_sink(intel_dp, crtc_state) : @@ -827,11 +855,22 @@ void intel_psr_enable_sink(struct intel_dp *intel_dp, drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, DP_SET_POWER_D0); } +void intel_psr_panel_replay_enable_sink(struct intel_dp *intel_dp) +{ + /* + * NOTE: We might want to trigger mode set when + * disabling/enabling Panel Replay via debugfs interface to + * ensure this bit is cleared/set accordingly. + */ + if (CAN_PANEL_REPLAY(intel_dp) && panel_replay_global_enabled(intel_dp)) + drm_dp_dpcd_writeb(&intel_dp->aux, PANEL_REPLAY_CONFIG, + DP_PANEL_REPLAY_ENABLE); +} + static u32 intel_psr1_get_tp_time(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); struct intel_connector *connector = intel_dp->attached_connector; - struct drm_i915_private *dev_priv = to_i915(display->drm); u32 val = 0; if (DISPLAY_VER(display) >= 11) @@ -865,7 +904,7 @@ static u32 intel_psr1_get_tp_time(struct intel_dp *intel_dp) * WA 0479: hsw,bdw * "Do not skip both TP1 and TP2/TP3" */ - if (DISPLAY_VER(dev_priv) < 9 && + if (DISPLAY_VER(display) < 9 && connector->panel.vbt.psr.tp1_wakeup_time_us == 0 && connector->panel.vbt.psr.tp2_tp3_wakeup_time_us == 0) val |= EDP_PSR_TP2_TP3_TIME_100us; @@ -890,7 +929,7 @@ static u8 psr_compute_idle_frames(struct intel_dp *intel_dp) * off-by-one issue that HW has in some cases. */ idle_frames = max(6, connector->panel.vbt.psr.idle_frames); - idle_frames = max(idle_frames, intel_dp->psr.sink_sync_latency + 1); + idle_frames = max(idle_frames, connector->dp.psr_caps.sync_latency + 1); if (drm_WARN_ON(display->drm, idle_frames > 0xf)) idle_frames = 0xf; @@ -898,10 +937,22 @@ static u8 psr_compute_idle_frames(struct intel_dp *intel_dp) return idle_frames; } +static bool is_dc5_dc6_blocked(struct intel_dp *intel_dp) +{ + struct intel_display *display = to_intel_display(intel_dp); + u32 current_dc_state = intel_display_power_get_current_dc_state(display); + struct intel_crtc *crtc = intel_crtc_for_pipe(display, intel_dp->psr.pipe); + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(&crtc->base); + + return (current_dc_state != DC_STATE_EN_UPTO_DC5 && + current_dc_state != DC_STATE_EN_UPTO_DC6) || + intel_dp->psr.active_non_psr_pipes || + READ_ONCE(vblank->enabled); +} + static void hsw_activate_psr1(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); - struct drm_i915_private *dev_priv = to_i915(display->drm); enum transcoder cpu_transcoder = intel_dp->psr.transcoder; u32 max_sleep_time = 0x1f; u32 val = EDP_PSR_ENABLE; @@ -911,7 +962,7 @@ static void hsw_activate_psr1(struct intel_dp *intel_dp) if (DISPLAY_VER(display) < 20) val |= EDP_PSR_MAX_SLEEP_TIME(max_sleep_time); - if (IS_HASWELL(dev_priv)) + if (display->platform.haswell) val |= EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES; if (intel_dp->psr.link_standby) @@ -927,6 +978,14 @@ static void hsw_activate_psr1(struct intel_dp *intel_dp) intel_de_rmw(display, psr_ctl_reg(display, cpu_transcoder), ~EDP_PSR_RESTORE_PSR_ACTIVE_CTX_MASK, val); + + /* Wa_16025596647 */ + if ((DISPLAY_VER(display) == 20 || + IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) && + is_dc5_dc6_blocked(intel_dp) && intel_dp->psr.pkg_c_latency_used) + intel_dmc_start_pkgc_exit_at_start_of_undelayed_vblank(display, + intel_dp->psr.pipe, + true); } static u32 intel_psr2_get_tp_time(struct intel_dp *intel_dp) @@ -951,23 +1010,25 @@ static u32 intel_psr2_get_tp_time(struct intel_dp *intel_dp) return val; } -static int psr2_block_count_lines(struct intel_dp *intel_dp) +static int +psr2_block_count_lines(u8 io_wake_lines, u8 fast_wake_lines) { - return intel_dp->alpm_parameters.io_wake_lines < 9 && - intel_dp->alpm_parameters.fast_wake_lines < 9 ? 8 : 12; + return io_wake_lines < 9 && fast_wake_lines < 9 ? 8 : 12; } static int psr2_block_count(struct intel_dp *intel_dp) { - return psr2_block_count_lines(intel_dp) / 4; + return psr2_block_count_lines(intel_dp->psr.io_wake_lines, + intel_dp->psr.fast_wake_lines) / 4; } static u8 frames_before_su_entry(struct intel_dp *intel_dp) { + struct intel_connector *connector = intel_dp->attached_connector; u8 frames_before_su_entry; frames_before_su_entry = max_t(u8, - intel_dp->psr.sink_sync_latency + 1, + connector->dp.psr_caps.sync_latency + 1, 2); /* Entry setup frames must be at least 1 less than frames before SU entry */ @@ -1005,14 +1066,21 @@ static void dg2_activate_panel_replay(struct intel_dp *intel_dp) static void hsw_activate_psr2(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); - struct drm_i915_private *dev_priv = to_i915(display->drm); enum transcoder cpu_transcoder = intel_dp->psr.transcoder; u32 val = EDP_PSR2_ENABLE; u32 psr_val = 0; + u8 idle_frames; - val |= EDP_PSR2_IDLE_FRAMES(psr_compute_idle_frames(intel_dp)); + /* Wa_16025596647 */ + if ((DISPLAY_VER(display) == 20 || + IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) && + is_dc5_dc6_blocked(intel_dp) && intel_dp->psr.pkg_c_latency_used) + idle_frames = 0; + else + idle_frames = psr_compute_idle_frames(intel_dp); + val |= EDP_PSR2_IDLE_FRAMES(idle_frames); - if (DISPLAY_VER(display) < 14 && !IS_ALDERLAKE_P(dev_priv)) + if (DISPLAY_VER(display) < 14 && !display->platform.alderlake_p) val |= EDP_SU_TRACK_ENABLE; if (DISPLAY_VER(display) >= 10 && DISPLAY_VER(display) < 13) @@ -1030,7 +1098,7 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp) } /* Wa_22012278275:adl-p */ - if (IS_ALDERLAKE_P(dev_priv) && IS_DISPLAY_STEP(display, STEP_A0, STEP_E0)) { + if (intel_display_wa(display, INTEL_DISPLAY_WA_22012278275)) { static const u8 map[] = { 2, /* 5 lines */ 1, /* 6 lines */ @@ -1043,24 +1111,24 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp) }; /* * Still using the default IO_BUFFER_WAKE and FAST_WAKE, see - * comments bellow for more information + * comments below for more information */ int tmp; - tmp = map[intel_dp->alpm_parameters.io_wake_lines - + tmp = map[intel_dp->psr.io_wake_lines - TGL_EDP_PSR2_IO_BUFFER_WAKE_MIN_LINES]; val |= TGL_EDP_PSR2_IO_BUFFER_WAKE(tmp + TGL_EDP_PSR2_IO_BUFFER_WAKE_MIN_LINES); - tmp = map[intel_dp->alpm_parameters.fast_wake_lines - TGL_EDP_PSR2_FAST_WAKE_MIN_LINES]; + tmp = map[intel_dp->psr.fast_wake_lines - TGL_EDP_PSR2_FAST_WAKE_MIN_LINES]; val |= TGL_EDP_PSR2_FAST_WAKE(tmp + TGL_EDP_PSR2_FAST_WAKE_MIN_LINES); } else if (DISPLAY_VER(display) >= 20) { - val |= LNL_EDP_PSR2_IO_BUFFER_WAKE(intel_dp->alpm_parameters.io_wake_lines); + val |= LNL_EDP_PSR2_IO_BUFFER_WAKE(intel_dp->psr.io_wake_lines); } else if (DISPLAY_VER(display) >= 12) { - val |= TGL_EDP_PSR2_IO_BUFFER_WAKE(intel_dp->alpm_parameters.io_wake_lines); - val |= TGL_EDP_PSR2_FAST_WAKE(intel_dp->alpm_parameters.fast_wake_lines); + val |= TGL_EDP_PSR2_IO_BUFFER_WAKE(intel_dp->psr.io_wake_lines); + val |= TGL_EDP_PSR2_FAST_WAKE(intel_dp->psr.fast_wake_lines); } else if (DISPLAY_VER(display) >= 9) { - val |= EDP_PSR2_IO_BUFFER_WAKE(intel_dp->alpm_parameters.io_wake_lines); - val |= EDP_PSR2_FAST_WAKE(intel_dp->alpm_parameters.fast_wake_lines); + val |= EDP_PSR2_IO_BUFFER_WAKE(intel_dp->psr.io_wake_lines); + val |= EDP_PSR2_FAST_WAKE(intel_dp->psr.fast_wake_lines); } if (intel_dp->psr.req_psr2_sdp_prior_scanline) @@ -1095,9 +1163,7 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp) static bool transcoder_has_psr2(struct intel_display *display, enum transcoder cpu_transcoder) { - struct drm_i915_private *dev_priv = to_i915(display->drm); - - if (IS_ALDERLAKE_P(dev_priv) || DISPLAY_VER(display) >= 14) + if (display->platform.alderlake_p || DISPLAY_VER(display) >= 14) return cpu_transcoder == TRANSCODER_A || cpu_transcoder == TRANSCODER_B; else if (DISPLAY_VER(display) >= 12) return cpu_transcoder == TRANSCODER_A; @@ -1175,10 +1241,9 @@ dc3co_is_pipe_port_compatible(struct intel_dp *intel_dp, struct intel_display *display = to_intel_display(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe; - struct drm_i915_private *dev_priv = to_i915(display->drm); enum port port = dig_port->base.port; - if (IS_ALDERLAKE_P(dev_priv) || DISPLAY_VER(display) >= 14) + if (display->platform.alderlake_p || DISPLAY_VER(display) >= 14) return pipe <= PIPE_B && port <= PORT_B; else return pipe == PIPE_A && port == PORT_A; @@ -1189,7 +1254,6 @@ tgl_dc3co_exitline_compute_config(struct intel_dp *intel_dp, struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(intel_dp); - struct drm_i915_private *dev_priv = to_i915(display->drm); const u32 crtc_vdisplay = crtc_state->uapi.adjusted_mode.crtc_vdisplay; struct i915_power_domains *power_domains = &display->power.domains; u32 exit_scanlines; @@ -1215,7 +1279,7 @@ tgl_dc3co_exitline_compute_config(struct intel_dp *intel_dp, return; /* Wa_16011303918:adl-p */ - if (IS_ALDERLAKE_P(dev_priv) && IS_DISPLAY_STEP(display, STEP_A0, STEP_B0)) + if (intel_display_wa(display, INTEL_DISPLAY_WA_16011303918)) return; /* @@ -1243,47 +1307,52 @@ static bool intel_psr2_sel_fetch_config_valid(struct intel_dp *intel_dp, return false; } - if (crtc_state->uapi.async_flip) { - drm_dbg_kms(display->drm, - "PSR2 sel fetch not enabled, async flip enabled\n"); - return false; - } - return crtc_state->enable_psr2_sel_fetch = true; } -static bool psr2_granularity_check(struct intel_dp *intel_dp, - struct intel_crtc_state *crtc_state) +static bool psr2_granularity_check(struct intel_crtc_state *crtc_state, + struct intel_connector *connector) { + struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_display *display = to_intel_display(intel_dp); - struct drm_i915_private *dev_priv = to_i915(display->drm); const struct drm_dsc_config *vdsc_cfg = &crtc_state->dsc.config; const int crtc_hdisplay = crtc_state->hw.adjusted_mode.crtc_hdisplay; const int crtc_vdisplay = crtc_state->hw.adjusted_mode.crtc_vdisplay; u16 y_granularity = 0; + u16 sink_y_granularity = crtc_state->has_panel_replay ? + connector->dp.panel_replay_caps.su_y_granularity : + connector->dp.psr_caps.su_y_granularity; + u16 sink_w_granularity; + + if (crtc_state->has_panel_replay) + sink_w_granularity = connector->dp.panel_replay_caps.su_w_granularity == + DP_PANEL_REPLAY_FULL_LINE_GRANULARITY ? + crtc_hdisplay : connector->dp.panel_replay_caps.su_w_granularity; + else + sink_w_granularity = connector->dp.psr_caps.su_w_granularity; /* PSR2 HW only send full lines so we only need to validate the width */ - if (crtc_hdisplay % intel_dp->psr.su_w_granularity) + if (crtc_hdisplay % sink_w_granularity) return false; - if (crtc_vdisplay % intel_dp->psr.su_y_granularity) + if (crtc_vdisplay % sink_y_granularity) return false; /* HW tracking is only aligned to 4 lines */ if (!crtc_state->enable_psr2_sel_fetch) - return intel_dp->psr.su_y_granularity == 4; + return sink_y_granularity == 4; /* * adl_p and mtl platforms have 1 line granularity. * For other platforms with SW tracking we can adjust the y coordinates * to match sink requirement if multiple of 4. */ - if (IS_ALDERLAKE_P(dev_priv) || DISPLAY_VER(display) >= 14) - y_granularity = intel_dp->psr.su_y_granularity; - else if (intel_dp->psr.su_y_granularity <= 2) + if (display->platform.alderlake_p || DISPLAY_VER(display) >= 14) + y_granularity = sink_y_granularity; + else if (sink_y_granularity <= 2) y_granularity = 4; - else if ((intel_dp->psr.su_y_granularity % 4) == 0) - y_granularity = intel_dp->psr.su_y_granularity; + else if ((sink_y_granularity % 4) == 0) + y_granularity = sink_y_granularity; if (y_granularity == 0 || crtc_vdisplay % y_granularity) return false; @@ -1296,9 +1365,35 @@ static bool psr2_granularity_check(struct intel_dp *intel_dp, return true; } -static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_dp, - struct intel_crtc_state *crtc_state) +static bool apply_scanline_indication_wa(struct intel_crtc_state *crtc_state, + struct intel_connector *connector) { + struct intel_dp *intel_dp = intel_attached_dp(connector); + u8 early_scanline_support = connector->dp.psr_caps.intel_wa_dpcd & + INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK; + + if (intel_dp->edp_dpcd[0] >= DP_EDP_15) + return true; + + switch (early_scanline_support) { + case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1: + crtc_state->req_psr2_sdp_prior_scanline = false; + return false; + case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE: + return true; + case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE: + crtc_state->req_psr2_sdp_prior_scanline = false; + return true; + default: + MISSING_CASE(early_scanline_support); + return false; + } +} + +static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_crtc_state *crtc_state, + struct intel_connector *connector) +{ + struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_display *display = to_intel_display(intel_dp); const struct drm_display_mode *adjusted_mode = &crtc_state->uapi.adjusted_mode; u32 hblank_total, hblank_ns, req_ns; @@ -1317,20 +1412,23 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d return false; crtc_state->req_psr2_sdp_prior_scanline = true; - return true; + + return apply_scanline_indication_wa(crtc_state, connector); } static int intel_psr_entry_setup_frames(struct intel_dp *intel_dp, + struct drm_connector_state *conn_state, const struct drm_display_mode *adjusted_mode) { struct intel_display *display = to_intel_display(intel_dp); - int psr_setup_time = drm_dp_psr_setup_time(intel_dp->psr_dpcd); + struct intel_connector *connector = to_intel_connector(conn_state->connector); + int psr_setup_time = drm_dp_psr_setup_time(connector->dp.psr_caps.dpcd); int entry_setup_frames = 0; if (psr_setup_time < 0) { drm_dbg_kms(display->drm, "PSR condition failed: Invalid PSR setup time (0x%02x)\n", - intel_dp->psr_dpcd[1]); + connector->dp.psr_caps.dpcd[1]); return -ETIME; } @@ -1353,22 +1451,54 @@ static int intel_psr_entry_setup_frames(struct intel_dp *intel_dp, return entry_setup_frames; } -static bool wake_lines_fit_into_vblank(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state, - bool aux_less) +static +int _intel_psr_min_set_context_latency(const struct intel_crtc_state *crtc_state, + bool needs_panel_replay, + bool needs_sel_update) { - struct intel_display *display = to_intel_display(intel_dp); - int vblank = crtc_state->hw.adjusted_mode.crtc_vblank_end - - crtc_state->hw.adjusted_mode.crtc_vblank_start; - int wake_lines; + struct intel_display *display = to_intel_display(crtc_state); - if (aux_less) - wake_lines = intel_dp->alpm_parameters.aux_less_wake_lines; + if (!crtc_state->has_psr) + return 0; + + /* Wa_14015401596 */ + if (intel_vrr_possible(crtc_state) && IS_DISPLAY_VER(display, 13, 14)) + return 1; + + /* Rest is for SRD_STATUS needed on LunarLake and onwards */ + if (DISPLAY_VER(display) < 20) + return 0; + + /* + * Comment on SRD_STATUS register in Bspec for LunarLake and onwards: + * + * To deterministically capture the transition of the state machine + * going from SRDOFFACK to IDLE, the delayed V. Blank should be at least + * one line after the non-delayed V. Blank. + * + * Legacy TG: TRANS_SET_CONTEXT_LATENCY > 0 + * VRR TG: TRANS_VRR_CTL[ VRR Guardband ] < (TRANS_VRR_VMAX[ VRR Vmax ] + * - TRANS_VTOTAL[ Vertical Active ]) + * + * SRD_STATUS is used only by PSR1 on PantherLake. + * SRD_STATUS is used by PSR1 and Panel Replay DP on LunarLake. + */ + + if (DISPLAY_VER(display) >= 30 && (needs_panel_replay || + needs_sel_update)) + return 0; + else if (DISPLAY_VER(display) < 30 && (needs_sel_update || + intel_crtc_has_type(crtc_state, + INTEL_OUTPUT_EDP))) + return 0; else - wake_lines = DISPLAY_VER(display) < 20 ? - psr2_block_count_lines(intel_dp) : - intel_dp->alpm_parameters.io_wake_lines; + return 1; +} +static bool _wake_lines_fit_into_vblank(const struct intel_crtc_state *crtc_state, + int vblank, + int wake_lines) +{ if (crtc_state->req_psr2_sdp_prior_scanline) vblank -= 1; @@ -1379,9 +1509,46 @@ static bool wake_lines_fit_into_vblank(struct intel_dp *intel_dp, return true; } +static bool wake_lines_fit_into_vblank(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state, + bool aux_less, + bool needs_panel_replay, + bool needs_sel_update) +{ + struct intel_display *display = to_intel_display(intel_dp); + int vblank = crtc_state->hw.adjusted_mode.crtc_vblank_end - + crtc_state->hw.adjusted_mode.crtc_vblank_start; + int wake_lines; + int scl = _intel_psr_min_set_context_latency(crtc_state, + needs_panel_replay, + needs_sel_update); + vblank -= scl; + + if (aux_less) + wake_lines = crtc_state->alpm_state.aux_less_wake_lines; + else + 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; + + /* + * Guardband has not been computed yet, so we conservatively check if the + * full vblank duration is sufficient to accommodate wake line requirements + * for PSR features like Panel Replay and Selective Update. + * + * Once the actual guardband is available, a more accurate validation is + * performed in intel_psr_compute_config_late(), and PSR features are + * disabled if wake lines exceed the available guardband. + */ + return _wake_lines_fit_into_vblank(crtc_state, vblank, wake_lines); +} + static bool alpm_config_valid(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state, - bool aux_less) + struct intel_crtc_state *crtc_state, + bool aux_less, + bool needs_panel_replay, + bool needs_sel_update) { struct intel_display *display = to_intel_display(intel_dp); @@ -1391,7 +1558,8 @@ static bool alpm_config_valid(struct intel_dp *intel_dp, return false; } - if (!wake_lines_fit_into_vblank(intel_dp, crtc_state, aux_less)) { + if (!wake_lines_fit_into_vblank(intel_dp, crtc_state, aux_less, + needs_panel_replay, needs_sel_update)) { drm_dbg_kms(display->drm, "PSR2/Panel Replay not enabled, too short vblank time\n"); return false; @@ -1401,32 +1569,32 @@ static bool alpm_config_valid(struct intel_dp *intel_dp, } static bool intel_psr2_config_valid(struct intel_dp *intel_dp, - struct intel_crtc_state *crtc_state) + struct intel_crtc_state *crtc_state, + struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(intel_dp); - struct drm_i915_private *dev_priv = to_i915(display->drm); + struct intel_connector *connector = to_intel_connector(conn_state->connector); int crtc_hdisplay = crtc_state->hw.adjusted_mode.crtc_hdisplay; int crtc_vdisplay = crtc_state->hw.adjusted_mode.crtc_vdisplay; int psr_max_h = 0, psr_max_v = 0, max_bpp = 0; - if (!intel_dp->psr.sink_psr2_support) + if (!connector->dp.psr_caps.su_support || display->params.enable_psr == 1) return false; /* JSL and EHL only supports eDP 1.3 */ - if (IS_JASPERLAKE(dev_priv) || IS_ELKHARTLAKE(dev_priv)) { + if (display->platform.jasperlake || display->platform.elkhartlake) { drm_dbg_kms(display->drm, "PSR2 not supported by phy\n"); return false; } /* Wa_16011181250 */ - if (IS_ROCKETLAKE(dev_priv) || IS_ALDERLAKE_S(dev_priv) || - IS_DG2(dev_priv)) { + if (intel_display_wa(display, INTEL_DISPLAY_WA_16011181250)) { drm_dbg_kms(display->drm, "PSR2 is defeatured for this platform\n"); return false; } - if (IS_ALDERLAKE_P(dev_priv) && IS_DISPLAY_STEP(display, STEP_A0, STEP_B0)) { + if (display->platform.alderlake_p && IS_DISPLAY_STEP(display, STEP_A0, STEP_B0)) { drm_dbg_kms(display->drm, "PSR2 not completely functional in this stepping\n"); return false; @@ -1445,7 +1613,7 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp, * over PSR2. */ if (crtc_state->dsc.compression_enable && - (DISPLAY_VER(display) < 14 && !IS_ALDERLAKE_P(dev_priv))) { + (DISPLAY_VER(display) < 14 && !display->platform.alderlake_p)) { drm_dbg_kms(display->drm, "PSR2 cannot be enabled since DSC is enabled\n"); return false; @@ -1478,13 +1646,13 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp, /* Wa_16011303918:adl-p */ if (crtc_state->vrr.enable && - IS_ALDERLAKE_P(dev_priv) && IS_DISPLAY_STEP(display, STEP_A0, STEP_B0)) { + display->platform.alderlake_p && IS_DISPLAY_STEP(display, STEP_A0, STEP_B0)) { drm_dbg_kms(display->drm, "PSR2 not enabled, not compatible with HW stepping + VRR\n"); return false; } - if (!alpm_config_valid(intel_dp, crtc_state, false)) + if (!alpm_config_valid(intel_dp, crtc_state, false, false, true)) return false; if (!crtc_state->enable_psr2_sel_fetch && @@ -1501,9 +1669,11 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp, return true; } -static bool intel_sel_update_config_valid(struct intel_dp *intel_dp, - struct intel_crtc_state *crtc_state) +static bool intel_sel_update_config_valid(struct intel_crtc_state *crtc_state, + struct drm_connector_state *conn_state) { + struct intel_connector *connector = to_intel_connector(conn_state->connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_display *display = to_intel_display(intel_dp); if (HAS_PSR2_SEL_FETCH(display) && @@ -1514,24 +1684,37 @@ static bool intel_sel_update_config_valid(struct intel_dp *intel_dp, goto unsupported; } - if (!psr2_global_enabled(intel_dp)) { + if (!sel_update_global_enabled(intel_dp)) { drm_dbg_kms(display->drm, "Selective update disabled by flag\n"); goto unsupported; } - if (!crtc_state->has_panel_replay && !intel_psr2_config_valid(intel_dp, crtc_state)) + if (!crtc_state->has_panel_replay && !intel_psr2_config_valid(intel_dp, crtc_state, + conn_state)) goto unsupported; - if (!_compute_psr2_sdp_prior_scanline_indication(intel_dp, crtc_state)) { + if (!_compute_psr2_sdp_prior_scanline_indication(crtc_state, connector)) { drm_dbg_kms(display->drm, "Selective update not enabled, SDP indication do not fit in hblank\n"); goto unsupported; } - if (crtc_state->has_panel_replay && (DISPLAY_VER(display) < 14 || - !intel_dp->psr.sink_panel_replay_su_support)) - goto unsupported; + if (crtc_state->has_panel_replay) { + if (DISPLAY_VER(display) < 14) + goto unsupported; + + if (!connector->dp.panel_replay_caps.su_support) + goto unsupported; + + if (intel_dsc_enabled_on_link(crtc_state) && + connector->dp.panel_replay_caps.dsc_support != + INTEL_DP_PANEL_REPLAY_DSC_SELECTIVE_UPDATE) { + drm_dbg_kms(display->drm, + "Selective update with Panel Replay not enabled because it's not supported with DSC\n"); + goto unsupported; + } + } if (crtc_state->crc_enabled) { drm_dbg_kms(display->drm, @@ -1539,14 +1722,14 @@ static bool intel_sel_update_config_valid(struct intel_dp *intel_dp, goto unsupported; } - if (!psr2_granularity_check(intel_dp, crtc_state)) { + if (!psr2_granularity_check(crtc_state, connector)) { drm_dbg_kms(display->drm, "Selective update not enabled, SU granularity not compatible\n"); goto unsupported; } - crtc_state->enable_psr2_su_region_et = - psr2_su_region_et_valid(intel_dp, crtc_state->has_panel_replay); + crtc_state->enable_psr2_su_region_et = psr2_su_region_et_valid(connector, + crtc_state->has_panel_replay); return true; @@ -1556,20 +1739,28 @@ unsupported: } static bool _psr_compute_config(struct intel_dp *intel_dp, - struct intel_crtc_state *crtc_state) + struct intel_crtc_state *crtc_state, + struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(intel_dp); const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; int entry_setup_frames; - if (!CAN_PSR(intel_dp)) + if (!CAN_PSR(intel_dp) || !display->params.enable_psr) + return false; + + /* + * Currently PSR doesn't work reliably with VRR enabled. + */ + if (crtc_state->vrr.enable) return false; - entry_setup_frames = intel_psr_entry_setup_frames(intel_dp, adjusted_mode); + entry_setup_frames = intel_psr_entry_setup_frames(intel_dp, conn_state, adjusted_mode); if (entry_setup_frames >= 0) { - intel_dp->psr.entry_setup_frames = entry_setup_frames; + crtc_state->entry_setup_frames = entry_setup_frames; } else { + crtc_state->no_psr_reason = "PSR setup timing not met"; drm_dbg_kms(display->drm, "PSR condition failed: PSR setup timing not met\n"); return false; @@ -1578,24 +1769,55 @@ static bool _psr_compute_config(struct intel_dp *intel_dp, return true; } -static bool -_panel_replay_compute_config(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state, - const struct drm_connector_state *conn_state) +static inline bool compute_link_off_after_as_sdp_when_pr_active(struct intel_connector *connector) +{ + return (connector->dp.panel_replay_caps.dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_CAPABILITY)] & + DP_PANEL_REPLAY_LINK_OFF_SUPPORTED_IN_PR_AFTER_ADAPTIVE_SYNC_SDP); +} + +static inline bool compute_disable_as_sdp_when_pr_active(struct intel_connector *connector) +{ + return !(connector->dp.panel_replay_caps.dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_CAPABILITY)] & + DP_PANEL_REPLAY_ASYNC_VIDEO_TIMING_NOT_SUPPORTED_IN_PR); +} + +static bool _panel_replay_compute_config(struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) { - struct intel_display *display = to_intel_display(intel_dp); struct intel_connector *connector = to_intel_connector(conn_state->connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_display *display = to_intel_display(intel_dp); struct intel_hdcp *hdcp = &connector->hdcp; if (!CAN_PANEL_REPLAY(intel_dp)) return false; + if (!connector->dp.panel_replay_caps.support) + return false; + if (!panel_replay_global_enabled(intel_dp)) { drm_dbg_kms(display->drm, "Panel Replay disabled by flag\n"); return false; } + if (crtc_state->crc_enabled) { + drm_dbg_kms(display->drm, + "Panel Replay not enabled because it would inhibit pipe CRC calculation\n"); + return false; + } + + if (intel_dsc_enabled_on_link(crtc_state) && + connector->dp.panel_replay_caps.dsc_support == + INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED) { + drm_dbg_kms(display->drm, + "Panel Replay not enabled because it's not supported with DSC\n"); + return false; + } + + crtc_state->link_off_after_as_sdp_when_pr_active = compute_link_off_after_as_sdp_when_pr_active(connector); + crtc_state->disable_as_sdp_when_pr_active = compute_disable_as_sdp_when_pr_active(connector); + if (!intel_dp_is_edp(intel_dp)) return true; @@ -1623,15 +1845,9 @@ _panel_replay_compute_config(struct intel_dp *intel_dp, return false; } - if (!alpm_config_valid(intel_dp, crtc_state, true)) + if (!alpm_config_valid(intel_dp, crtc_state, true, true, false)) return false; - if (crtc_state->crc_enabled) { - drm_dbg_kms(display->drm, - "Panel Replay not enabled because it would inhibit pipe CRC calculation\n"); - return false; - } - return true; } @@ -1640,15 +1856,43 @@ static bool intel_psr_needs_wa_18037818876(struct intel_dp *intel_dp, { struct intel_display *display = to_intel_display(intel_dp); - return (DISPLAY_VER(display) == 20 && intel_dp->psr.entry_setup_frames > 0 && + return (DISPLAY_VER(display) == 20 && crtc_state->entry_setup_frames > 0 && !crtc_state->has_sel_update); } +static +void intel_psr_set_non_psr_pipes(struct intel_dp *intel_dp, + struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(intel_dp); + struct intel_atomic_state *state = to_intel_atomic_state(crtc_state->uapi.state); + struct intel_crtc *crtc; + u8 active_pipes = 0; + + /* Wa_16025596647 */ + if (!intel_display_wa(display, INTEL_DISPLAY_WA_16025596647)) + return; + + /* Not needed by Panel Replay */ + if (crtc_state->has_panel_replay) + return; + + /* We ignore possible secondary PSR/Panel Replay capable eDP */ + for_each_intel_crtc(display->drm, crtc) + active_pipes |= crtc->active ? BIT(crtc->pipe) : 0; + + active_pipes = intel_calc_active_pipes(state, active_pipes); + + crtc_state->active_non_psr_pipes = active_pipes & + ~BIT(to_intel_crtc(crtc_state->uapi.crtc)->pipe); +} + void intel_psr_compute_config(struct intel_dp *intel_dp, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(intel_dp); + struct intel_connector *connector = to_intel_connector(conn_state->connector); const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; if (!psr_global_enabled(intel_dp)) { @@ -1679,30 +1923,17 @@ void intel_psr_compute_config(struct intel_dp *intel_dp, return; } - /* - * Currently PSR/PR doesn't work reliably with VRR enabled. - */ - if (crtc_state->vrr.enable) - return; - - crtc_state->has_panel_replay = _panel_replay_compute_config(intel_dp, - crtc_state, - conn_state); + /* Only used for state verification. */ + crtc_state->panel_replay_dsc_support = connector->dp.panel_replay_caps.dsc_support; + crtc_state->has_panel_replay = _panel_replay_compute_config(crtc_state, conn_state); crtc_state->has_psr = crtc_state->has_panel_replay ? true : - _psr_compute_config(intel_dp, crtc_state); + _psr_compute_config(intel_dp, crtc_state, conn_state); if (!crtc_state->has_psr) return; - crtc_state->has_sel_update = intel_sel_update_config_valid(intel_dp, crtc_state); - - /* Wa_18037818876 */ - if (intel_psr_needs_wa_18037818876(intel_dp, crtc_state)) { - crtc_state->has_psr = false; - drm_dbg_kms(display->drm, - "PSR disabled to workaround PSR FSM hang issue\n"); - } + crtc_state->has_sel_update = intel_sel_update_config_valid(crtc_state, conn_state); } void intel_psr_get_config(struct intel_encoder *encoder, @@ -1773,6 +2004,8 @@ static void intel_psr_activate(struct intel_dp *intel_dp) drm_WARN_ON(display->drm, intel_dp->psr.active); + drm_WARN_ON(display->drm, !intel_dp->psr.enabled); + lockdep_assert_held(&intel_dp->psr.lock); /* psr1, psr2 and panel-replay are mutually exclusive.*/ @@ -1784,6 +2017,7 @@ static void intel_psr_activate(struct intel_dp *intel_dp) hsw_activate_psr1(intel_dp); intel_dp->psr.active = true; + intel_dp->psr.no_psr_reason = NULL; } /* @@ -1819,7 +2053,6 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(intel_dp); - struct drm_i915_private *dev_priv = to_i915(display->drm); enum transcoder cpu_transcoder = intel_dp->psr.transcoder; u32 mask = 0; @@ -1858,7 +2091,7 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp, * As a workaround leave LPSP unmasked to prevent PSR entry * when external displays are active. */ - if (DISPLAY_VER(display) >= 8 || IS_HASWELL_ULT(dev_priv)) + if (DISPLAY_VER(display) >= 8 || display->platform.haswell_ult) mask |= EDP_PSR_DEBUG_MASK_LPSP; if (DISPLAY_VER(display) < 20) @@ -1872,7 +2105,7 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp, mask |= EDP_PSR_DEBUG_MASK_DISP_REG_WRITE; /* allow PSR with sprite enabled */ - if (IS_HASWELL(dev_priv)) + if (display->platform.haswell) mask |= EDP_PSR_DEBUG_MASK_SPRITE_ENABLE; } @@ -1895,9 +2128,6 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp, intel_dp->psr.psr2_sel_fetch_enabled ? IGNORE_PSR2_HW_TRACKING : 0); - if (intel_dp_is_edp(intel_dp)) - intel_alpm_configure(intel_dp, crtc_state); - /* * Wa_16013835468 * Wa_14015648006 @@ -1917,7 +2147,7 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp, */ if (!intel_dp->psr.panel_replay_enabled && (IS_DISPLAY_VERx100_STEP(display, 1400, STEP_A0, STEP_B0) || - IS_ALDERLAKE_P(dev_priv))) + display->platform.alderlake_p)) intel_de_rmw(display, CHICKEN_TRANS(display, cpu_transcoder), 0, ADLP_1_BASED_X_GRANULARITY); @@ -1928,10 +2158,21 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp, MTL_CLKGATE_DIS_TRANS(display, cpu_transcoder), 0, MTL_CLKGATE_DIS_TRANS_DMASC_GATING_DIS); - else if (IS_ALDERLAKE_P(dev_priv)) + else if (display->platform.alderlake_p) intel_de_rmw(display, CLKGATE_DIS_MISC, 0, CLKGATE_DIS_MISC_DMASC_GATING_DIS); } + + /* Wa_16025596647 */ + if ((DISPLAY_VER(display) == 20 || + IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) && + !intel_dp->psr.panel_replay_enabled) + intel_dmc_block_pkgc(display, intel_dp->psr.pipe, true); + + intel_alpm_configure(intel_dp, crtc_state); + + if (HAS_PSR_TRANS_PUSH_FRAME_CHANGE(display)) + intel_vrr_psr_frame_change_enable(crtc_state); } static bool psr_interrupt_error_check(struct intel_dp *intel_dp) @@ -1987,29 +2228,42 @@ static void intel_psr_enable_locked(struct intel_dp *intel_dp, intel_dp->psr.psr2_sel_fetch_cff_enabled = false; intel_dp->psr.req_psr2_sdp_prior_scanline = crtc_state->req_psr2_sdp_prior_scanline; + intel_dp->psr.active_non_psr_pipes = crtc_state->active_non_psr_pipes; + intel_dp->psr.pkg_c_latency_used = crtc_state->pkg_c_latency_used; + intel_dp->psr.io_wake_lines = crtc_state->alpm_state.io_wake_lines; + intel_dp->psr.fast_wake_lines = crtc_state->alpm_state.fast_wake_lines; + intel_dp->psr.entry_setup_frames = crtc_state->entry_setup_frames; if (!psr_interrupt_error_check(intel_dp)) return; - if (intel_dp->psr.panel_replay_enabled) { + if (intel_dp->psr.panel_replay_enabled) drm_dbg_kms(display->drm, "Enabling Panel Replay\n"); - } else { + else drm_dbg_kms(display->drm, "Enabling PSR%s\n", intel_dp->psr.sel_update_enabled ? "2" : "1"); - /* - * Panel replay has to be enabled before link training: doing it - * only for PSR here. - */ - intel_psr_enable_sink(intel_dp, crtc_state); - } + /* + * Enabling sink PSR/Panel Replay here only for PSR. Panel Replay enable + * bit is already written at this point. Sink ALPM is enabled here for + * PSR and Panel Replay. See + * intel_psr_panel_replay_enable_sink. Modifiers/options: + * - Selective Update + * - Region Early Transport + * - Selective Update Region Scanline Capture + * - VSC_SDP_CRC + * - HPD on different Errors + * - CRC verification + * are written for PSR and Panel Replay here. + */ + intel_psr_enable_sink(intel_dp, crtc_state); if (intel_dp_is_edp(intel_dp)) intel_snps_phy_update_psr_power_state(&dig_port->base, true); intel_psr_enable_source(intel_dp, crtc_state); intel_dp->psr.enabled = true; - intel_dp->psr.paused = false; + intel_dp->psr.pause_counter = 0; /* * Link_ok is sticky and set here on PSR enable. We can assume link @@ -2055,6 +2309,13 @@ static void intel_psr_exit(struct intel_dp *intel_dp) drm_WARN_ON(display->drm, !(val & EDP_PSR2_ENABLE)); } else { + if ((DISPLAY_VER(display) == 20 || + IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) && + intel_dp->psr.pkg_c_latency_used) + intel_dmc_start_pkgc_exit_at_start_of_undelayed_vblank(display, + intel_dp->psr.pipe, + false); + val = intel_de_rmw(display, psr_ctl_reg(display, cpu_transcoder), EDP_PSR_ENABLE, 0); @@ -2081,15 +2342,14 @@ static void intel_psr_wait_exit_locked(struct intel_dp *intel_dp) } /* Wait till PSR is idle */ - if (intel_de_wait_for_clear(display, psr_status, - psr_status_mask, 2000)) + if (intel_de_wait_for_clear_ms(display, psr_status, + psr_status_mask, 2000)) drm_err(display->drm, "Timed out waiting PSR idle state\n"); } static void intel_psr_disable_locked(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); - struct drm_i915_private *dev_priv = to_i915(display->drm); enum transcoder cpu_transcoder = intel_dp->psr.transcoder; lockdep_assert_held(&intel_dp->psr.lock); @@ -2121,7 +2381,7 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp) intel_de_rmw(display, MTL_CLKGATE_DIS_TRANS(display, cpu_transcoder), MTL_CLKGATE_DIS_TRANS_DMASC_GATING_DIS, 0); - else if (IS_ALDERLAKE_P(dev_priv)) + else if (display->platform.alderlake_p) intel_de_rmw(display, CLKGATE_DIS_MISC, CLKGATE_DIS_MISC_DMASC_GATING_DIS, 0); } @@ -2129,16 +2389,8 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp) if (intel_dp_is_edp(intel_dp)) intel_snps_phy_update_psr_power_state(&dp_to_dig_port(intel_dp)->base, false); - /* Panel Replay on eDP is always using ALPM aux less. */ - if (intel_dp->psr.panel_replay_enabled && intel_dp_is_edp(intel_dp)) { - intel_de_rmw(display, ALPM_CTL(display, cpu_transcoder), - ALPM_CTL_ALPM_ENABLE | - ALPM_CTL_ALPM_AUX_LESS_ENABLE, 0); - - intel_de_rmw(display, - PORT_ALPM_CTL(cpu_transcoder), - PORT_ALPM_CTL_ALPM_AUX_LESS_ENABLE, 0); - } + if (intel_dp->psr.panel_replay_enabled && intel_dp_is_edp(intel_dp)) + intel_alpm_disable(intel_dp); /* Disable PSR on Sink */ if (!intel_dp->psr.panel_replay_enabled) { @@ -2149,12 +2401,20 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp) DP_RECEIVER_ALPM_CONFIG, 0); } + /* Wa_16025596647 */ + if ((DISPLAY_VER(display) == 20 || + IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) && + !intel_dp->psr.panel_replay_enabled) + intel_dmc_block_pkgc(display, intel_dp->psr.pipe, false); + intel_dp->psr.enabled = false; intel_dp->psr.panel_replay_enabled = false; intel_dp->psr.sel_update_enabled = false; intel_dp->psr.psr2_sel_fetch_enabled = false; intel_dp->psr.su_region_et_enabled = false; intel_dp->psr.psr2_sel_fetch_cff_enabled = false; + intel_dp->psr.active_non_psr_pipes = 0; + intel_dp->psr.pkg_c_latency_used = 0; } /** @@ -2172,7 +2432,8 @@ void intel_psr_disable(struct intel_dp *intel_dp, if (!old_crtc_state->has_psr) return; - if (drm_WARN_ON(display->drm, !CAN_PSR(intel_dp))) + if (drm_WARN_ON(display->drm, !CAN_PSR(intel_dp) && + !CAN_PANEL_REPLAY(intel_dp))) return; mutex_lock(&intel_dp->psr.lock); @@ -2194,7 +2455,6 @@ void intel_psr_disable(struct intel_dp *intel_dp, */ void intel_psr_pause(struct intel_dp *intel_dp) { - struct intel_display *display = to_intel_display(intel_dp); struct intel_psr *psr = &intel_dp->psr; if (!CAN_PSR(intel_dp) && !CAN_PANEL_REPLAY(intel_dp)) @@ -2207,12 +2467,10 @@ void intel_psr_pause(struct intel_dp *intel_dp) return; } - /* If we ever hit this, we will need to add refcount to pause/resume */ - drm_WARN_ON(display->drm, psr->paused); - - intel_psr_exit(intel_dp); - intel_psr_wait_exit_locked(intel_dp); - psr->paused = true; + if (intel_dp->psr.pause_counter++ == 0) { + intel_psr_exit(intel_dp); + intel_psr_wait_exit_locked(intel_dp); + } mutex_unlock(&psr->lock); @@ -2228,6 +2486,7 @@ void intel_psr_pause(struct intel_dp *intel_dp) */ void intel_psr_resume(struct intel_dp *intel_dp) { + struct intel_display *display = to_intel_display(intel_dp); struct intel_psr *psr = &intel_dp->psr; if (!CAN_PSR(intel_dp) && !CAN_PANEL_REPLAY(intel_dp)) @@ -2235,28 +2494,36 @@ void intel_psr_resume(struct intel_dp *intel_dp) mutex_lock(&psr->lock); - if (!psr->paused) - goto unlock; + if (!psr->enabled) + goto out; - psr->paused = false; - intel_psr_activate(intel_dp); + if (!psr->pause_counter) { + drm_warn(display->drm, "Unbalanced PSR pause/resume!\n"); + goto out; + } -unlock: + if (--intel_dp->psr.pause_counter == 0) + intel_psr_activate(intel_dp); + +out: mutex_unlock(&psr->lock); } /** - * intel_psr_needs_block_dc_vblank - Check if block dc entry is needed + * intel_psr_needs_vblank_notification - Check if PSR need vblank enable/disable + * notification. * @crtc_state: CRTC status * * We need to block DC6 entry in case of Panel Replay as enabling VBI doesn't * prevent it in case of Panel Replay. Panel Replay switches main link off on * DC entry. This means vblank interrupts are not fired and is a problem if - * user-space is polling for vblank events. + * user-space is polling for vblank events. Also Wa_16025596647 needs + * information when vblank is enabled/disabled. */ -bool intel_psr_needs_block_dc_vblank(const struct intel_crtc_state *crtc_state) +bool intel_psr_needs_vblank_notification(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct intel_display *display = to_intel_display(crtc_state); struct intel_encoder *encoder; for_each_encoder_on_crtc(crtc->base.dev, &crtc->base, encoder) { @@ -2267,61 +2534,88 @@ bool intel_psr_needs_block_dc_vblank(const struct intel_crtc_state *crtc_state) intel_dp = enc_to_intel_dp(encoder); - if (intel_dp_is_edp(intel_dp) && - CAN_PANEL_REPLAY(intel_dp)) + if (!intel_dp_is_edp(intel_dp)) + continue; + + if (CAN_PANEL_REPLAY(intel_dp)) + return true; + + if ((DISPLAY_VER(display) == 20 || + IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) && + CAN_PSR(intel_dp)) return true; } return false; } -static u32 man_trk_ctl_enable_bit_get(struct intel_display *display) +/** + * intel_psr_trigger_frame_change_event - Trigger "Frame Change" event + * @dsb: DSB context + * @state: the atomic state + * @crtc: the CRTC + * + * Generate PSR "Frame Change" event. + */ +void intel_psr_trigger_frame_change_event(struct intel_dsb *dsb, + struct intel_atomic_state *state, + struct intel_crtc *crtc) { - struct drm_i915_private *dev_priv = to_i915(display->drm); + const struct intel_crtc_state *crtc_state = + intel_pre_commit_crtc_state(state, crtc); + struct intel_display *display = to_intel_display(crtc); - return IS_ALDERLAKE_P(dev_priv) || DISPLAY_VER(display) >= 14 ? 0 : + if (!crtc_state->has_psr || intel_psr_use_trans_push(crtc_state)) + return; + + intel_de_write_dsb(display, dsb, + CURSURFLIVE(display, crtc->pipe), 0); +} + +/** + * intel_psr_min_set_context_latency - Minimum 'set context latency' lines needed by PSR + * @crtc_state: the crtc state + * + * Return minimum SCL lines/delay needed by PSR. + */ +int intel_psr_min_set_context_latency(const struct intel_crtc_state *crtc_state) +{ + + return _intel_psr_min_set_context_latency(crtc_state, + crtc_state->has_panel_replay, + crtc_state->has_sel_update); +} + +static u32 man_trk_ctl_enable_bit_get(struct intel_display *display) +{ + return display->platform.alderlake_p || DISPLAY_VER(display) >= 14 ? 0 : PSR2_MAN_TRK_CTL_ENABLE; } static u32 man_trk_ctl_single_full_frame_bit_get(struct intel_display *display) { - struct drm_i915_private *dev_priv = to_i915(display->drm); - - return IS_ALDERLAKE_P(dev_priv) || DISPLAY_VER(display) >= 14 ? + return display->platform.alderlake_p || DISPLAY_VER(display) >= 14 ? ADLP_PSR2_MAN_TRK_CTL_SF_SINGLE_FULL_FRAME : PSR2_MAN_TRK_CTL_SF_SINGLE_FULL_FRAME; } static u32 man_trk_ctl_partial_frame_bit_get(struct intel_display *display) { - struct drm_i915_private *dev_priv = to_i915(display->drm); - - return IS_ALDERLAKE_P(dev_priv) || DISPLAY_VER(display) >= 14 ? + return display->platform.alderlake_p || DISPLAY_VER(display) >= 14 ? ADLP_PSR2_MAN_TRK_CTL_SF_PARTIAL_FRAME_UPDATE : PSR2_MAN_TRK_CTL_SF_PARTIAL_FRAME_UPDATE; } static u32 man_trk_ctl_continuos_full_frame(struct intel_display *display) { - struct drm_i915_private *dev_priv = to_i915(display->drm); - - return IS_ALDERLAKE_P(dev_priv) || DISPLAY_VER(display) >= 14 ? + return display->platform.alderlake_p || DISPLAY_VER(display) >= 14 ? ADLP_PSR2_MAN_TRK_CTL_SF_CONTINUOS_FULL_FRAME : PSR2_MAN_TRK_CTL_SF_CONTINUOS_FULL_FRAME; } -static void psr_force_hw_tracking_exit(struct intel_dp *intel_dp) +static void intel_psr_force_update(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); - enum transcoder cpu_transcoder = intel_dp->psr.transcoder; - - if (intel_dp->psr.psr2_sel_fetch_enabled) - intel_de_write(display, - PSR2_MAN_TRK_CTL(display, cpu_transcoder), - man_trk_ctl_enable_bit_get(display) | - man_trk_ctl_partial_frame_bit_get(display) | - man_trk_ctl_single_full_frame_bit_get(display) | - man_trk_ctl_continuos_full_frame(display)); /* * Display WA #0884: skl+ @@ -2339,7 +2633,8 @@ static void psr_force_hw_tracking_exit(struct intel_dp *intel_dp) intel_de_write(display, CURSURFLIVE(display, intel_dp->psr.pipe), 0); } -void intel_psr2_program_trans_man_trk_ctl(const struct intel_crtc_state *crtc_state) +void intel_psr2_program_trans_man_trk_ctl(struct intel_dsb *dsb, + const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); @@ -2353,35 +2648,41 @@ void intel_psr2_program_trans_man_trk_ctl(const struct intel_crtc_state *crtc_st crtc_state->uapi.encoder_mask) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - lockdep_assert_held(&intel_dp->psr.lock); - if (intel_dp->psr.psr2_sel_fetch_cff_enabled) + if (!dsb) + lockdep_assert_held(&intel_dp->psr.lock); + + if (DISPLAY_VER(display) < 20 && intel_dp->psr.psr2_sel_fetch_cff_enabled) return; break; } - intel_de_write(display, PSR2_MAN_TRK_CTL(display, cpu_transcoder), - crtc_state->psr2_man_track_ctl); + intel_de_write_dsb(display, dsb, + PSR2_MAN_TRK_CTL(display, cpu_transcoder), + crtc_state->psr2_man_track_ctl); if (!crtc_state->enable_psr2_su_region_et) return; - intel_de_write(display, PIPE_SRCSZ_ERLY_TPT(crtc->pipe), - crtc_state->pipe_srcsz_early_tpt); + intel_de_write_dsb(display, dsb, PIPE_SRCSZ_ERLY_TPT(crtc->pipe), + crtc_state->pipe_srcsz_early_tpt); + + if (!crtc_state->dsc.compression_enable) + return; + + intel_dsc_su_et_parameters_configure(dsb, encoder, crtc_state, + drm_rect_height(&crtc_state->psr2_su_area)); } static void psr2_man_trk_ctl_calc(struct intel_crtc_state *crtc_state, bool full_update) { struct intel_display *display = to_intel_display(crtc_state); - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); u32 val = man_trk_ctl_enable_bit_get(display); /* SF partial frame enable has to be set even on full update */ val |= man_trk_ctl_partial_frame_bit_get(display); if (full_update) { - val |= man_trk_ctl_single_full_frame_bit_get(display); val |= man_trk_ctl_continuos_full_frame(display); goto exit; } @@ -2389,7 +2690,7 @@ static void psr2_man_trk_ctl_calc(struct intel_crtc_state *crtc_state, if (crtc_state->psr2_su_area.y1 == -1) goto exit; - if (IS_ALDERLAKE_P(dev_priv) || DISPLAY_VER(display) >= 14) { + if (display->platform.alderlake_p || DISPLAY_VER(display) >= 14) { val |= ADLP_PSR2_MAN_TRK_CTL_SU_REGION_START_ADDR(crtc_state->psr2_su_area.y1); val |= ADLP_PSR2_MAN_TRK_CTL_SU_REGION_END_ADDR(crtc_state->psr2_su_area.y2 - 1); } else { @@ -2422,9 +2723,9 @@ static u32 psr2_pipe_srcsz_early_tpt_calc(struct intel_crtc_state *crtc_state, static void clip_area_update(struct drm_rect *overlap_damage_area, struct drm_rect *damage_area, - struct drm_rect *pipe_src) + struct drm_rect *display_area) { - if (!drm_rect_intersect(damage_area, pipe_src)) + if (!drm_rect_intersect(damage_area, display_area)) return; if (overlap_damage_area->y1 == -1) { @@ -2440,24 +2741,32 @@ static void clip_area_update(struct drm_rect *overlap_damage_area, overlap_damage_area->y2 = damage_area->y2; } -static void intel_psr2_sel_fetch_pipe_alignment(struct intel_crtc_state *crtc_state) +static bool intel_psr2_sel_fetch_pipe_alignment(struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); - struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); const struct drm_dsc_config *vdsc_cfg = &crtc_state->dsc.config; u16 y_alignment; + bool su_area_changed = false; /* ADLP aligns the SU region to vdsc slice height in case dsc is enabled */ if (crtc_state->dsc.compression_enable && - (IS_ALDERLAKE_P(dev_priv) || DISPLAY_VER(display) >= 14)) + (display->platform.alderlake_p || DISPLAY_VER(display) >= 14)) y_alignment = vdsc_cfg->slice_height; else y_alignment = crtc_state->su_y_granularity; - crtc_state->psr2_su_area.y1 -= crtc_state->psr2_su_area.y1 % y_alignment; - if (crtc_state->psr2_su_area.y2 % y_alignment) + if (crtc_state->psr2_su_area.y1 % y_alignment) { + crtc_state->psr2_su_area.y1 -= crtc_state->psr2_su_area.y1 % y_alignment; + su_area_changed = true; + } + + if (crtc_state->psr2_su_area.y2 % y_alignment) { crtc_state->psr2_su_area.y2 = ((crtc_state->psr2_su_area.y2 / y_alignment) + 1) * y_alignment; + su_area_changed = true; + } + + return su_area_changed; } /* @@ -2467,6 +2776,7 @@ static void intel_psr2_sel_fetch_pipe_alignment(struct intel_crtc_state *crtc_st static void intel_psr2_sel_fetch_et_alignment(struct intel_atomic_state *state, struct intel_crtc *crtc, + struct drm_rect *display_area, bool *cursor_in_su_area) { struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); @@ -2480,7 +2790,7 @@ intel_psr2_sel_fetch_et_alignment(struct intel_atomic_state *state, for_each_new_intel_plane_in_state(state, plane, new_plane_state, i) { struct drm_rect inter; - if (new_plane_state->uapi.crtc != crtc_state->uapi.crtc) + if (new_plane_state->hw.crtc != crtc_state->uapi.crtc) continue; if (plane->id != PLANE_CURSOR) @@ -2494,7 +2804,7 @@ intel_psr2_sel_fetch_et_alignment(struct intel_atomic_state *state, continue; clip_area_update(&crtc_state->psr2_su_area, &new_plane_state->uapi.dst, - &crtc_state->pipe_src); + display_area); *cursor_in_su_area = true; } } @@ -2513,7 +2823,7 @@ static bool psr2_sel_fetch_plane_state_supported(const struct intel_plane_state if (plane_state->uapi.dst.y1 < 0 || plane_state->uapi.dst.x1 < 0 || plane_state->scaler_id >= 0 || - plane_state->uapi.rotation != DRM_MODE_ROTATE_0) + plane_state->hw.rotation != DRM_MODE_ROTATE_0) return false; return true; @@ -2528,7 +2838,8 @@ static bool psr2_sel_fetch_plane_state_supported(const struct intel_plane_state */ static bool psr2_sel_fetch_pipe_state_supported(const struct intel_crtc_state *crtc_state) { - if (crtc_state->scaler_state.scaler_id >= 0) + if (crtc_state->scaler_state.scaler_id >= 0 || + crtc_state->async_flip_planes) return false; return true; @@ -2570,12 +2881,11 @@ static void intel_psr_apply_su_area_workarounds(struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); - struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); /* Wa_14014971492 */ if (!crtc_state->has_panel_replay && ((IS_DISPLAY_VERx100_STEP(display, 1400, STEP_A0, STEP_B0) || - IS_ALDERLAKE_P(i915) || IS_TIGERLAKE(i915))) && + display->platform.alderlake_p || display->platform.tigerlake)) && crtc_state->splitter.enable) crtc_state->psr2_su_area.y1 = 0; @@ -2591,7 +2901,13 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); struct intel_plane_state *new_plane_state, *old_plane_state; struct intel_plane *plane; - bool full_update = false, cursor_in_su_area = false; + struct drm_rect display_area = { + .x1 = 0, + .y1 = 0, + .x2 = crtc_state->hw.adjusted_mode.crtc_hdisplay, + .y2 = crtc_state->hw.adjusted_mode.crtc_vdisplay, + }; + bool full_update = false, su_area_changed; int i, ret; if (!crtc_state->enable_psr2_sel_fetch) @@ -2604,7 +2920,7 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, crtc_state->psr2_su_area.x1 = 0; crtc_state->psr2_su_area.y1 = -1; - crtc_state->psr2_su_area.x2 = drm_rect_width(&crtc_state->pipe_src); + crtc_state->psr2_su_area.x2 = drm_rect_width(&display_area); crtc_state->psr2_su_area.y2 = -1; /* @@ -2618,7 +2934,7 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, struct drm_rect src, damaged_area = { .x1 = 0, .y1 = -1, .x2 = INT_MAX }; - if (new_plane_state->uapi.crtc != crtc_state->uapi.crtc) + if (new_plane_state->hw.crtc != crtc_state->uapi.crtc) continue; if (!new_plane_state->uapi.visible && @@ -2642,14 +2958,14 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, damaged_area.y1 = old_plane_state->uapi.dst.y1; damaged_area.y2 = old_plane_state->uapi.dst.y2; clip_area_update(&crtc_state->psr2_su_area, &damaged_area, - &crtc_state->pipe_src); + &display_area); } if (new_plane_state->uapi.visible) { damaged_area.y1 = new_plane_state->uapi.dst.y1; damaged_area.y2 = new_plane_state->uapi.dst.y2; clip_area_update(&crtc_state->psr2_su_area, &damaged_area, - &crtc_state->pipe_src); + &display_area); } continue; } else if (new_plane_state->uapi.alpha != old_plane_state->uapi.alpha) { @@ -2657,7 +2973,7 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, damaged_area.y1 = new_plane_state->uapi.dst.y1; damaged_area.y2 = new_plane_state->uapi.dst.y2; clip_area_update(&crtc_state->psr2_su_area, &damaged_area, - &crtc_state->pipe_src); + &display_area); continue; } @@ -2673,7 +2989,7 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, damaged_area.x1 += new_plane_state->uapi.dst.x1 - src.x1; damaged_area.x2 += new_plane_state->uapi.dst.x1 - src.x1; - clip_area_update(&crtc_state->psr2_su_area, &damaged_area, &crtc_state->pipe_src); + clip_area_update(&crtc_state->psr2_su_area, &damaged_area, &display_area); } /* @@ -2698,15 +3014,33 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, if (ret) return ret; - /* - * Adjust su area to cover cursor fully as necessary (early - * transport). This needs to be done after - * drm_atomic_add_affected_planes to ensure visible cursor is added into - * affected planes even when cursor is not updated by itself. - */ - intel_psr2_sel_fetch_et_alignment(state, crtc, &cursor_in_su_area); + do { + bool cursor_in_su_area = false; + + /* + * Adjust su area to cover cursor fully as necessary + * (early transport). This needs to be done after + * drm_atomic_add_affected_planes to ensure visible + * cursor is added into affected planes even when + * cursor is not updated by itself. + */ + intel_psr2_sel_fetch_et_alignment(state, crtc, &display_area, + &cursor_in_su_area); - intel_psr2_sel_fetch_pipe_alignment(crtc_state); + su_area_changed = intel_psr2_sel_fetch_pipe_alignment(crtc_state); + + /* + * If the cursor was outside the SU area before + * alignment, the alignment step (which only expands + * SU) may pull the cursor partially inside, so we + * must run ET alignment again to fully cover it. But + * if the cursor was already fully inside before + * alignment, expanding the SU area won't change that, + * so no further work is needed. + */ + if (cursor_in_su_area) + break; + } while (su_area_changed); /* * Now that we have the pipe damaged area check if it intersect with @@ -2717,7 +3051,7 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, struct drm_rect *sel_fetch_area, inter; struct intel_plane *linked = new_plane_state->planar_linked_plane; - if (new_plane_state->uapi.crtc != crtc_state->uapi.crtc || + if (new_plane_state->hw.crtc != crtc_state->uapi.crtc || !new_plane_state->uapi.visible) continue; @@ -2766,17 +3100,40 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, } skip_sel_fetch_set_loop: + if (full_update) + clip_area_update(&crtc_state->psr2_su_area, &display_area, + &display_area); + psr2_man_trk_ctl_calc(crtc_state, full_update); crtc_state->pipe_srcsz_early_tpt = psr2_pipe_srcsz_early_tpt_calc(crtc_state, full_update); return 0; } +void intel_psr2_panic_force_full_update(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + u32 val = man_trk_ctl_enable_bit_get(display); + + /* SF partial frame enable has to be set even on full update */ + val |= man_trk_ctl_partial_frame_bit_get(display); + val |= man_trk_ctl_continuos_full_frame(display); + + /* Directly write the register */ + intel_de_write_fw(display, PSR2_MAN_TRK_CTL(display, cpu_transcoder), val); + + if (!crtc_state->enable_psr2_su_region_et) + return; + + intel_de_write_fw(display, PIPE_SRCSZ_ERLY_TPT(crtc->pipe), 0); +} + void intel_psr_pre_plane_update(struct intel_atomic_state *state, struct intel_crtc *crtc) { struct intel_display *display = to_intel_display(state); - struct drm_i915_private *i915 = to_i915(state->base.dev); const struct intel_crtc_state *old_crtc_state = intel_atomic_get_old_crtc_state(state, crtc); const struct intel_crtc_state *new_crtc_state = @@ -2790,37 +3147,54 @@ void intel_psr_pre_plane_update(struct intel_atomic_state *state, old_crtc_state->uapi.encoder_mask) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct intel_psr *psr = &intel_dp->psr; - bool needs_to_disable = false; mutex_lock(&psr->lock); - /* - * Reasons to disable: - * - PSR disabled in new state - * - All planes will go inactive - * - Changing between PSR versions - * - Region Early Transport changing - * - Display WA #1136: skl, bxt - */ - needs_to_disable |= intel_crtc_needs_modeset(new_crtc_state); - needs_to_disable |= !new_crtc_state->has_psr; - needs_to_disable |= !new_crtc_state->active_planes; - needs_to_disable |= new_crtc_state->has_sel_update != psr->sel_update_enabled; - needs_to_disable |= new_crtc_state->enable_psr2_su_region_et != - psr->su_region_et_enabled; - needs_to_disable |= DISPLAY_VER(i915) < 11 && - new_crtc_state->wm_level_disabled; - - if (psr->enabled && needs_to_disable) - intel_psr_disable_locked(intel_dp); - else if (psr->enabled && new_crtc_state->wm_level_disabled) - /* Wa_14015648006 */ - wm_optimization_wa(intel_dp, new_crtc_state); + if (!new_crtc_state->has_psr) + psr->no_psr_reason = new_crtc_state->no_psr_reason; + + if (psr->enabled) { + /* + * Reasons to disable: + * - PSR disabled in new state + * - All planes will go inactive + * - Changing between PSR versions + * - Region Early Transport changing + * - Display WA #1136: skl, bxt + */ + if (intel_crtc_needs_modeset(new_crtc_state) || + new_crtc_state->update_m_n || + new_crtc_state->update_lrr || + !new_crtc_state->has_psr || + !new_crtc_state->active_planes || + new_crtc_state->has_sel_update != psr->sel_update_enabled || + new_crtc_state->enable_psr2_su_region_et != psr->su_region_et_enabled || + new_crtc_state->has_panel_replay != psr->panel_replay_enabled || + (DISPLAY_VER(display) < 11 && new_crtc_state->wm_level_disabled)) + intel_psr_disable_locked(intel_dp); + else if (new_crtc_state->wm_level_disabled) + /* Wa_14015648006 */ + wm_optimization_wa(intel_dp, new_crtc_state); + } mutex_unlock(&psr->lock); } } +static void +verify_panel_replay_dsc_state(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + + if (!crtc_state->has_panel_replay) + return; + + drm_WARN_ON(display->drm, + intel_dsc_enabled_on_link(crtc_state) && + crtc_state->panel_replay_dsc_support == + INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED); +} + void intel_psr_post_plane_update(struct intel_atomic_state *state, struct intel_crtc *crtc) { @@ -2832,6 +3206,8 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state, if (!crtc_state->has_psr) return; + verify_panel_replay_dsc_state(crtc_state); + for_each_intel_encoder_mask_with_psr(state->base.dev, encoder, crtc_state->uapi.encoder_mask) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); @@ -2843,12 +3219,19 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state, drm_WARN_ON(display->drm, psr->enabled && !crtc_state->active_planes); - keep_disabled |= psr->sink_not_reliable; - keep_disabled |= !crtc_state->active_planes; + if (psr->sink_not_reliable) + keep_disabled = true; + + if (!crtc_state->active_planes) { + psr->no_psr_reason = "All planes inactive"; + keep_disabled = true; + } /* Display WA #1136: skl, bxt */ - keep_disabled |= DISPLAY_VER(display) < 11 && - crtc_state->wm_level_disabled; + if (DISPLAY_VER(display) < 11 && crtc_state->wm_level_disabled) { + psr->no_psr_reason = "Workaround #1136 for skl, bxt"; + keep_disabled = true; + } if (!psr->enabled && !keep_disabled) intel_psr_enable_locked(intel_dp, crtc_state); @@ -2858,7 +3241,7 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state, /* Force a PSR exit when enabling CRC to avoid CRC timeouts */ if (crtc_state->crc_enabled && psr->enabled) - psr_force_hw_tracking_exit(intel_dp); + intel_psr_force_update(intel_dp); /* * Clear possible busy bits in case we have @@ -2870,35 +3253,57 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state, } } -static int _psr2_ready_for_pipe_update_locked(struct intel_dp *intel_dp) +/* + * From bspec: Panel Self Refresh (BDW+) + * Max. time for PSR to idle = Inverse of the refresh rate + 6 ms of + * exit training time + 1.5 ms of aux channel handshake. 50 ms is + * defensive enough to cover everything. + */ +#define PSR_IDLE_TIMEOUT_MS 50 + +static int +_psr2_ready_for_pipe_update_locked(const struct intel_crtc_state *new_crtc_state, + struct intel_dsb *dsb) { - struct intel_display *display = to_intel_display(intel_dp); - enum transcoder cpu_transcoder = intel_dp->psr.transcoder; + struct intel_display *display = to_intel_display(new_crtc_state); + enum transcoder cpu_transcoder = new_crtc_state->cpu_transcoder; /* * Any state lower than EDP_PSR2_STATUS_STATE_DEEP_SLEEP is enough. * As all higher states has bit 4 of PSR2 state set we can just wait for * EDP_PSR2_STATUS_STATE_DEEP_SLEEP to be cleared. */ - return intel_de_wait_for_clear(display, + if (dsb) { + intel_dsb_poll(dsb, EDP_PSR2_STATUS(display, cpu_transcoder), + EDP_PSR2_STATUS_STATE_DEEP_SLEEP, 0, 200, + PSR_IDLE_TIMEOUT_MS * 1000 / 200); + return true; + } + + return intel_de_wait_for_clear_ms(display, EDP_PSR2_STATUS(display, cpu_transcoder), - EDP_PSR2_STATUS_STATE_DEEP_SLEEP, 50); + EDP_PSR2_STATUS_STATE_DEEP_SLEEP, + PSR_IDLE_TIMEOUT_MS); } -static int _psr1_ready_for_pipe_update_locked(struct intel_dp *intel_dp) +static int +_psr1_ready_for_pipe_update_locked(const struct intel_crtc_state *new_crtc_state, + struct intel_dsb *dsb) { - struct intel_display *display = to_intel_display(intel_dp); - enum transcoder cpu_transcoder = intel_dp->psr.transcoder; + struct intel_display *display = to_intel_display(new_crtc_state); + enum transcoder cpu_transcoder = new_crtc_state->cpu_transcoder; - /* - * From bspec: Panel Self Refresh (BDW+) - * Max. time for PSR to idle = Inverse of the refresh rate + 6 ms of - * exit training time + 1.5 ms of aux channel handshake. 50 ms is - * defensive enough to cover everything. - */ - return intel_de_wait_for_clear(display, + if (dsb) { + intel_dsb_poll(dsb, psr_status_reg(display, cpu_transcoder), + EDP_PSR_STATUS_STATE_MASK, 0, 200, + PSR_IDLE_TIMEOUT_MS * 1000 / 200); + return true; + } + + return intel_de_wait_for_clear_ms(display, psr_status_reg(display, cpu_transcoder), - EDP_PSR_STATUS_STATE_MASK, 50); + EDP_PSR_STATUS_STATE_MASK, + PSR_IDLE_TIMEOUT_MS); } /** @@ -2927,9 +3332,11 @@ void intel_psr_wait_for_idle_locked(const struct intel_crtc_state *new_crtc_stat continue; if (intel_dp->psr.sel_update_enabled) - ret = _psr2_ready_for_pipe_update_locked(intel_dp); + ret = _psr2_ready_for_pipe_update_locked(new_crtc_state, + NULL); else - ret = _psr1_ready_for_pipe_update_locked(intel_dp); + ret = _psr1_ready_for_pipe_update_locked(new_crtc_state, + NULL); if (ret) drm_err(display->drm, @@ -2937,6 +3344,18 @@ void intel_psr_wait_for_idle_locked(const struct intel_crtc_state *new_crtc_stat } } +void intel_psr_wait_for_idle_dsb(struct intel_dsb *dsb, + const struct intel_crtc_state *new_crtc_state) +{ + if (!new_crtc_state->has_psr || new_crtc_state->has_panel_replay) + return; + + if (new_crtc_state->has_sel_update) + _psr2_ready_for_pipe_update_locked(new_crtc_state, dsb); + else + _psr1_ready_for_pipe_update_locked(new_crtc_state, dsb); +} + static bool __psr_wait_for_idle_locked(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); @@ -2959,14 +3378,14 @@ static bool __psr_wait_for_idle_locked(struct intel_dp *intel_dp) mutex_unlock(&intel_dp->psr.lock); - err = intel_de_wait_for_clear(display, reg, mask, 50); + err = intel_de_wait_for_clear_ms(display, reg, mask, 50); if (err) drm_err(display->drm, "Timed out waiting for PSR Idle for re-enable\n"); /* After the unlocked wait, verify that PSR is still wanted! */ mutex_lock(&intel_dp->psr.lock); - return err == 0 && intel_dp->psr.enabled; + return err == 0 && intel_dp->psr.enabled && !intel_dp->psr.pause_counter; } static int intel_psr_fastset_force(struct intel_display *display) @@ -3095,8 +3514,13 @@ static void intel_psr_work(struct work_struct *work) if (!intel_dp->psr.enabled) goto unlock; - if (READ_ONCE(intel_dp->psr.irq_aux_error)) + if (READ_ONCE(intel_dp->psr.irq_aux_error)) { intel_psr_handle_irq(intel_dp); + goto unlock; + } + + if (intel_dp->psr.pause_counter) + goto unlock; /* * We have to make sure PSR is ready for re-enable @@ -3120,31 +3544,37 @@ unlock: mutex_unlock(&intel_dp->psr.lock); } -static void _psr_invalidate_handle(struct intel_dp *intel_dp) +static void intel_psr_configure_full_frame_update(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); enum transcoder cpu_transcoder = intel_dp->psr.transcoder; - if (intel_dp->psr.psr2_sel_fetch_enabled) { - u32 val; - - if (intel_dp->psr.psr2_sel_fetch_cff_enabled) { - /* Send one update otherwise lag is observed in screen */ - intel_de_write(display, - CURSURFLIVE(display, intel_dp->psr.pipe), - 0); - return; - } + if (!intel_dp->psr.psr2_sel_fetch_enabled) + return; - val = man_trk_ctl_enable_bit_get(display) | - man_trk_ctl_partial_frame_bit_get(display) | - man_trk_ctl_continuos_full_frame(display); + if (DISPLAY_VER(display) >= 20) + intel_de_write(display, LNL_SFF_CTL(cpu_transcoder), + LNL_SFF_CTL_SF_SINGLE_FULL_FRAME); + else intel_de_write(display, PSR2_MAN_TRK_CTL(display, cpu_transcoder), - val); - intel_de_write(display, - CURSURFLIVE(display, intel_dp->psr.pipe), 0); - intel_dp->psr.psr2_sel_fetch_cff_enabled = true; + man_trk_ctl_enable_bit_get(display) | + man_trk_ctl_partial_frame_bit_get(display) | + man_trk_ctl_single_full_frame_bit_get(display) | + man_trk_ctl_continuos_full_frame(display)); +} + +static void _psr_invalidate_handle(struct intel_dp *intel_dp) +{ + struct intel_display *display = to_intel_display(intel_dp); + + if (DISPLAY_VER(display) < 20 && intel_dp->psr.psr2_sel_fetch_enabled) { + if (!intel_dp->psr.psr2_sel_fetch_cff_enabled) { + intel_dp->psr.psr2_sel_fetch_cff_enabled = true; + intel_psr_configure_full_frame_update(intel_dp); + } + + intel_psr_force_update(intel_dp); } else { intel_psr_exit(intel_dp); } @@ -3202,7 +3632,6 @@ tgl_dc3co_flush_locked(struct intel_dp *intel_dp, unsigned int frontbuffer_bits, enum fb_op_origin origin) { struct intel_display *display = to_intel_display(intel_dp); - struct drm_i915_private *i915 = to_i915(display->drm); if (!intel_dp->psr.dc3co_exitline || !intel_dp->psr.sel_update_enabled || !intel_dp->psr.active) @@ -3217,52 +3646,50 @@ tgl_dc3co_flush_locked(struct intel_dp *intel_dp, unsigned int frontbuffer_bits, return; tgl_psr2_enable_dc3co(intel_dp); - mod_delayed_work(i915->unordered_wq, &intel_dp->psr.dc3co_work, + mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work, intel_dp->psr.dc3co_exit_delay); } static void _psr_flush_handle(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); - struct drm_i915_private *dev_priv = to_i915(display->drm); - enum transcoder cpu_transcoder = intel_dp->psr.transcoder; - if (intel_dp->psr.psr2_sel_fetch_enabled) { + if (DISPLAY_VER(display) >= 20) { + /* + * We can use PSR exit on LunarLake onwards. Also + * using trans push mechanism to trigger Frame Change + * event requires using PSR exit. + */ + intel_psr_exit(intel_dp); + } else if (intel_dp->psr.psr2_sel_fetch_enabled) { + /* Selective fetch prior LNL */ if (intel_dp->psr.psr2_sel_fetch_cff_enabled) { /* can we turn CFF off? */ - if (intel_dp->psr.busy_frontbuffer_bits == 0) { - u32 val = man_trk_ctl_enable_bit_get(display) | - man_trk_ctl_partial_frame_bit_get(display) | - man_trk_ctl_single_full_frame_bit_get(display) | - man_trk_ctl_continuos_full_frame(display); - - /* - * Set psr2_sel_fetch_cff_enabled as false to allow selective - * updates. Still keep cff bit enabled as we don't have proper - * SU configuration in case update is sent for any reason after - * sff bit gets cleared by the HW on next vblank. - */ - intel_de_write(display, - PSR2_MAN_TRK_CTL(display, cpu_transcoder), - val); - intel_de_write(display, - CURSURFLIVE(display, intel_dp->psr.pipe), - 0); + if (intel_dp->psr.busy_frontbuffer_bits == 0) intel_dp->psr.psr2_sel_fetch_cff_enabled = false; - } - } else { - /* - * continuous full frame is disabled, only a single full - * frame is required - */ - psr_force_hw_tracking_exit(intel_dp); } - } else { - psr_force_hw_tracking_exit(intel_dp); - if (!intel_dp->psr.active && !intel_dp->psr.busy_frontbuffer_bits) - queue_work(dev_priv->unordered_wq, &intel_dp->psr.work); + /* + * Still keep cff bit enabled as we don't have proper SU + * configuration in case update is sent for any reason after + * sff bit gets cleared by the HW on next vblank. + * + * NOTE: Setting cff bit is not needed for LunarLake onwards as + * we have own register for SFF bit and we are not overwriting + * existing SU configuration + */ + intel_psr_configure_full_frame_update(intel_dp); + + intel_psr_force_update(intel_dp); + } else { + /* + * On older platforms using PSR exit was seen causing problems + */ + intel_psr_force_update(intel_dp); } + + if (!intel_dp->psr.active && !intel_dp->psr.busy_frontbuffer_bits) + queue_work(display->wq.unordered, &intel_dp->psr.work); } /** @@ -3302,7 +3729,7 @@ void intel_psr_flush(struct intel_display *display, * we have to ensure that the PSR is not activated until * intel_psr_resume() is called. */ - if (intel_dp->psr.paused) + if (intel_dp->psr.pause_counter) goto unlock; if (origin == ORIGIN_FLIP || @@ -3399,29 +3826,14 @@ static int psr_get_status_and_error_status(struct intel_dp *intel_dp, static void psr_alpm_check(struct intel_dp *intel_dp) { - struct intel_display *display = to_intel_display(intel_dp); - struct drm_dp_aux *aux = &intel_dp->aux; struct intel_psr *psr = &intel_dp->psr; - u8 val; - int r; if (!psr->sel_update_enabled) return; - r = drm_dp_dpcd_readb(aux, DP_RECEIVER_ALPM_STATUS, &val); - if (r != 1) { - drm_err(display->drm, "Error reading ALPM status\n"); - return; - } - - if (val & DP_ALPM_LOCK_TIMEOUT_ERROR) { + if (intel_alpm_get_error(intel_dp)) { intel_psr_disable_locked(intel_dp); psr->sink_not_reliable = true; - drm_dbg_kms(display->drm, - "ALPM lock timeout error, disabling PSR\n"); - - /* Clearing error */ - drm_dp_dpcd_writeb(aux, DP_RECEIVER_ALPM_STATUS, val); } } @@ -3606,6 +4018,167 @@ void intel_psr_unlock(const struct intel_crtc_state *crtc_state) } } +/* Wa_16025596647 */ +static void intel_psr_apply_underrun_on_idle_wa_locked(struct intel_dp *intel_dp) +{ + struct intel_display *display = to_intel_display(intel_dp); + bool dc5_dc6_blocked; + + if (!intel_dp->psr.active || !intel_dp->psr.pkg_c_latency_used) + return; + + dc5_dc6_blocked = is_dc5_dc6_blocked(intel_dp); + + if (intel_dp->psr.sel_update_enabled) + psr2_program_idle_frames(intel_dp, dc5_dc6_blocked ? 0 : + psr_compute_idle_frames(intel_dp)); + else + intel_dmc_start_pkgc_exit_at_start_of_undelayed_vblank(display, + intel_dp->psr.pipe, + dc5_dc6_blocked); +} + +static void psr_dc5_dc6_wa_work(struct work_struct *work) +{ + struct intel_display *display = container_of(work, typeof(*display), + psr_dc5_dc6_wa_work); + struct intel_encoder *encoder; + + 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 (intel_dp->psr.enabled && !intel_dp->psr.panel_replay_enabled && + !intel_dp->psr.pkg_c_latency_used) + intel_psr_apply_underrun_on_idle_wa_locked(intel_dp); + + mutex_unlock(&intel_dp->psr.lock); + } +} + +/** + * intel_psr_notify_dc5_dc6 - Notify PSR about enable/disable dc5/dc6 + * @display: intel atomic state + * + * This is targeted for underrun on idle PSR HW bug (Wa_16025596647) to schedule + * psr_dc5_dc6_wa_work used for applying/removing the workaround. + */ +void intel_psr_notify_dc5_dc6(struct intel_display *display) +{ + if (!intel_display_wa(display, INTEL_DISPLAY_WA_16025596647)) + return; + + schedule_work(&display->psr_dc5_dc6_wa_work); +} + +/** + * intel_psr_dc5_dc6_wa_init - Init work for underrun on idle PSR HW bug wa + * @display: intel atomic state + * + * This is targeted for underrun on idle PSR HW bug (Wa_16025596647) to init + * psr_dc5_dc6_wa_work used for applying the workaround. + */ +void intel_psr_dc5_dc6_wa_init(struct intel_display *display) +{ + if (!intel_display_wa(display, INTEL_DISPLAY_WA_16025596647)) + return; + + INIT_WORK(&display->psr_dc5_dc6_wa_work, psr_dc5_dc6_wa_work); +} + +/** + * intel_psr_notify_pipe_change - Notify PSR about enable/disable of a pipe + * @state: intel atomic state + * @crtc: intel crtc + * @enable: enable/disable + * + * This is targeted for underrun on idle PSR HW bug (Wa_16025596647) to apply + * remove the workaround when pipe is getting enabled/disabled + */ +void intel_psr_notify_pipe_change(struct intel_atomic_state *state, + struct intel_crtc *crtc, bool enable) +{ + struct intel_display *display = to_intel_display(state); + struct intel_encoder *encoder; + + if (!intel_display_wa(display, INTEL_DISPLAY_WA_16025596647)) + return; + + for_each_intel_encoder_with_psr(display->drm, encoder) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + u8 active_non_psr_pipes; + + mutex_lock(&intel_dp->psr.lock); + + if (!intel_dp->psr.enabled || intel_dp->psr.panel_replay_enabled) + goto unlock; + + active_non_psr_pipes = intel_dp->psr.active_non_psr_pipes; + + if (enable) + active_non_psr_pipes |= BIT(crtc->pipe); + else + active_non_psr_pipes &= ~BIT(crtc->pipe); + + if (active_non_psr_pipes == intel_dp->psr.active_non_psr_pipes) + goto unlock; + + if ((enable && intel_dp->psr.active_non_psr_pipes) || + (!enable && !intel_dp->psr.active_non_psr_pipes) || + !intel_dp->psr.pkg_c_latency_used) { + intel_dp->psr.active_non_psr_pipes = active_non_psr_pipes; + goto unlock; + } + + intel_dp->psr.active_non_psr_pipes = active_non_psr_pipes; + + intel_psr_apply_underrun_on_idle_wa_locked(intel_dp); +unlock: + mutex_unlock(&intel_dp->psr.lock); + } +} + +/** + * intel_psr_notify_vblank_enable_disable - Notify PSR about enable/disable of vblank + * @display: intel display struct + * @enable: enable/disable + * + * This is targeted for underrun on idle PSR HW bug (Wa_16025596647) to apply + * remove the workaround when vblank is getting enabled/disabled + */ +void intel_psr_notify_vblank_enable_disable(struct intel_display *display, + bool enable) +{ + struct intel_encoder *encoder; + + 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 (intel_dp->psr.panel_replay_enabled) { + mutex_unlock(&intel_dp->psr.lock); + break; + } + + if (intel_dp->psr.enabled && intel_dp->psr.pkg_c_latency_used) + intel_psr_apply_underrun_on_idle_wa_locked(intel_dp); + + mutex_unlock(&intel_dp->psr.lock); + return; + } + + /* + * 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. + */ + intel_display_power_set_target_dc_state(display, enable ? DC_STATE_DISABLE : + DC_STATE_EN_UPTO_DC6); +} + static void psr_source_status(struct intel_dp *intel_dp, struct seq_file *m) { @@ -3614,8 +4187,8 @@ psr_source_status(struct intel_dp *intel_dp, struct seq_file *m) const char *status = "unknown"; u32 val, status_val; - if (intel_dp_is_edp(intel_dp) && (intel_dp->psr.sel_update_enabled || - intel_dp->psr.panel_replay_enabled)) { + if ((intel_dp_is_edp(intel_dp) || DISPLAY_VER(display) >= 30) && + (intel_dp->psr.sel_update_enabled || intel_dp->psr.panel_replay_enabled)) { static const char * const live_status[] = { "IDLE", "CAPTURE", @@ -3655,22 +4228,23 @@ psr_source_status(struct intel_dp *intel_dp, struct seq_file *m) seq_printf(m, "Source PSR/PanelReplay status: %s [0x%08x]\n", status, val); } -static void intel_psr_sink_capability(struct intel_dp *intel_dp, +static void intel_psr_sink_capability(struct intel_connector *connector, struct seq_file *m) { - struct intel_psr *psr = &intel_dp->psr; - seq_printf(m, "Sink support: PSR = %s", - str_yes_no(psr->sink_support)); + str_yes_no(connector->dp.psr_caps.support)); - if (psr->sink_support) - seq_printf(m, " [0x%02x]", intel_dp->psr_dpcd[0]); - if (intel_dp->psr_dpcd[0] == DP_PSR2_WITH_Y_COORD_ET_SUPPORTED) + if (connector->dp.psr_caps.support) + seq_printf(m, " [0x%02x]", connector->dp.psr_caps.dpcd[0]); + if (connector->dp.psr_caps.dpcd[0] == DP_PSR2_WITH_Y_COORD_ET_SUPPORTED) seq_printf(m, " (Early Transport)"); - seq_printf(m, ", Panel Replay = %s", str_yes_no(psr->sink_panel_replay_support)); + seq_printf(m, ", Panel Replay = %s", str_yes_no(connector->dp.panel_replay_caps.support)); seq_printf(m, ", Panel Replay Selective Update = %s", - str_yes_no(psr->sink_panel_replay_su_support)); - if (intel_dp->pr_dpcd & DP_PANEL_REPLAY_EARLY_TRANSPORT_SUPPORT) + str_yes_no(connector->dp.panel_replay_caps.su_support)); + seq_printf(m, ", Panel Replay DSC support = %s", + panel_replay_dsc_support_str(connector->dp.panel_replay_caps.dsc_support)); + if (connector->dp.panel_replay_caps.dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_SUPPORT)] & + DP_PANEL_REPLAY_EARLY_TRANSPORT_SUPPORT) seq_printf(m, " (Early Transport)"); seq_printf(m, "\n"); } @@ -3703,24 +4277,26 @@ static void intel_psr_print_mode(struct intel_dp *intel_dp, region_et = ""; seq_printf(m, "PSR mode: %s%s%s\n", mode, status, region_et); + if (psr->no_psr_reason) + seq_printf(m, " %s\n", psr->no_psr_reason); } -static int intel_psr_status(struct seq_file *m, struct intel_dp *intel_dp) +static int intel_psr_status(struct seq_file *m, struct intel_dp *intel_dp, + struct intel_connector *connector) { struct intel_display *display = to_intel_display(intel_dp); - struct drm_i915_private *dev_priv = to_i915(display->drm); enum transcoder cpu_transcoder = intel_dp->psr.transcoder; struct intel_psr *psr = &intel_dp->psr; - intel_wakeref_t wakeref; + struct ref_tracker *wakeref; bool enabled; u32 val, psr2_ctl; - intel_psr_sink_capability(intel_dp, m); + intel_psr_sink_capability(connector, m); - if (!(psr->sink_support || psr->sink_panel_replay_support)) + if (!(connector->dp.psr_caps.support || connector->dp.panel_replay_caps.support)) return 0; - wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm); + wakeref = intel_display_rpm_get(display); mutex_lock(&psr->lock); intel_psr_print_mode(intel_dp, m); @@ -3776,24 +4352,30 @@ static int intel_psr_status(struct seq_file *m, struct intel_dp *intel_dp) int frame; /* - * Reading all 3 registers before hand to minimize crossing a - * frame boundary between register reads + * PSR2_SU_STATUS register has been tied-off since DG2/ADL-P + * (it returns zeros only) and it has been removed on Xe2_LPD. */ - for (frame = 0; frame < PSR2_SU_STATUS_FRAMES; frame += 3) { - val = intel_de_read(display, - PSR2_SU_STATUS(display, cpu_transcoder, frame)); - su_frames_val[frame / 3] = val; - } + if (DISPLAY_VER(display) < 13) { + /* + * Reading all 3 registers before hand to minimize crossing a + * frame boundary between register reads + */ + for (frame = 0; frame < PSR2_SU_STATUS_FRAMES; frame += 3) { + val = intel_de_read(display, + PSR2_SU_STATUS(display, cpu_transcoder, frame)); + su_frames_val[frame / 3] = val; + } - seq_puts(m, "Frame:\tPSR2 SU blocks:\n"); + seq_puts(m, "Frame:\tPSR2 SU blocks:\n"); - for (frame = 0; frame < PSR2_SU_STATUS_FRAMES; frame++) { - u32 su_blocks; + for (frame = 0; frame < PSR2_SU_STATUS_FRAMES; frame++) { + u32 su_blocks; - su_blocks = su_frames_val[frame / 3] & - PSR2_SU_STATUS_MASK(frame); - su_blocks = su_blocks >> PSR2_SU_STATUS_SHIFT(frame); - seq_printf(m, "%d\t%d\n", frame, su_blocks); + su_blocks = su_frames_val[frame / 3] & + PSR2_SU_STATUS_MASK(frame); + su_blocks = su_blocks >> PSR2_SU_STATUS_SHIFT(frame); + seq_printf(m, "%d\t%d\n", frame, su_blocks); + } } seq_printf(m, "PSR2 selective fetch: %s\n", @@ -3802,7 +4384,7 @@ static int intel_psr_status(struct seq_file *m, struct intel_dp *intel_dp) unlock: mutex_unlock(&psr->lock); - intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref); + intel_display_rpm_put(display, wakeref); return 0; } @@ -3825,7 +4407,7 @@ static int i915_edp_psr_status_show(struct seq_file *m, void *data) if (!intel_dp) return -ENODEV; - return intel_psr_status(m, intel_dp); + return intel_psr_status(m, intel_dp, intel_dp->attached_connector); } DEFINE_SHOW_ATTRIBUTE(i915_edp_psr_status); @@ -3833,9 +4415,7 @@ static int i915_edp_psr_debug_set(void *data, u64 val) { struct intel_display *display = data; - struct drm_i915_private *dev_priv = to_i915(display->drm); struct intel_encoder *encoder; - intel_wakeref_t wakeref; int ret = -ENODEV; if (!HAS_PSR(display)) @@ -3846,12 +4426,9 @@ i915_edp_psr_debug_set(void *data, u64 val) drm_dbg_kms(display->drm, "Setting PSR debug to %llx\n", val); - wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm); - // TODO: split to each transcoder's PSR debug state - ret = intel_psr_debug_set(intel_dp, val); - - intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref); + with_intel_display_rpm(display) + ret = intel_psr_debug_set(intel_dp, val); } return ret; @@ -3883,12 +4460,12 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_edp_psr_debug_fops, void intel_psr_debugfs_register(struct intel_display *display) { - struct drm_minor *minor = display->drm->primary; + struct dentry *debugfs_root = display->drm->debugfs_root; - debugfs_create_file("i915_edp_psr_debug", 0644, minor->debugfs_root, + debugfs_create_file("i915_edp_psr_debug", 0644, debugfs_root, display, &i915_edp_psr_debug_fops); - debugfs_create_file("i915_edp_psr_status", 0444, minor->debugfs_root, + debugfs_create_file("i915_edp_psr_status", 0444, debugfs_root, display, &i915_edp_psr_status_fops); } @@ -3964,7 +4541,7 @@ static int i915_psr_status_show(struct seq_file *m, void *data) struct intel_connector *connector = m->private; struct intel_dp *intel_dp = intel_attached_dp(connector); - return intel_psr_status(m, intel_dp); + return intel_psr_status(m, intel_dp, connector); } DEFINE_SHOW_ATTRIBUTE(i915_psr_status); @@ -3984,3 +4561,107 @@ void intel_psr_connector_debugfs_add(struct intel_connector *connector) debugfs_create_file("i915_psr_status", 0444, root, connector, &i915_psr_status_fops); } + +bool intel_psr_needs_alpm(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state) +{ + /* + * eDP Panel Replay uses always ALPM + * PSR2 uses ALPM but PSR1 doesn't + */ + return intel_dp_is_edp(intel_dp) && (crtc_state->has_sel_update || + crtc_state->has_panel_replay); +} + +bool intel_psr_needs_alpm_aux_less(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state) +{ + return intel_dp_is_edp(intel_dp) && crtc_state->has_panel_replay; +} + +void intel_psr_compute_config_late(struct intel_dp *intel_dp, + struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(intel_dp); + int vblank = intel_crtc_vblank_length(crtc_state); + int wake_lines; + + if (intel_psr_needs_alpm_aux_less(intel_dp, crtc_state)) + wake_lines = crtc_state->alpm_state.aux_less_wake_lines; + else if (intel_psr_needs_alpm(intel_dp, 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; + else + wake_lines = 0; + + /* + * Disable the PSR features if wake lines exceed the available vblank. + * Though SCL is computed based on these PSR features, it is not reset + * even if the PSR features are disabled to avoid changing vblank start + * at this stage. + */ + if (wake_lines && !_wake_lines_fit_into_vblank(crtc_state, vblank, wake_lines)) { + drm_dbg_kms(display->drm, + "Adjusting PSR/PR mode: vblank too short for wake lines = %d\n", + wake_lines); + + if (crtc_state->has_panel_replay) { + crtc_state->has_panel_replay = false; + /* + * #TODO : Add fall back to PSR/PSR2 + * Since panel replay cannot be supported, we can fall back to PSR/PSR2. + * This will require calling compute_config for psr and psr2 with check for + * actual guardband instead of vblank_length. + */ + crtc_state->has_psr = false; + } + + crtc_state->has_sel_update = false; + crtc_state->enable_psr2_su_region_et = false; + crtc_state->enable_psr2_sel_fetch = false; + } + + /* Wa_18037818876 */ + if (intel_psr_needs_wa_18037818876(intel_dp, crtc_state)) { + crtc_state->has_psr = false; + drm_dbg_kms(display->drm, + "PSR disabled to workaround PSR FSM hang issue\n"); + } + + intel_psr_set_non_psr_pipes(intel_dp, crtc_state); +} + +int intel_psr_min_guardband(struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + int psr_min_guardband; + int wake_lines; + + if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP)) + return 0; + + if (crtc_state->has_panel_replay) + wake_lines = crtc_state->alpm_state.aux_less_wake_lines; + else if (crtc_state->has_sel_update) + 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; + else + return 0; + + psr_min_guardband = wake_lines + crtc_state->set_context_latency; + + if (crtc_state->req_psr2_sdp_prior_scanline) + psr_min_guardband++; + + return psr_min_guardband; +} + +bool intel_psr_use_trans_push(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + + return HAS_PSR_TRANS_PUSH_FRAME_CHANGE(display) && crtc_state->has_psr; +} |
