On Wed, Dec 03, 2025 at 12:31:28PM +0200, Jouni Högander wrote:
> 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.
>
> 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 <[email protected]>
> Reviewed-by: Imre Deak <[email protected]>
> ---
> drivers/gpu/drm/i915/display/intel_dp.c | 4 +-
> drivers/gpu/drm/i915/display/intel_psr.c | 147 +++++++++++------------
> drivers/gpu/drm/i915/display/intel_psr.h | 2 +-
> 3 files changed, 72 insertions(+), 81 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/display/intel_dp.c
> b/drivers/gpu/drm/i915/display/intel_dp.c
> index 7df0e5e13688d..dcceb0ae2a56d 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 15ef3b6caad6e..5f8df67f9993e 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;
This needs now a cpu_to_le16() or a separate variable.
> 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)
> + if (r != sizeof(w) || w == 0)
> w = 4;
This also needs a conversion/separate variable as above.
The patch otherwise looks ok to me.
>
> - 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 024ee4c309852..b41dc4d44ff29 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.43.0
>