drm/amd/display: Fix seamless mode switch not triggering for HDR to SDR transition

[Why]
The seamless mode switch was not getting triggered during HDR to SDR
transitions, and no DPCD write was observed. Root cause analysis
revealed that incorrect panel capabilities were being reported for PSR
SU panels. Due to the wrong capabilities, the OS was not invoking the
seamless mode switch API, resulting in no DPCD communication and also
gated eDP teardown across the seamless mode switch hold.

[How]
Fixed by setting the correct power panel capabilities for PSR SU panels.
This ensures the OS receives accurate panel capability information and
triggers the seamless mode switch API as expected, restoring proper
DPCD writes during HDR to SDR transitions.
The DC commit sequence was tearing the eDP down anyway -- backlight off,
ABM disable, DPMS off, PSR/Replay enable state cleared, PHY TX off,
OTG/OPTC off; all these actions are blocked now with the
skip_implict_edp_power_control

Reviewed-by: Aric Cyr <aric.cyr@amd.com>
Signed-off-by: Karthi Kandasamy <karthi.kandasamy@amd.com>
Signed-off-by: Roman Li <roman.li@amd.com>
Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Karthi Kandasamy 2026-05-28 12:49:44 +02:00 committed by Alex Deucher
parent 8b87300e1b
commit 73efd24e21
7 changed files with 60 additions and 16 deletions

View File

@ -1994,6 +1994,7 @@ struct dc_scratch_space {
// BW ALLOCATON USB4 ONLY
struct dc_dpia_bw_alloc dpia_bw_alloc_config;
bool skip_implict_edp_power_control;
bool forced_psr_active;
enum backlight_control_type backlight_control_type;
};

View File

@ -1286,6 +1286,8 @@ void dce110_blank_stream(struct pipe_ctx *pipe_ctx)
return;
if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) {
if (link->forced_psr_active)
return;
if (!link->skip_implict_edp_power_control && hws)
hws->funcs.edp_backlight_control(link, false);
link->dc->hwss.set_abm_immediate_disable(pipe_ctx);
@ -1811,7 +1813,9 @@ enum dc_status dce110_apply_single_controller_ctx_to_hw(
dc->link_srv->set_dsc_enable(pipe_ctx, true);
}
if (!stream->dpms_off)
if (!stream->dpms_off &&
!(link->connector_signal == SIGNAL_TYPE_EDP &&
link->forced_psr_active))
dc->link_srv->set_dpms_on(context, pipe_ctx);
/* DCN3.1 FPGA Workaround
@ -1830,7 +1834,9 @@ enum dc_status dce110_apply_single_controller_ctx_to_hw(
* is constructed with the same sink). Make sure not to override
* and link programming on the main.
*/
if (dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM) {
if (dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM &&
!(link->connector_signal == SIGNAL_TYPE_EDP &&
link->forced_psr_active)) {
pipe_ctx->stream->link->psr_settings.psr_feature_enabled = false;
pipe_ctx->stream->link->replay_settings.replay_feature_enabled = false;
}

View File

@ -537,10 +537,12 @@ static void dcn31_reset_back_end_for_pipe(
}
ASSERT(!pipe_ctx->top_pipe);
dc->hwss.set_abm_immediate_disable(pipe_ctx);
link = pipe_ctx->stream->link;
if (!(link->connector_signal == SIGNAL_TYPE_EDP &&
link->forced_psr_active))
dc->hwss.set_abm_immediate_disable(pipe_ctx);
if (dc->hwseq)
dc->hwseq->wa_state.skip_blank_stream = false;
@ -555,9 +557,11 @@ static void dcn31_reset_back_end_for_pipe(
pipe_ctx->stream_res.tg,
OPTC_DSC_DISABLED, 0, 0);
pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg);
pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false);
if (!(link->connector_signal == SIGNAL_TYPE_EDP &&
link->forced_psr_active)) {
pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg);
pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false);
}
if (pipe_ctx->stream_res.tg->funcs->set_odm_bypass)
pipe_ctx->stream_res.tg->funcs->set_odm_bypass(
pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing);
@ -586,7 +590,12 @@ static void dcn31_reset_back_end_for_pipe(
* screen only, the dpms_off would be true but
* VBIOS lit up eDP, so check link status too.
*/
if (!pipe_ctx->stream->dpms_off || link->link_status.link_active)
if (link->connector_signal == SIGNAL_TYPE_EDP &&
link->forced_psr_active) {
/* forced psr is active for seamless switch; skip dpms-off. */
if (pipe_ctx->stream_res.audio)
dc->hwss.disable_audio_stream(pipe_ctx);
} else if (!pipe_ctx->stream->dpms_off || link->link_status.link_active)
dc->link_srv->set_dpms_off(pipe_ctx);
else if (pipe_ctx->stream_res.audio)
dc->hwss.disable_audio_stream(pipe_ctx);

View File

@ -1819,8 +1819,11 @@ void dcn35_disable_link_output(struct dc_link *link,
disable_link_output_symclk_on_tx_off(link, DP_UNKNOWN_ENCODING);
link->phy_state.symclk_state = SYMCLK_ON_TX_OFF;
} else {
link_hwss->disable_link_output(link, link_res, signal);
link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF;
if (!(signal == SIGNAL_TYPE_EDP &&
link->forced_psr_active)) {
link_hwss->disable_link_output(link, link_res, signal);
link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF;
}
}
/*
* Add the logic to extract BOTH power up and power down sequences

View File

@ -1113,8 +1113,11 @@ void dcn401_disable_link_output(struct dc_link *link,
disable_link_output_symclk_on_tx_off(link, DP_UNKNOWN_ENCODING);
link->phy_state.symclk_state = SYMCLK_ON_TX_OFF;
} else {
link_hwss->disable_link_output(link, link_res, signal);
link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF;
if (!(signal == SIGNAL_TYPE_EDP &&
link->forced_psr_active)) {
link_hwss->disable_link_output(link, link_res, signal);
link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF;
}
}
if (signal == SIGNAL_TYPE_EDP &&
@ -2162,7 +2165,12 @@ void dcn401_reset_back_end_for_pipe(
* screen only, the dpms_off would be true but
* VBIOS lit up eDP, so check link status too.
*/
if (!pipe_ctx->stream->dpms_off || link->link_status.link_active)
if (link->connector_signal == SIGNAL_TYPE_EDP &&
link->forced_psr_active) {
/* Forced psr is active for seamless switch; skip dpms-off. */
if (pipe_ctx->stream_res.audio)
dc->hwss.disable_audio_stream(pipe_ctx);
} else if (!pipe_ctx->stream->dpms_off || link->link_status.link_active)
dc->link_srv->set_dpms_off(pipe_ctx);
else if (pipe_ctx->stream_res.audio)
dc->hwss.disable_audio_stream(pipe_ctx);
@ -2187,12 +2195,15 @@ void dcn401_reset_back_end_for_pipe(
* parent pipe.
*/
if (pipe_ctx->top_pipe == NULL) {
if (!(link->connector_signal == SIGNAL_TYPE_EDP &&
link->forced_psr_active)) {
dc->hwss.set_abm_immediate_disable(pipe_ctx);
dc->hwss.set_abm_immediate_disable(pipe_ctx);
pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg);
pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg);
pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false);
pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false);
}
if (pipe_ctx->stream_res.tg->funcs->set_odm_bypass)
pipe_ctx->stream_res.tg->funcs->set_odm_bypass(
pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing);

View File

@ -58,6 +58,13 @@ bool mod_power_psr_notify_mode_change(struct mod_power *mod_power,
// stream_index is passed as validated parameter
active_psr_events = core_power->map[stream_index].psr_events;
/* forced psr is active for seamless switch.
* Re-running edp_setup_psr would disturb the freeze,
* so skip the PSR re-setup until forced psr releases the override.
*/
if (active_psr_events & psr_event_os_override_hold)
return false;
/* Calculate PSR configurations */
mod_power_calc_psr_configs(&psr_config, link, stream);

View File

@ -805,6 +805,13 @@ void mod_power_replay_notify_mode_change(struct mod_power *mod_power,
core_power = MOD_POWER_TO_CORE(mod_power);
active_replay_events = core_power->map[stream_index].replay_events;
/* forced psr is active for seamless switch.
* Re-running edp_setup_psr would disturb the freeze,
* so skip the PSR re-setup until forced psr releases the override.
*/
if (active_replay_events & replay_event_os_override_hold)
return;
link->replay_settings.replay_smu_opt_enable =
(link->replay_settings.config.replay_smu_opt_supported &&
mod_power_only_edp(dc->current_state, stream));