]> git.hungrycats.org Git - linux/commitdiff
drm/amd/display: Fix DMSS not triggering for HDR to SDR transition
authorKarthi Kandasamy <karthi.kandasamy@amd.com>
Thu, 28 May 2026 10:49:44 +0000 (12:49 +0200)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 15 Jul 2026 13:15:40 +0000 (09:15 -0400)
Why:
DMSS feature 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 DMSS API,
resulting in no DPCD communication and also gate eDP teardown across
DMSS 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 DMSS 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 feature_enabled 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: George Zhang <george.zhang@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c
drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c
drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c
drivers/gpu/drm/amd/display/modules/power/power_psr.c
drivers/gpu/drm/amd/display/modules/power/power_replay.c

index c9691974bf7275f55e826998ee082f507a92019d..cce4f30655758413b4890b9258191d231e38760f 100644 (file)
@@ -1287,7 +1287,9 @@ void dce110_blank_stream(struct pipe_ctx *pipe_ctx)
                return;
 
        if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) {
-               if (!link->skip_implict_edp_power_control && hws)
+               if (link->skip_implict_edp_power_control)
+                       return;
+               if (hws)
                        hws->funcs.edp_backlight_control(link, false);
                link->dc->hwss.set_abm_immediate_disable(pipe_ctx);
        }
@@ -1793,7 +1795,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->skip_implict_edp_power_control))
                dc->link_srv->set_dpms_on(context, pipe_ctx);
 
        /* DCN3.1 FPGA Workaround
@@ -1812,7 +1816,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->skip_implict_edp_power_control)) {
                pipe_ctx->stream->link->psr_settings.psr_feature_enabled = false;
                pipe_ctx->stream->link->replay_settings.replay_feature_enabled = false;
        }
index b6f3c0480ab6c0f5cd79e83d07547045a54fd5ec..6f1ad651ed2ca4d39d407e07b4e3b84254a2a567 100644 (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->skip_implict_edp_power_control))
+               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->skip_implict_edp_power_control)) {
+               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->skip_implict_edp_power_control) {
+               /* DMSS is holding the panel across the commit; 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);
index f14c39a643da969c2ee329fda822720ea53b025d..4ed7480d1efa6103af94dccc5e2a3147ebbd5c86 100644 (file)
@@ -1818,8 +1818,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->skip_implict_edp_power_control)) {
+                       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
index 0336d118e77e3eab9cd0b7ea88c437c23f8e466e..d8d3208fc1af92a7b5c46139c30024fc93e712a9 100644 (file)
@@ -1101,8 +1101,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->skip_implict_edp_power_control)) {
+                       link_hwss->disable_link_output(link, link_res, signal);
+                       link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF;
+               }
        }
 
        if (signal == SIGNAL_TYPE_EDP &&
@@ -2150,7 +2153,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->skip_implict_edp_power_control) {
+               /* DMSS is holding the panel across the commit; 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);
@@ -2175,12 +2183,15 @@ void dcn401_reset_back_end_for_pipe(
         * parent pipe.
         */
        if (pipe_ctx->top_pipe == NULL) {
+               if (!(link->connector_signal == SIGNAL_TYPE_EDP &&
+                     link->skip_implict_edp_power_control)) {
 
-               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);
index 5ecb570c204e17e48959212251e2c813f5c2f9a4..0ad4c4924696514763c24159608fc4d51604dcf8 100644 (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;
 
+       /* DMSS holds the panel in a forced PSR freeze (e.g. during HDR/SDR toggle).
+        * Re-running edp_setup_psr would reprogram DPCD 0x170 and disturb the freeze,
+        * so skip the PSR re-setup until DMSS 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);
 
index e782501442c42b66b7ea21412403dc8fa8e5f64a..1ad2ee01d5600579b490508938a53f9f6a9775d6 100644 (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;
 
+       /* DMSS holds the panel in a forced freeze (e.g. during HDR/SDR toggle).
+        * Re-running dp_setup_replay would reprogram DPCD 0x37B and disturb the
+        * freeze, so skip the replay re-setup until DMSS 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));