The atomic helpers skip bridge disable when an active CRTC leaves self refresh. The eDP enable callback also takes a fast path that only exits PSR, so a concurrent modeset can leave the retained stream using its old configuration, including the output depth.
Exit PSR and tear down the retained stream in pre-enable when leaving self refresh with mode_changed or connectors_changed set. Install the new state and run the normal stream enable sequence afterwards. Keep the fast path for ordinary self-refresh exits: active_changed alone does not require rebuilding the stream. Assisted-by: LLM Signed-off-by: Xilin Wu <[email protected]> --- drivers/gpu/drm/msm/dp/dp_drm.c | 24 +++++++++++++++++++++++- drivers/gpu/drm/msm/dp/dp_utils.c | 7 +++++++ drivers/gpu/drm/msm/dp/dp_utils.h | 4 ++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index c1e4147bfef1..7332fbfe1644 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -14,6 +14,7 @@ #include "msm_kms.h" #include "dp_audio.h" #include "dp_drm.h" +#include "dp_utils.h" /** * msm_dp_bridge_get_modes - callback to add drm modes via drm_mode_probed_add() @@ -54,6 +55,26 @@ static void msm_dp_bridge_atomic_pre_enable(struct drm_bridge *drm_bridge, { struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge); struct msm_dp *dp = dp_bridge->msm_dp_display; + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + + crtc = drm_atomic_get_new_crtc_for_encoder(state, drm_bridge->encoder); + if (!crtc) + return; + + old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + /* + * The helpers skip bridge disable when waking an active CRTC from + * self refresh, even when its color configuration requires a modeset. + * Tear down the retained stream before installing the new state. + */ + if (dp->is_edp && dp->power_on && old_crtc_state->self_refresh_active && + new_crtc_state->active && msm_dp_utils_psr_needs_modeset(new_crtc_state)) { + msm_dp_display_set_psr(dp, false); + msm_dp_display_atomic_disable(dp); + msm_dp_display_atomic_post_disable(dp); + } msm_dp_display_atomic_pre_enable(dp, state); } @@ -157,7 +178,8 @@ static void msm_edp_bridge_atomic_enable(struct drm_bridge *drm_bridge, old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); - if (old_crtc_state && old_crtc_state->self_refresh_active) { + if (dp->power_on && old_crtc_state && old_crtc_state->self_refresh_active && + !msm_dp_utils_psr_needs_modeset(drm_atomic_get_new_crtc_state(state, crtc))) { msm_dp_display_set_psr(dp, false); return; } diff --git a/drivers/gpu/drm/msm/dp/dp_utils.c b/drivers/gpu/drm/msm/dp/dp_utils.c index 226b370ce8fe..72bae48a1fe0 100644 --- a/drivers/gpu/drm/msm/dp/dp_utils.c +++ b/drivers/gpu/drm/msm/dp/dp_utils.c @@ -4,6 +4,7 @@ */ #include <linux/types.h> +#include <drm/drm_crtc.h> #include "dp_utils.h" @@ -87,3 +88,9 @@ void msm_dp_utils_pack_sdp_header(const struct dp_sdp_header *sdp_header, FIELD_PREP(HEADER_3_MASK, sdp_header->HB3) | FIELD_PREP(PARITY_3_MASK, msm_dp_utils_calculate_parity(sdp_header->HB3)); } + +bool msm_dp_utils_psr_needs_modeset(const struct drm_crtc_state *state) +{ + /* active_changed alone also describes a normal self-refresh exit. */ + return state->mode_changed || state->connectors_changed; +} diff --git a/drivers/gpu/drm/msm/dp/dp_utils.h b/drivers/gpu/drm/msm/dp/dp_utils.h index 2fdb89efc57c..5d86c978dffd 100644 --- a/drivers/gpu/drm/msm/dp/dp_utils.h +++ b/drivers/gpu/drm/msm/dp/dp_utils.h @@ -10,6 +10,8 @@ #include <linux/bits.h> #include <drm/display/drm_dp_helper.h> +struct drm_crtc_state; + #define HEADER_BYTE_0_BIT 0 #define PARITY_BYTE_0_BIT 8 #define HEADER_BYTE_1_BIT 16 @@ -34,4 +36,6 @@ u8 msm_dp_utils_calculate_parity(u32 data); void msm_dp_utils_pack_sdp_header(const struct dp_sdp_header *sdp_header, u32 header_buff[2]); +bool msm_dp_utils_psr_needs_modeset(const struct drm_crtc_state *state); + #endif /* _DP_UTILS_H_ */ -- 2.55.0
