drm/amd/display: Split DPMS ON into parts

[Why]
Ongoing optimization efforts require splitting DPMS ON around
enable_link, in order to enable running multiple sequences
in parallel.

[How]
* Split link_set_dpms_on across enable_link
* Add return value indicating whether the programming sequence
succeeded and/or run to the end.

Reviewed-by: Alvin Lee <alvin.lee2@amd.com>
Signed-off-by: Dominik Kaszewski <dominik.kaszewski@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Dominik Kaszewski 2026-06-19 15:21:35 +02:00 committed by Alex Deucher
parent cfc867ccfa
commit 843049bac7
4 changed files with 156 additions and 46 deletions

View File

@ -62,6 +62,16 @@ enum dc_status {
DC_FAIL_DP_LINK_BANDWIDTH = 28,
DC_FAIL_HW_CURSOR_SUPPORT = 29,
DC_FAIL_DP_TUNNEL_BW_VALIDATE = 30,
/// Link protocol handshake and DPMS hardware programming successful.
DC_DPMS_SUCCESS = DC_OK,
/// Handshake skipped by optimized path, programming successfully completed.
DC_DPMS_SKIPPED_HANDSHAKE = 31,
/// Handshake failed, programming successful, DCN in consistent state.
DC_DPMS_FAILED_HANDSHAKE = 32,
/// Handshake failed, programming aborted, DCN may be in inconsistent state.
DC_DPMS_FAILED_INCOMPLETE = 33,
DC_ERROR_UNEXPECTED = -1
};

View File

@ -155,8 +155,8 @@ struct link_service {
/*************************** DPMS *************************************/
void (*set_dpms_on)(struct dc_state *state, struct pipe_ctx *pipe_ctx);
void (*set_dpms_off)(struct pipe_ctx *pipe_ctx);
enum dc_status (*set_dpms_on)(struct dc_state *state, struct pipe_ctx *pipe_ctx);
enum dc_status (*set_dpms_off)(struct pipe_ctx *pipe_ctx);
void (*resume)(struct dc_link *link);
void (*blank_all_dp_displays)(struct dc *dc);
void (*blank_all_edp_displays)(struct dc *dc);

View File

@ -528,19 +528,27 @@ static bool write_i2c_redriver_setting(
return success;
}
static struct link_encoder *get_link_encoder(struct pipe_ctx *pipe_ctx)
{
struct link_encoder *link_enc
= pipe_ctx->stream->ctx->dc->config.unify_link_enc_assignment
? pipe_ctx->link_res.dio_link_enc
: link_enc_cfg_get_link_enc(pipe_ctx->stream->link);
ASSERT(link_enc);
return link_enc;
}
static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off)
{
struct cp_psp *cp_psp = &pipe_ctx->stream->ctx->cp_psp;
struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc;
struct link_encoder *link_enc = get_link_encoder(pipe_ctx);
struct cp_psp_stream_config config = {0};
enum dp_panel_mode panel_mode =
dp_get_panel_mode(pipe_ctx->stream->link);
if (cp_psp == NULL || cp_psp->funcs.update_stream_config == NULL)
return;
if (!pipe_ctx->stream->ctx->dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(pipe_ctx->stream->link);
ASSERT(link_enc);
if (link_enc == NULL)
return;
@ -2375,7 +2383,7 @@ static struct vpg *get_vpg(struct pipe_ctx *pipe_ctx)
return pipe_ctx->stream_res.stream_enc->vpg;
}
void link_set_dpms_off(struct pipe_ctx *pipe_ctx)
enum dc_status link_set_dpms_off(struct pipe_ctx *pipe_ctx)
{
DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger);
struct dc_stream_state *stream = pipe_ctx->stream;
@ -2387,7 +2395,8 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx)
ASSERT(is_master_pipe_for_link(link, pipe_ctx));
if (dc_is_virtual_signal(stream->signal))
return;
/* No real hardware to program for virtual signals. */
return DC_DPMS_SKIPPED_HANDSHAKE;
if (stream->sink) {
if (stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL &&
@ -2481,18 +2490,23 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx)
/* since current psp not loaded, we need to reset it to default */
link->panel_mode = panel_mode;
}
return DC_DPMS_SUCCESS;
}
void link_set_dpms_on(
static enum dc_status link_set_dpms_on_pre_enable_link(
struct dc_state *state,
struct pipe_ctx *pipe_ctx)
struct pipe_ctx *pipe_ctx
)
{
// Used conditionally in ifdef'ed diagnostic builds
(void) state;
DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger);
struct dc_stream_state *stream = pipe_ctx->stream;
struct dc *dc = stream->ctx->dc;
struct dc_link *link = stream->link;
enum dc_status status;
struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc;
enum otg_out_mux_dest otg_out_dest = OUT_MUX_DIO;
struct vpg *vpg = get_vpg(pipe_ctx);
const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res);
@ -2502,7 +2516,8 @@ void link_set_dpms_on(
ASSERT(is_master_pipe_for_link(link, pipe_ctx));
if (dc_is_virtual_signal(stream->signal))
return;
/* No real hardware to program for virtual signals. */
return DC_DPMS_SKIPPED_HANDSHAKE;
if (stream->sink) {
if (stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL &&
@ -2516,13 +2531,12 @@ void link_set_dpms_on(
}
link_wait_for_unlocked(link);
if (!dc->config.unify_link_enc_assignment)
link_enc = link_enc_cfg_get_link_enc(link);
ASSERT(link_enc);
if (!dc_is_virtual_signal(stream->signal)
&& !dc_is_hdmi_frl_signal(stream->signal)
&& !dp_is_128b_132b_signal(pipe_ctx)) {
struct link_encoder *link_enc = get_link_encoder(pipe_ctx);
if (link_enc)
link_enc->funcs->setup(
link_enc,
@ -2569,7 +2583,9 @@ void link_set_dpms_on(
}
update_psp_stream_config(pipe_ctx, false);
return;
/* Seamless boot: hardware already enabled by BIOS; skip link training. */
return DC_DPMS_SKIPPED_HANDSHAKE;
}
/* eDP lit up by bios already, no need to enable again. */
@ -2587,11 +2603,16 @@ void link_set_dpms_on(
msleep(post_oui_delay);
}
return;
/* eDP already lit by BIOS; skip standard enable steps. */
return DC_DPMS_SKIPPED_HANDSHAKE;
}
if (stream->dpms_off)
return;
/*
* Stream is configured as DPMS-off; skip link enable.
* Hardware will NOT be in a fully enabled state after this early exit.
*/
return DC_DPMS_FAILED_INCOMPLETE;
/* For Dp tunneling link, a pending HPD means that we have a race condition between processing
* current link and processing the pending HPD. If we enable the link now, we may end up with a
@ -2599,7 +2620,11 @@ void link_set_dpms_on(
*/
if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && link->is_hpd_pending) {
DC_LOG_DEBUG("%s, Link%d HPD is pending, not enable it.\n", __func__, link->link_index);
return;
/*
* Pending HPD on USB4 DPIA link: skip enable to avoid race condition.
* Hardware will NOT be in a fully enabled state after this early exit.
*/
return DC_DPMS_FAILED_INCOMPLETE;
}
/* Have to setup DSC before DIG FE and BE are connected (which happens before the
@ -2617,30 +2642,61 @@ void link_set_dpms_on(
if (link->replay_settings.config.replay_supported && !dc_is_embedded_signal(link->connector_signal))
dp_setup_replay(link, stream);
// TODO: Split DPMS-on into 3 functions at this point
status = enable_link(state, pipe_ctx);
return DC_DPMS_SUCCESS;
}
if (status != DC_OK) {
DC_LOG_WARNING("enabling link %u failed: %d\n",
link->link_index,
status);
static enum dc_status link_set_dpms_on_enable_link(
struct dc_state *state,
struct pipe_ctx *pipe_ctx
)
{
DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger);
struct dc_stream_state *stream = pipe_ctx->stream;
struct dc_link *link = stream->link;
const enum dc_status status = enable_link(state, pipe_ctx);
/* Abort stream enable *unless* the failure was due to
* DP link training - some DP monitors will recover and
* show the stream anyway. But MST displays can't proceed
* without link training.
*/
if ((status != DC_FAIL_DP_LINK_TRAINING &&
status != DC_FAIL_HDMI_FRL_LINK_TRAINING) ||
stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
if (false == link->link_status.link_active)
disable_link(link, &pipe_ctx->link_res,
stream->signal);
BREAK_TO_DEBUGGER();
return;
}
if (status == DC_OK)
return DC_DPMS_SUCCESS;
DC_LOG_WARNING("enabling link %u failed: %d\n", link->link_index, status);
/* Abort stream enable *unless* the failure was due to
* DP link training - some DP monitors will recover and
* show the stream anyway. But MST displays can't proceed
* without link training.
*/
switch (status) {
case DC_FAIL_DP_LINK_TRAINING:
case DC_FAIL_HDMI_FRL_LINK_TRAINING:
if (stream->signal != SIGNAL_TYPE_DISPLAY_PORT_MST)
return DC_DPMS_SUCCESS;
break;
default:
break;
}
// TODO: Split DPMS-on into 3 functions at this point
if (!link->link_status.link_active)
disable_link(link, &pipe_ctx->link_res, stream->signal);
/*
* Link enable failed; do NOT set skip_remaining so that post_enable_link
* still runs and leaves hardware in a consistent state.
*/
return DC_DPMS_FAILED_HANDSHAKE;
}
static enum dc_status link_set_dpms_on_post_enable_link(
struct dc_state *state,
struct pipe_ctx *pipe_ctx
)
{
// Used conditionally in ifdef'ed diagnostic builds
(void) state;
struct dc_stream_state *stream = pipe_ctx->stream;
struct dc_link *link = stream->link;
struct hw_sequencer_funcs *hwss = &stream->ctx->dc->hwss;
if (stream->timing.flags.DSC && dc_is_hdmi_frl_signal(stream->signal))
//TODO: bring HDMI FRL in line with DP
@ -2659,13 +2715,15 @@ void link_set_dpms_on(
if (!(dc_is_virtual_signal(stream->signal) ||
dc_is_hdmi_frl_signal(stream->signal) ||
dp_is_128b_132b_signal(pipe_ctx))) {
struct link_encoder *link_enc = get_link_encoder(pipe_ctx);
if (link_enc)
link_enc->funcs->setup(
link_enc,
stream->signal);
}
dc->hwss.enable_stream(pipe_ctx);
hwss->enable_stream(pipe_ctx);
/* Set DPS PPS SDP (AKA "info frames") */
if (stream->timing.flags.DSC) {
@ -2697,7 +2755,7 @@ void link_set_dpms_on(
link->is_display_mux_present)
msleep(20);
dc->hwss.unblank_stream(pipe_ctx,
hwss->unblank_stream(pipe_ctx,
&link->cur_link_settings);
if (stream->sink_patches.delay_ignore_msa > 0)
@ -2707,8 +2765,50 @@ void link_set_dpms_on(
enable_stream_features(pipe_ctx);
update_psp_stream_config(pipe_ctx, false);
dc->hwss.enable_audio_stream(pipe_ctx);
hwss->enable_audio_stream(pipe_ctx);
if (dc_is_hdmi_signal(stream->signal))
set_avmute(pipe_ctx, false);
return DC_DPMS_SUCCESS;
}
enum dc_status link_set_dpms_on(
struct dc_state *state,
struct pipe_ctx *pipe_ctx
)
{
enum dc_status result = DC_DPMS_SUCCESS;
typedef enum dc_status (*step)(struct dc_state *, struct pipe_ctx *);
const step steps[] = {
link_set_dpms_on_pre_enable_link,
link_set_dpms_on_enable_link,
link_set_dpms_on_post_enable_link,
};
for (size_t i = 0; i < ARRAY_SIZE(steps); i++) {
const enum dc_status step_result = steps[i](state, pipe_ctx);
switch (step_result) {
case DC_DPMS_SUCCESS:
case DC_DPMS_FAILED_HANDSHAKE:
// Enum is ordered from "best" to "worst" results
result = max(result, step_result);
break;
case DC_DPMS_SKIPPED_HANDSHAKE:
return step_result;
case DC_DPMS_FAILED_INCOMPLETE:
ASSERT(false);
return step_result;
default:
ASSERT(false);
return step_result;
}
}
return result;
}

View File

@ -27,10 +27,10 @@
#define __DC_LINK_DPMS_H__
#include "link_service.h"
void link_set_dpms_on(
enum dc_status link_set_dpms_on(
struct dc_state *state,
struct pipe_ctx *pipe_ctx);
void link_set_dpms_off(struct pipe_ctx *pipe_ctx);
enum dc_status link_set_dpms_off(struct pipe_ctx *pipe_ctx);
void link_resume(struct dc_link *link);
void link_blank_all_dp_displays(struct dc *dc);
void link_blank_all_edp_displays(struct dc *dc);