mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
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:
parent
cfc867ccfa
commit
843049bac7
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user