From 1cc854647450199e20cdbe0fbc3be1f5253d7693 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jouni=20H=C3=B6gander?= Date: Thu, 4 Dec 2025 12:47:27 +0200 Subject: [PATCH] drm/i915/psr: Use SU granularity information available in intel_connector MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Currently we are storing only one set of granularity information for panels supporting both PSR and Panel Replay. As panel is informing own granularities for PSR and Panel Replay they could be different. Let's use own granularities for PSR and Panel Replay instead of having only one set for both. This is done by having intel_connector::psr_caps and panel_replay_caps both containing granularity information. Also remove complexity of sharing granularity read between PSR and Panel Replay. v3: - use cpu_to_le16 for default value v2: - use __le16 for two byte values in dpcd - use sizeof instead of hardcoded size in reading dpcd - drop unnecessarily passing intel_dp pointer Signed-off-by: Jouni Högander Reviewed-by: Imre Deak Link: https://patch.msgid.link/20251204104733.1106145-3-jouni.hogander@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 4 +- drivers/gpu/drm/i915/display/intel_psr.c | 151 +++++++++++------------ drivers/gpu/drm/i915/display/intel_psr.h | 2 +- 3 files changed, 74 insertions(+), 83 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 7df0e5e13688..dcceb0ae2a56 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4562,7 +4562,7 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector * This has to be called after intel_dp->edp_dpcd is filled, PSR checks * for SET_POWER_CAPABLE bit in intel_dp->edp_dpcd[1] */ - intel_psr_init_dpcd(intel_dp); + intel_psr_init_dpcd(intel_dp, connector); intel_edp_set_sink_rates(intel_dp); intel_dp_set_max_sink_lane_count(intel_dp); @@ -6074,7 +6074,7 @@ intel_dp_detect(struct drm_connector *_connector, connector->base.epoch_counter++; if (!intel_dp_is_edp(intel_dp)) - intel_psr_init_dpcd(intel_dp); + intel_psr_init_dpcd(intel_dp, connector); intel_dp_detect_dsc_caps(intel_dp, connector); diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 15ef3b6caad6..417a6cd2fca9 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -494,82 +494,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) { - if (drm_dp_dpcd_read_byte(&intel_dp->aux, - DP_PANEL_REPLAY_CAP_CAPABILITY, - &su_capability) < 0) - return 0; - } 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_REPLAY_CAP_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_REPLAY_CAP_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 (!(intel_dp->psr_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"); @@ -579,8 +534,8 @@ 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 @@ -621,7 +576,32 @@ static const char *panel_replay_dsc_support_str(enum intel_panel_replay_dsc_supp }; } -static void _panel_replay_init_dpcd(struct intel_dp *intel_dp) +static void _panel_replay_compute_su_granularity(struct intel_dp *intel_dp, + struct intel_connector *connector) +{ + u16 w; + u8 y; + + if (!(intel_dp->pr_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 *)&intel_dp->pr_dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_X_GRANULARITY)]) ? : 4; + y = intel_dp->pr_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, struct intel_connector *connector) { struct intel_display *display = to_intel_display(intel_dp); int ret; @@ -657,9 +637,12 @@ static void _panel_replay_init_dpcd(struct intel_dp *intel_dp) intel_dp->psr.sink_panel_replay_support = true; if (intel_dp->pr_dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_SUPPORT)] & - DP_PANEL_REPLAY_SU_SUPPORT) + DP_PANEL_REPLAY_SU_SUPPORT) { intel_dp->psr.sink_panel_replay_su_support = true; + _panel_replay_compute_su_granularity(intel_dp, connector); + } + intel_dp->psr.sink_panel_replay_dsc_support = compute_pr_dsc_support(intel_dp); drm_dbg_kms(display->drm, @@ -669,7 +652,7 @@ static void _panel_replay_init_dpcd(struct intel_dp *intel_dp) panel_replay_dsc_support_str(intel_dp->psr.sink_panel_replay_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; @@ -722,17 +705,16 @@ static void _psr_init_dpcd(struct intel_dp *intel_dp) drm_dbg_kms(display->drm, "PSR2 %ssupported\n", intel_dp->psr.sink_psr2_support ? "" : "not "); } + + if (intel_dp->psr.sink_psr2_support) + _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) { - _psr_init_dpcd(intel_dp); - - _panel_replay_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) @@ -1304,25 +1286,32 @@ static bool intel_psr2_sel_fetch_config_valid(struct intel_dp *intel_dp, 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); 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 = crtc_state->has_panel_replay ? + connector->dp.panel_replay_caps.su_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. @@ -1330,11 +1319,11 @@ static bool psr2_granularity_check(struct intel_dp *intel_dp, * to match sink requirement if multiple of 4. */ if (display->platform.alderlake_p || DISPLAY_VER(display) >= 14) - y_granularity = intel_dp->psr.su_y_granularity; - else if (intel_dp->psr.su_y_granularity <= 2) + 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; @@ -1621,9 +1610,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) && @@ -1671,7 +1662,7 @@ 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; @@ -1866,7 +1857,7 @@ void intel_psr_compute_config(struct intel_dp *intel_dp, if (!crtc_state->has_psr) return; - crtc_state->has_sel_update = intel_sel_update_config_valid(intel_dp, crtc_state); + crtc_state->has_sel_update = intel_sel_update_config_valid(crtc_state, conn_state); } void intel_psr_get_config(struct intel_encoder *encoder, diff --git a/drivers/gpu/drm/i915/display/intel_psr.h b/drivers/gpu/drm/i915/display/intel_psr.h index 024ee4c30985..b41dc4d44ff2 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.h +++ b/drivers/gpu/drm/i915/display/intel_psr.h @@ -28,7 +28,7 @@ struct intel_plane_state; bool intel_encoder_can_psr(struct intel_encoder *encoder); bool intel_psr_needs_aux_io_power(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); -void intel_psr_init_dpcd(struct intel_dp *intel_dp); +void intel_psr_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector); void intel_psr_panel_replay_enable_sink(struct intel_dp *intel_dp); void intel_psr_pre_plane_update(struct intel_atomic_state *state, struct intel_crtc *crtc); -- 2.47.3