Commit 0d4b4253 authored by Jimmy Kizito's avatar Jimmy Kizito Committed by Alex Deucher

drm/amd/display: Fix dynamic encoder reassignment

[Why]
Incorrect encoder assignments were being used while applying a new state
to hardware.

(1) When committing a new state to hardware requires resetting the
back-end, the encoder assignments of the current or old state should be
used when disabling the back-end; and the encoder assignments for the
next or new state should be used when re-enabling the back-end.

(2) Link training on hot plug could take over an encoder already in use
by another stream without first disabling it.

[How]

(1) Introduce a resource context 'link_enc_cfg_context' which includes:
- a mode to indicate when transitioning from current to next state.
- transient encoder assignments to use during this state transition.

Update the encoder configuration interface to respond to queries about
encoder assignment based on the mode of operation.

(2) Check if an encoder is already in use before attempting to perform
link training on hot plug.
Reviewed-by: default avatarJun Lei <Jun.Lei@amd.com>
Acked-by: default avatarRodrigo Siqueira <Rodrigo.Siqueira@amd.com>
Signed-off-by: default avatarJimmy Kizito <Jimmy.Kizito@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent b3492ed1
...@@ -3490,11 +3490,8 @@ static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off) ...@@ -3490,11 +3490,8 @@ static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off)
link_enc = pipe_ctx->stream->link->link_enc; link_enc = pipe_ctx->stream->link->link_enc;
config.phy_idx = link_enc->transmitter - TRANSMITTER_UNIPHY_A; config.phy_idx = link_enc->transmitter - TRANSMITTER_UNIPHY_A;
} else if (pipe_ctx->stream->link->dc->res_pool->funcs->link_encs_assign) { } else if (pipe_ctx->stream->link->dc->res_pool->funcs->link_encs_assign) {
/* Use link encoder assignment from current DC state - which may differ from the DC state to be
* committed - when updating PSP config.
*/
link_enc = link_enc_cfg_get_link_enc_used_by_stream( link_enc = link_enc_cfg_get_link_enc_used_by_stream(
pipe_ctx->stream->link->dc->current_state, pipe_ctx->stream->ctx->dc,
pipe_ctx->stream); pipe_ctx->stream);
config.phy_idx = 0; /* Clear phy_idx for non-physical display endpoints. */ config.phy_idx = 0; /* Clear phy_idx for non-physical display endpoints. */
} }
...@@ -3619,7 +3616,7 @@ void core_link_enable_stream( ...@@ -3619,7 +3616,7 @@ void core_link_enable_stream(
return; return;
if (dc->res_pool->funcs->link_encs_assign && stream->link->ep_type != DISPLAY_ENDPOINT_PHY) if (dc->res_pool->funcs->link_encs_assign && stream->link->ep_type != DISPLAY_ENDPOINT_PHY)
link_enc = stream->link_enc; link_enc = link_enc_cfg_get_link_enc_used_by_stream(dc, stream);
else else
link_enc = stream->link->link_enc; link_enc = stream->link->link_enc;
ASSERT(link_enc); ASSERT(link_enc);
...@@ -4216,14 +4213,14 @@ bool dc_link_is_fec_supported(const struct dc_link *link) ...@@ -4216,14 +4213,14 @@ bool dc_link_is_fec_supported(const struct dc_link *link)
*/ */
if (link->is_dig_mapping_flexible && if (link->is_dig_mapping_flexible &&
link->dc->res_pool->funcs->link_encs_assign) { link->dc->res_pool->funcs->link_encs_assign) {
link_enc = link_enc_cfg_get_link_enc_used_by_link(link->dc->current_state, link); link_enc = link_enc_cfg_get_link_enc_used_by_link(link->ctx->dc, link);
if (link_enc == NULL) if (link_enc == NULL)
link_enc = link_enc_cfg_get_next_avail_link_enc(link->dc, link->dc->current_state); link_enc = link_enc_cfg_get_next_avail_link_enc(link->ctx->dc);
} else } else
link_enc = link->link_enc; link_enc = link->link_enc;
ASSERT(link_enc); ASSERT(link_enc);
return (dc_is_dp_signal(link->connector_signal) && return (dc_is_dp_signal(link->connector_signal) && link_enc &&
link_enc->features.fec_supported && link_enc->features.fec_supported &&
link->dpcd_caps.fec_cap.bits.FEC_CAPABLE && link->dpcd_caps.fec_cap.bits.FEC_CAPABLE &&
!IS_FPGA_MAXIMUS_DC(link->ctx->dce_environment)); !IS_FPGA_MAXIMUS_DC(link->ctx->dce_environment));
......
...@@ -304,7 +304,7 @@ static enum dc_dp_training_pattern decide_eq_training_pattern(struct dc_link *li ...@@ -304,7 +304,7 @@ static enum dc_dp_training_pattern decide_eq_training_pattern(struct dc_link *li
*/ */
if (link->is_dig_mapping_flexible && if (link->is_dig_mapping_flexible &&
link->dc->res_pool->funcs->link_encs_assign) link->dc->res_pool->funcs->link_encs_assign)
link_enc = link_enc_cfg_get_link_enc_used_by_link(link->dc->current_state, link); link_enc = link_enc_cfg_get_link_enc_used_by_link(link->ctx->dc, link);
else else
link_enc = link->link_enc; link_enc = link->link_enc;
ASSERT(link_enc); ASSERT(link_enc);
...@@ -339,7 +339,7 @@ static enum dc_dp_training_pattern decide_eq_training_pattern(struct dc_link *li ...@@ -339,7 +339,7 @@ static enum dc_dp_training_pattern decide_eq_training_pattern(struct dc_link *li
*/ */
if (link->is_dig_mapping_flexible && if (link->is_dig_mapping_flexible &&
link->dc->res_pool->funcs->link_encs_assign) link->dc->res_pool->funcs->link_encs_assign)
link_enc = link_enc_cfg_get_link_enc_used_by_link(link->dc->current_state, link); link_enc = link_enc_cfg_get_link_enc_used_by_link(link->ctx->dc, link);
else else
link_enc = link->link_enc; link_enc = link->link_enc;
ASSERT(link_enc); ASSERT(link_enc);
...@@ -2385,7 +2385,7 @@ bool perform_link_training_with_retries( ...@@ -2385,7 +2385,7 @@ bool perform_link_training_with_retries(
* link. * link.
*/ */
if (link->is_dig_mapping_flexible && link->dc->res_pool->funcs->link_encs_assign) if (link->is_dig_mapping_flexible && link->dc->res_pool->funcs->link_encs_assign)
link_enc = stream->link_enc; link_enc = link_enc_cfg_get_link_enc_used_by_stream(link->ctx->dc, pipe_ctx->stream);
else else
link_enc = link->link_enc; link_enc = link->link_enc;
...@@ -2666,9 +2666,9 @@ bool dc_link_dp_get_max_link_enc_cap(const struct dc_link *link, struct dc_link_ ...@@ -2666,9 +2666,9 @@ bool dc_link_dp_get_max_link_enc_cap(const struct dc_link *link, struct dc_link_
*/ */
if (link->is_dig_mapping_flexible && if (link->is_dig_mapping_flexible &&
link->dc->res_pool->funcs->link_encs_assign) { link->dc->res_pool->funcs->link_encs_assign) {
link_enc = link_enc_cfg_get_link_enc_used_by_link(link->dc->current_state, link); link_enc = link_enc_cfg_get_link_enc_used_by_link(link->ctx->dc, link);
if (link_enc == NULL) if (link_enc == NULL)
link_enc = link_enc_cfg_get_next_avail_link_enc(link->dc, link->dc->current_state); link_enc = link_enc_cfg_get_next_avail_link_enc(link->ctx->dc);
} else } else
link_enc = link->link_enc; link_enc = link->link_enc;
ASSERT(link_enc); ASSERT(link_enc);
...@@ -2697,9 +2697,9 @@ static struct dc_link_settings get_max_link_cap(struct dc_link *link) ...@@ -2697,9 +2697,9 @@ static struct dc_link_settings get_max_link_cap(struct dc_link *link)
*/ */
if (link->is_dig_mapping_flexible && if (link->is_dig_mapping_flexible &&
link->dc->res_pool->funcs->link_encs_assign) { link->dc->res_pool->funcs->link_encs_assign) {
link_enc = link_enc_cfg_get_link_enc_used_by_link(link->dc->current_state, link); link_enc = link_enc_cfg_get_link_enc_used_by_link(link->ctx->dc, link);
if (link_enc == NULL) if (link_enc == NULL)
link_enc = link_enc_cfg_get_next_avail_link_enc(link->dc, link->dc->current_state); link_enc = link_enc_cfg_get_next_avail_link_enc(link->ctx->dc);
} else } else
link_enc = link->link_enc; link_enc = link->link_enc;
ASSERT(link_enc); ASSERT(link_enc);
...@@ -2881,7 +2881,13 @@ bool dp_verify_link_cap( ...@@ -2881,7 +2881,13 @@ bool dp_verify_link_cap(
enum link_training_result status; enum link_training_result status;
union hpd_irq_data irq_data; union hpd_irq_data irq_data;
if (link->dc->debug.skip_detection_link_training) { /* Accept reported capabilities if link supports flexible encoder mapping or encoder already in use. */
if (link->dc->debug.skip_detection_link_training ||
link->is_dig_mapping_flexible) {
link->verified_link_cap = *known_limit_link_setting;
return true;
} else if (link->link_enc && link->dc->res_pool->funcs->link_encs_assign &&
!link_enc_cfg_is_link_enc_avail(link->ctx->dc, link->link_enc->preferred_engine)) {
link->verified_link_cap = *known_limit_link_setting; link->verified_link_cap = *known_limit_link_setting;
return true; return true;
} }
...@@ -5679,7 +5685,7 @@ enum dc_status dp_set_fec_ready(struct dc_link *link, bool ready) ...@@ -5679,7 +5685,7 @@ enum dc_status dp_set_fec_ready(struct dc_link *link, bool ready)
*/ */
if (link->is_dig_mapping_flexible && if (link->is_dig_mapping_flexible &&
link->dc->res_pool->funcs->link_encs_assign) link->dc->res_pool->funcs->link_encs_assign)
link_enc = link_enc_cfg_get_link_enc_used_by_link(link->dc->current_state, link); link_enc = link_enc_cfg_get_link_enc_used_by_link(link->ctx->dc, link);
else else
link_enc = link->link_enc; link_enc = link->link_enc;
ASSERT(link_enc); ASSERT(link_enc);
...@@ -5726,8 +5732,7 @@ void dp_set_fec_enable(struct dc_link *link, bool enable) ...@@ -5726,8 +5732,7 @@ void dp_set_fec_enable(struct dc_link *link, bool enable)
*/ */
if (link->is_dig_mapping_flexible && if (link->is_dig_mapping_flexible &&
link->dc->res_pool->funcs->link_encs_assign) link->dc->res_pool->funcs->link_encs_assign)
link_enc = link_enc_cfg_get_link_enc_used_by_link( link_enc = link_enc_cfg_get_link_enc_used_by_link(link->ctx->dc, link);
link->dc->current_state, link);
else else
link_enc = link->link_enc; link_enc = link->link_enc;
ASSERT(link_enc); ASSERT(link_enc);
......
...@@ -63,6 +63,18 @@ static bool is_dig_link_enc_stream(struct dc_stream_state *stream) ...@@ -63,6 +63,18 @@ static bool is_dig_link_enc_stream(struct dc_stream_state *stream)
return is_dig_stream; return is_dig_stream;
} }
static struct link_enc_assignment get_assignment(struct dc *dc, int i)
{
struct link_enc_assignment assignment;
if (dc->current_state->res_ctx.link_enc_cfg_ctx.mode == LINK_ENC_CFG_TRANSIENT)
assignment = dc->current_state->res_ctx.link_enc_cfg_ctx.transient_assignments[i];
else /* LINK_ENC_CFG_STEADY */
assignment = dc->current_state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
return assignment;
}
/* Return stream using DIG link encoder resource. NULL if unused. */ /* Return stream using DIG link encoder resource. NULL if unused. */
static struct dc_stream_state *get_stream_using_link_enc( static struct dc_stream_state *get_stream_using_link_enc(
struct dc_state *state, struct dc_state *state,
...@@ -72,7 +84,7 @@ static struct dc_stream_state *get_stream_using_link_enc( ...@@ -72,7 +84,7 @@ static struct dc_stream_state *get_stream_using_link_enc(
int i; int i;
for (i = 0; i < state->stream_count; i++) { for (i = 0; i < state->stream_count; i++) {
struct link_enc_assignment assignment = state->res_ctx.link_enc_assignments[i]; struct link_enc_assignment assignment = state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
if ((assignment.valid == true) && (assignment.eng_id == eng_id)) { if ((assignment.valid == true) && (assignment.eng_id == eng_id)) {
stream = state->streams[i]; stream = state->streams[i];
...@@ -98,15 +110,15 @@ static void remove_link_enc_assignment( ...@@ -98,15 +110,15 @@ static void remove_link_enc_assignment(
* link_enc_assignments table. * link_enc_assignments table.
*/ */
for (i = 0; i < MAX_PIPES; i++) { for (i = 0; i < MAX_PIPES; i++) {
struct link_enc_assignment assignment = state->res_ctx.link_enc_assignments[i]; struct link_enc_assignment assignment = state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
if (assignment.valid && assignment.stream == stream) { if (assignment.valid && assignment.stream == stream) {
state->res_ctx.link_enc_assignments[i].valid = false; state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i].valid = false;
/* Only add link encoder back to availability pool if not being /* Only add link encoder back to availability pool if not being
* used by any other stream (i.e. removing SST stream or last MST stream). * used by any other stream (i.e. removing SST stream or last MST stream).
*/ */
if (get_stream_using_link_enc(state, eng_id) == NULL) if (get_stream_using_link_enc(state, eng_id) == NULL)
state->res_ctx.link_enc_avail[eng_idx] = eng_id; state->res_ctx.link_enc_cfg_ctx.link_enc_avail[eng_idx] = eng_id;
stream->link_enc = NULL; stream->link_enc = NULL;
break; break;
} }
...@@ -130,14 +142,14 @@ static void add_link_enc_assignment( ...@@ -130,14 +142,14 @@ static void add_link_enc_assignment(
*/ */
for (i = 0; i < state->stream_count; i++) { for (i = 0; i < state->stream_count; i++) {
if (stream == state->streams[i]) { if (stream == state->streams[i]) {
state->res_ctx.link_enc_assignments[i] = (struct link_enc_assignment){ state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i] = (struct link_enc_assignment){
.valid = true, .valid = true,
.ep_id = (struct display_endpoint_id) { .ep_id = (struct display_endpoint_id) {
.link_id = stream->link->link_id, .link_id = stream->link->link_id,
.ep_type = stream->link->ep_type}, .ep_type = stream->link->ep_type},
.eng_id = eng_id, .eng_id = eng_id,
.stream = stream}; .stream = stream};
state->res_ctx.link_enc_avail[eng_idx] = ENGINE_ID_UNKNOWN; state->res_ctx.link_enc_cfg_ctx.link_enc_avail[eng_idx] = ENGINE_ID_UNKNOWN;
stream->link_enc = stream->ctx->dc->res_pool->link_encoders[eng_idx]; stream->link_enc = stream->ctx->dc->res_pool->link_encoders[eng_idx];
break; break;
} }
...@@ -157,7 +169,7 @@ static enum engine_id find_first_avail_link_enc( ...@@ -157,7 +169,7 @@ static enum engine_id find_first_avail_link_enc(
int i; int i;
for (i = 0; i < ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) { for (i = 0; i < ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) {
eng_id = state->res_ctx.link_enc_avail[i]; eng_id = state->res_ctx.link_enc_cfg_ctx.link_enc_avail[i];
if (eng_id != ENGINE_ID_UNKNOWN) if (eng_id != ENGINE_ID_UNKNOWN)
break; break;
} }
...@@ -170,7 +182,7 @@ static bool is_avail_link_enc(struct dc_state *state, enum engine_id eng_id) ...@@ -170,7 +182,7 @@ static bool is_avail_link_enc(struct dc_state *state, enum engine_id eng_id)
bool is_avail = false; bool is_avail = false;
int eng_idx = eng_id - ENGINE_ID_DIGA; int eng_idx = eng_id - ENGINE_ID_DIGA;
if (eng_id != ENGINE_ID_UNKNOWN && state->res_ctx.link_enc_avail[eng_idx] != ENGINE_ID_UNKNOWN) if (eng_id != ENGINE_ID_UNKNOWN && state->res_ctx.link_enc_cfg_ctx.link_enc_avail[eng_idx] != ENGINE_ID_UNKNOWN)
is_avail = true; is_avail = true;
return is_avail; return is_avail;
...@@ -190,6 +202,28 @@ static bool are_ep_ids_equal(struct display_endpoint_id *lhs, struct display_end ...@@ -190,6 +202,28 @@ static bool are_ep_ids_equal(struct display_endpoint_id *lhs, struct display_end
return are_equal; return are_equal;
} }
static struct link_encoder *get_link_enc_used_by_link(
struct dc_state *state,
const struct dc_link *link)
{
struct link_encoder *link_enc = NULL;
struct display_endpoint_id ep_id;
int i;
ep_id = (struct display_endpoint_id) {
.link_id = link->link_id,
.ep_type = link->ep_type};
for (i = 0; i < state->stream_count; i++) {
struct link_enc_assignment assignment = state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
if (assignment.valid == true && are_ep_ids_equal(&assignment.ep_id, &ep_id))
link_enc = link->dc->res_pool->link_encoders[assignment.eng_id - ENGINE_ID_DIGA];
}
return link_enc;
}
void link_enc_cfg_init( void link_enc_cfg_init(
struct dc *dc, struct dc *dc,
struct dc_state *state) struct dc_state *state)
...@@ -198,10 +232,12 @@ void link_enc_cfg_init( ...@@ -198,10 +232,12 @@ void link_enc_cfg_init(
for (i = 0; i < dc->res_pool->res_cap->num_dig_link_enc; i++) { for (i = 0; i < dc->res_pool->res_cap->num_dig_link_enc; i++) {
if (dc->res_pool->link_encoders[i]) if (dc->res_pool->link_encoders[i])
state->res_ctx.link_enc_avail[i] = (enum engine_id) i; state->res_ctx.link_enc_cfg_ctx.link_enc_avail[i] = (enum engine_id) i;
else else
state->res_ctx.link_enc_avail[i] = ENGINE_ID_UNKNOWN; state->res_ctx.link_enc_cfg_ctx.link_enc_avail[i] = ENGINE_ID_UNKNOWN;
} }
state->res_ctx.link_enc_cfg_ctx.mode = LINK_ENC_CFG_STEADY;
} }
void link_enc_cfg_link_encs_assign( void link_enc_cfg_link_encs_assign(
...@@ -221,7 +257,7 @@ void link_enc_cfg_link_encs_assign( ...@@ -221,7 +257,7 @@ void link_enc_cfg_link_encs_assign(
dc->res_pool->funcs->link_enc_unassign(state, streams[i]); dc->res_pool->funcs->link_enc_unassign(state, streams[i]);
for (i = 0; i < MAX_PIPES; i++) for (i = 0; i < MAX_PIPES; i++)
ASSERT(state->res_ctx.link_enc_assignments[i].valid == false); ASSERT(state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i].valid == false);
/* (a) Assign DIG link encoders to physical (unmappable) endpoints first. */ /* (a) Assign DIG link encoders to physical (unmappable) endpoints first. */
for (i = 0; i < stream_count; i++) { for (i = 0; i < stream_count; i++) {
...@@ -258,8 +294,8 @@ void link_enc_cfg_link_encs_assign( ...@@ -258,8 +294,8 @@ void link_enc_cfg_link_encs_assign(
struct dc_stream_state *prev_stream = prev_state->streams[j]; struct dc_stream_state *prev_stream = prev_state->streams[j];
if (stream == prev_stream && stream->link == prev_stream->link && if (stream == prev_stream && stream->link == prev_stream->link &&
prev_state->res_ctx.link_enc_assignments[j].valid) { prev_state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[j].valid) {
eng_id = prev_state->res_ctx.link_enc_assignments[j].eng_id; eng_id = prev_state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[j].eng_id;
if (is_avail_link_enc(state, eng_id)) if (is_avail_link_enc(state, eng_id))
add_link_enc_assignment(state, stream, eng_id); add_link_enc_assignment(state, stream, eng_id);
} }
...@@ -291,7 +327,7 @@ void link_enc_cfg_link_encs_assign( ...@@ -291,7 +327,7 @@ void link_enc_cfg_link_encs_assign(
* endpoint. These streams should use the same link encoder * endpoint. These streams should use the same link encoder
* assigned to that endpoint. * assigned to that endpoint.
*/ */
link_enc = link_enc_cfg_get_link_enc_used_by_link(state, stream->link); link_enc = get_link_enc_used_by_link(state, stream->link);
if (link_enc == NULL) if (link_enc == NULL)
eng_id = find_first_avail_link_enc(stream->ctx, state); eng_id = find_first_avail_link_enc(stream->ctx, state);
else else
...@@ -301,6 +337,15 @@ void link_enc_cfg_link_encs_assign( ...@@ -301,6 +337,15 @@ void link_enc_cfg_link_encs_assign(
} }
link_enc_cfg_validate(dc, state); link_enc_cfg_validate(dc, state);
/* Update transient assignments. */
for (i = 0; i < MAX_PIPES; i++) {
dc->current_state->res_ctx.link_enc_cfg_ctx.transient_assignments[i] =
state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
}
/* Current state mode will be set to steady once this state committed. */
state->res_ctx.link_enc_cfg_ctx.mode = LINK_ENC_CFG_STEADY;
} }
void link_enc_cfg_link_enc_unassign( void link_enc_cfg_link_enc_unassign(
...@@ -320,12 +365,12 @@ void link_enc_cfg_link_enc_unassign( ...@@ -320,12 +365,12 @@ void link_enc_cfg_link_enc_unassign(
} }
bool link_enc_cfg_is_transmitter_mappable( bool link_enc_cfg_is_transmitter_mappable(
struct dc_state *state, struct dc *dc,
struct link_encoder *link_enc) struct link_encoder *link_enc)
{ {
bool is_mappable = false; bool is_mappable = false;
enum engine_id eng_id = link_enc->preferred_engine; enum engine_id eng_id = link_enc->preferred_engine;
struct dc_stream_state *stream = get_stream_using_link_enc(state, eng_id); struct dc_stream_state *stream = link_enc_cfg_get_stream_using_link_enc(dc, eng_id);
if (stream) if (stream)
is_mappable = stream->link->is_dig_mapping_flexible; is_mappable = stream->link->is_dig_mapping_flexible;
...@@ -333,30 +378,43 @@ bool link_enc_cfg_is_transmitter_mappable( ...@@ -333,30 +378,43 @@ bool link_enc_cfg_is_transmitter_mappable(
return is_mappable; return is_mappable;
} }
struct dc_link *link_enc_cfg_get_link_using_link_enc( struct dc_stream_state *link_enc_cfg_get_stream_using_link_enc(
struct dc_state *state, struct dc *dc,
enum engine_id eng_id) enum engine_id eng_id)
{ {
struct dc_link *link = NULL; struct dc_stream_state *stream = NULL;
int i; int i;
for (i = 0; i < state->stream_count; i++) { for (i = 0; i < MAX_PIPES; i++) {
struct link_enc_assignment assignment = state->res_ctx.link_enc_assignments[i]; struct link_enc_assignment assignment = get_assignment(dc, i);
if ((assignment.valid == true) && (assignment.eng_id == eng_id)) { if ((assignment.valid == true) && (assignment.eng_id == eng_id)) {
link = state->streams[i]->link; stream = assignment.stream;
break; break;
} }
} }
if (link == NULL) return stream;
dm_output_to_console("%s: No link using DIG(%d).\n", __func__, eng_id); }
struct dc_link *link_enc_cfg_get_link_using_link_enc(
struct dc *dc,
enum engine_id eng_id)
{
struct dc_link *link = NULL;
struct dc_stream_state *stream = NULL;
stream = link_enc_cfg_get_stream_using_link_enc(dc, eng_id);
if (stream)
link = stream->link;
// dm_output_to_console("%s: No link using DIG(%d).\n", __func__, eng_id);
return link; return link;
} }
struct link_encoder *link_enc_cfg_get_link_enc_used_by_link( struct link_encoder *link_enc_cfg_get_link_enc_used_by_link(
struct dc_state *state, struct dc *dc,
const struct dc_link *link) const struct dc_link *link)
{ {
struct link_encoder *link_enc = NULL; struct link_encoder *link_enc = NULL;
...@@ -367,41 +425,74 @@ struct link_encoder *link_enc_cfg_get_link_enc_used_by_link( ...@@ -367,41 +425,74 @@ struct link_encoder *link_enc_cfg_get_link_enc_used_by_link(
.link_id = link->link_id, .link_id = link->link_id,
.ep_type = link->ep_type}; .ep_type = link->ep_type};
for (i = 0; i < state->stream_count; i++) { for (i = 0; i < MAX_PIPES; i++) {
struct link_enc_assignment assignment = state->res_ctx.link_enc_assignments[i]; struct link_enc_assignment assignment = get_assignment(dc, i);
if (assignment.valid == true && are_ep_ids_equal(&assignment.ep_id, &ep_id)) if (assignment.valid == true && are_ep_ids_equal(&assignment.ep_id, &ep_id)) {
link_enc = link->dc->res_pool->link_encoders[assignment.eng_id - ENGINE_ID_DIGA]; link_enc = link->dc->res_pool->link_encoders[assignment.eng_id - ENGINE_ID_DIGA];
break;
}
} }
return link_enc; return link_enc;
} }
struct link_encoder *link_enc_cfg_get_next_avail_link_enc( struct link_encoder *link_enc_cfg_get_next_avail_link_enc(struct dc *dc)
const struct dc *dc,
const struct dc_state *state)
{ {
struct link_encoder *link_enc = NULL; struct link_encoder *link_enc = NULL;
enum engine_id eng_id; enum engine_id encs_assigned[MAX_DIG_LINK_ENCODERS];
int i;
for (i = 0; i < MAX_DIG_LINK_ENCODERS; i++)
encs_assigned[i] = ENGINE_ID_UNKNOWN;
eng_id = find_first_avail_link_enc(dc->ctx, state); /* Add assigned encoders to list. */
if (eng_id != ENGINE_ID_UNKNOWN) for (i = 0; i < MAX_PIPES; i++) {
link_enc = dc->res_pool->link_encoders[eng_id - ENGINE_ID_DIGA]; struct link_enc_assignment assignment = get_assignment(dc, i);
if (assignment.valid)
encs_assigned[assignment.eng_id - ENGINE_ID_DIGA] = assignment.eng_id;
}
for (i = 0; i < dc->res_pool->res_cap->num_dig_link_enc; i++) {
if (encs_assigned[i] == ENGINE_ID_UNKNOWN) {
link_enc = dc->res_pool->link_encoders[i];
break;
}
}
return link_enc; return link_enc;
} }
struct link_encoder *link_enc_cfg_get_link_enc_used_by_stream( struct link_encoder *link_enc_cfg_get_link_enc_used_by_stream(
struct dc_state *state, struct dc *dc,
const struct dc_stream_state *stream) const struct dc_stream_state *stream)
{ {
struct link_encoder *link_enc; struct link_encoder *link_enc;
link_enc = link_enc_cfg_get_link_enc_used_by_link(state, stream->link); link_enc = link_enc_cfg_get_link_enc_used_by_link(dc, stream->link);
return link_enc; return link_enc;
} }
bool link_enc_cfg_is_link_enc_avail(struct dc *dc, enum engine_id eng_id)
{
bool is_avail = true;
int i;
/* Add assigned encoders to list. */
for (i = 0; i < MAX_PIPES; i++) {
struct link_enc_assignment assignment = get_assignment(dc, i);
if (assignment.valid && assignment.eng_id == eng_id) {
is_avail = false;
break;
}
}
return is_avail;
}
bool link_enc_cfg_validate(struct dc *dc, struct dc_state *state) bool link_enc_cfg_validate(struct dc *dc, struct dc_state *state)
{ {
bool is_valid = false; bool is_valid = false;
...@@ -418,7 +509,7 @@ bool link_enc_cfg_validate(struct dc *dc, struct dc_state *state) ...@@ -418,7 +509,7 @@ bool link_enc_cfg_validate(struct dc *dc, struct dc_state *state)
/* (1) No. valid entries same as stream count. */ /* (1) No. valid entries same as stream count. */
for (i = 0; i < MAX_PIPES; i++) { for (i = 0; i < MAX_PIPES; i++) {
struct link_enc_assignment assignment = state->res_ctx.link_enc_assignments[i]; struct link_enc_assignment assignment = state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
if (assignment.valid) if (assignment.valid)
valid_count++; valid_count++;
...@@ -431,7 +522,7 @@ bool link_enc_cfg_validate(struct dc *dc, struct dc_state *state) ...@@ -431,7 +522,7 @@ bool link_enc_cfg_validate(struct dc *dc, struct dc_state *state)
/* (2) Matching stream ptrs. */ /* (2) Matching stream ptrs. */
for (i = 0; i < MAX_PIPES; i++) { for (i = 0; i < MAX_PIPES; i++) {
struct link_enc_assignment assignment = state->res_ctx.link_enc_assignments[i]; struct link_enc_assignment assignment = state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
if (assignment.valid) { if (assignment.valid) {
if (assignment.stream == state->streams[i]) if (assignment.stream == state->streams[i])
...@@ -443,14 +534,15 @@ bool link_enc_cfg_validate(struct dc *dc, struct dc_state *state) ...@@ -443,14 +534,15 @@ bool link_enc_cfg_validate(struct dc *dc, struct dc_state *state)
/* (3) Each endpoint assigned unique encoder. */ /* (3) Each endpoint assigned unique encoder. */
for (i = 0; i < MAX_PIPES; i++) { for (i = 0; i < MAX_PIPES; i++) {
struct link_enc_assignment assignment_i = state->res_ctx.link_enc_assignments[i]; struct link_enc_assignment assignment_i = state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
if (assignment_i.valid) { if (assignment_i.valid) {
struct display_endpoint_id ep_id_i = assignment_i.ep_id; struct display_endpoint_id ep_id_i = assignment_i.ep_id;
eng_ids_per_ep_id[i]++; eng_ids_per_ep_id[i]++;
for (j = 0; j < MAX_PIPES; j++) { for (j = 0; j < MAX_PIPES; j++) {
struct link_enc_assignment assignment_j = state->res_ctx.link_enc_assignments[j]; struct link_enc_assignment assignment_j =
state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[j];
if (j == i) if (j == i)
continue; continue;
...@@ -470,11 +562,11 @@ bool link_enc_cfg_validate(struct dc *dc, struct dc_state *state) ...@@ -470,11 +562,11 @@ bool link_enc_cfg_validate(struct dc *dc, struct dc_state *state)
/* (4) Assigned encoders not in available pool. */ /* (4) Assigned encoders not in available pool. */
for (i = 0; i < MAX_PIPES; i++) { for (i = 0; i < MAX_PIPES; i++) {
struct link_enc_assignment assignment = state->res_ctx.link_enc_assignments[i]; struct link_enc_assignment assignment = state->res_ctx.link_enc_cfg_ctx.link_enc_assignments[i];
if (assignment.valid) { if (assignment.valid) {
for (j = 0; j < dc->res_pool->res_cap->num_dig_link_enc; j++) { for (j = 0; j < dc->res_pool->res_cap->num_dig_link_enc; j++) {
if (state->res_ctx.link_enc_avail[j] == assignment.eng_id) { if (state->res_ctx.link_enc_cfg_ctx.link_enc_avail[j] == assignment.eng_id) {
valid_avail = false; valid_avail = false;
break; break;
} }
......
...@@ -87,7 +87,7 @@ void dp_enable_link_phy( ...@@ -87,7 +87,7 @@ void dp_enable_link_phy(
/* Link should always be assigned encoder when en-/disabling. */ /* Link should always be assigned encoder when en-/disabling. */
if (link->is_dig_mapping_flexible && dc->res_pool->funcs->link_encs_assign) if (link->is_dig_mapping_flexible && dc->res_pool->funcs->link_encs_assign)
link_enc = link_enc_cfg_get_link_enc_used_by_link(link->dc->current_state, link); link_enc = link_enc_cfg_get_link_enc_used_by_link(dc, link);
else else
link_enc = link->link_enc; link_enc = link->link_enc;
ASSERT(link_enc); ASSERT(link_enc);
...@@ -247,7 +247,7 @@ void dp_disable_link_phy(struct dc_link *link, enum signal_type signal) ...@@ -247,7 +247,7 @@ void dp_disable_link_phy(struct dc_link *link, enum signal_type signal)
/* Link should always be assigned encoder when en-/disabling. */ /* Link should always be assigned encoder when en-/disabling. */
if (link->is_dig_mapping_flexible && dc->res_pool->funcs->link_encs_assign) if (link->is_dig_mapping_flexible && dc->res_pool->funcs->link_encs_assign)
link_enc = link_enc_cfg_get_link_enc_used_by_link(link->dc->current_state, link); link_enc = link_enc_cfg_get_link_enc_used_by_link(dc, link);
else else
link_enc = link->link_enc; link_enc = link->link_enc;
ASSERT(link_enc); ASSERT(link_enc);
...@@ -391,7 +391,7 @@ void dp_set_hw_test_pattern( ...@@ -391,7 +391,7 @@ void dp_set_hw_test_pattern(
*/ */
if (link->is_dig_mapping_flexible && if (link->is_dig_mapping_flexible &&
link->dc->res_pool->funcs->link_encs_assign) link->dc->res_pool->funcs->link_encs_assign)
encoder = link_enc_cfg_get_link_enc_used_by_link(link->dc->current_state, link); encoder = link_enc_cfg_get_link_enc_used_by_link(link->ctx->dc, link);
else else
encoder = link->link_enc; encoder = link->link_enc;
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include "set_mode_types.h" #include "set_mode_types.h"
#include "virtual/virtual_stream_encoder.h" #include "virtual/virtual_stream_encoder.h"
#include "dpcd_defs.h" #include "dpcd_defs.h"
#include "link_enc_cfg.h"
#include "dc_link_dp.h" #include "dc_link_dp.h"
#if defined(CONFIG_DRM_AMD_DC_SI) #if defined(CONFIG_DRM_AMD_DC_SI)
...@@ -2826,8 +2827,18 @@ bool pipe_need_reprogram( ...@@ -2826,8 +2827,18 @@ bool pipe_need_reprogram(
#endif #endif
/* DIG link encoder resource assignment for stream changed. */ /* DIG link encoder resource assignment for stream changed. */
if (pipe_ctx_old->stream->link_enc != pipe_ctx->stream->link_enc) if (pipe_ctx_old->stream->ctx->dc->res_pool->funcs->link_encs_assign) {
return true; bool need_reprogram = false;
struct dc *dc = pipe_ctx_old->stream->ctx->dc;
enum link_enc_cfg_mode mode = dc->current_state->res_ctx.link_enc_cfg_ctx.mode;
dc->current_state->res_ctx.link_enc_cfg_ctx.mode = LINK_ENC_CFG_STEADY;
if (link_enc_cfg_get_link_enc_used_by_stream(dc, pipe_ctx_old->stream) != pipe_ctx->stream->link_enc)
need_reprogram = true;
dc->current_state->res_ctx.link_enc_cfg_ctx.mode = mode;
return need_reprogram;
}
return false; return false;
} }
......
...@@ -1219,7 +1219,7 @@ void dce110_disable_stream(struct pipe_ctx *pipe_ctx) ...@@ -1219,7 +1219,7 @@ void dce110_disable_stream(struct pipe_ctx *pipe_ctx)
if (link->ep_type == DISPLAY_ENDPOINT_PHY) if (link->ep_type == DISPLAY_ENDPOINT_PHY)
link_enc = link->link_enc; link_enc = link->link_enc;
else if (dc->res_pool->funcs->link_encs_assign) else if (dc->res_pool->funcs->link_encs_assign)
link_enc = link_enc_cfg_get_link_enc_used_by_link(link->dc->current_state, link); link_enc = link_enc_cfg_get_link_enc_used_by_link(link->ctx->dc, link);
ASSERT(link_enc); ASSERT(link_enc);
#if defined(CONFIG_DRM_AMD_DC_DCN) #if defined(CONFIG_DRM_AMD_DC_DCN)
......
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
#include "hw_sequencer.h" #include "hw_sequencer.h"
#include "inc/link_dpcd.h" #include "inc/link_dpcd.h"
#include "dpcd_defs.h" #include "dpcd_defs.h"
#include "inc/link_enc_cfg.h"
#define DC_LOGGER_INIT(logger) #define DC_LOGGER_INIT(logger)
...@@ -2385,9 +2386,10 @@ void dcn20_enable_stream(struct pipe_ctx *pipe_ctx) ...@@ -2385,9 +2386,10 @@ void dcn20_enable_stream(struct pipe_ctx *pipe_ctx)
if (link->is_dig_mapping_flexible && if (link->is_dig_mapping_flexible &&
link->dc->res_pool->funcs->link_encs_assign) link->dc->res_pool->funcs->link_encs_assign)
link_enc = pipe_ctx->stream->link_enc; link_enc = link_enc_cfg_get_link_enc_used_by_stream(link->ctx->dc, pipe_ctx->stream);
else else
link_enc = link->link_enc; link_enc = link->link_enc;
ASSERT(link_enc);
/* For MST, there are multiply stream go to only one link. /* For MST, there are multiply stream go to only one link.
* connect DIG back_end to front_end while enable_stream and * connect DIG back_end to front_end while enable_stream and
......
...@@ -1610,10 +1610,9 @@ static void get_pixel_clock_parameters( ...@@ -1610,10 +1610,9 @@ static void get_pixel_clock_parameters(
*/ */
if (link->is_dig_mapping_flexible && if (link->is_dig_mapping_flexible &&
link->dc->res_pool->funcs->link_encs_assign) { link->dc->res_pool->funcs->link_encs_assign) {
link_enc = link_enc_cfg_get_link_enc_used_by_stream(stream->link->dc->current_state, stream); link_enc = link_enc_cfg_get_link_enc_used_by_stream(stream->ctx->dc, stream);
if (link_enc == NULL) if (link_enc == NULL)
link_enc = link_enc_cfg_get_next_avail_link_enc(stream->link->dc, link_enc = link_enc_cfg_get_next_avail_link_enc(stream->ctx->dc);
stream->link->dc->current_state);
} else } else
link_enc = stream->link->link_enc; link_enc = stream->link->link_enc;
ASSERT(link_enc); ASSERT(link_enc);
......
...@@ -367,7 +367,7 @@ void dcn31_link_encoder_enable_dp_output( ...@@ -367,7 +367,7 @@ void dcn31_link_encoder_enable_dp_output(
enum clock_source_id clock_source) enum clock_source_id clock_source)
{ {
/* Enable transmitter and encoder. */ /* Enable transmitter and encoder. */
if (!link_enc_cfg_is_transmitter_mappable(enc->ctx->dc->current_state, enc)) { if (!link_enc_cfg_is_transmitter_mappable(enc->ctx->dc, enc)) {
dcn20_link_encoder_enable_dp_output(enc, link_settings, clock_source); dcn20_link_encoder_enable_dp_output(enc, link_settings, clock_source);
...@@ -383,7 +383,7 @@ void dcn31_link_encoder_enable_dp_mst_output( ...@@ -383,7 +383,7 @@ void dcn31_link_encoder_enable_dp_mst_output(
enum clock_source_id clock_source) enum clock_source_id clock_source)
{ {
/* Enable transmitter and encoder. */ /* Enable transmitter and encoder. */
if (!link_enc_cfg_is_transmitter_mappable(enc->ctx->dc->current_state, enc)) { if (!link_enc_cfg_is_transmitter_mappable(enc->ctx->dc, enc)) {
dcn10_link_encoder_enable_dp_mst_output(enc, link_settings, clock_source); dcn10_link_encoder_enable_dp_mst_output(enc, link_settings, clock_source);
...@@ -398,7 +398,7 @@ void dcn31_link_encoder_disable_output( ...@@ -398,7 +398,7 @@ void dcn31_link_encoder_disable_output(
enum signal_type signal) enum signal_type signal)
{ {
/* Disable transmitter and encoder. */ /* Disable transmitter and encoder. */
if (!link_enc_cfg_is_transmitter_mappable(enc->ctx->dc->current_state, enc)) { if (!link_enc_cfg_is_transmitter_mappable(enc->ctx->dc, enc)) {
dcn10_link_encoder_disable_output(enc, signal); dcn10_link_encoder_disable_output(enc, signal);
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#include "dc_link_dp.h" #include "dc_link_dp.h"
#include "inc/link_dpcd.h" #include "inc/link_dpcd.h"
#include "dcn10/dcn10_hw_sequencer.h" #include "dcn10/dcn10_hw_sequencer.h"
#include "inc/link_enc_cfg.h"
#define DC_LOGGER_INIT(logger) #define DC_LOGGER_INIT(logger)
...@@ -599,4 +600,7 @@ void dcn31_reset_hw_ctx_wrap( ...@@ -599,4 +600,7 @@ void dcn31_reset_hw_ctx_wrap(
old_clk->funcs->cs_power_down(old_clk); old_clk->funcs->cs_power_down(old_clk);
} }
} }
/* New dc_state in the process of being applied to hardware. */
dc->current_state->res_ctx.link_enc_cfg_ctx.mode = LINK_ENC_CFG_TRANSIENT;
} }
...@@ -387,6 +387,17 @@ struct pipe_ctx { ...@@ -387,6 +387,17 @@ struct pipe_ctx {
bool vtp_locked; bool vtp_locked;
}; };
/* Data used for dynamic link encoder assignment.
* Tracks current and future assignments; available link encoders;
* and mode of operation (whether to use current or future assignments).
*/
struct link_enc_cfg_context {
enum link_enc_cfg_mode mode;
struct link_enc_assignment link_enc_assignments[MAX_PIPES];
enum engine_id link_enc_avail[MAX_DIG_LINK_ENCODERS];
struct link_enc_assignment transient_assignments[MAX_PIPES];
};
struct resource_context { struct resource_context {
struct pipe_ctx pipe_ctx[MAX_PIPES]; struct pipe_ctx pipe_ctx[MAX_PIPES];
bool is_stream_enc_acquired[MAX_PIPES * 2]; bool is_stream_enc_acquired[MAX_PIPES * 2];
...@@ -394,12 +405,7 @@ struct resource_context { ...@@ -394,12 +405,7 @@ struct resource_context {
uint8_t clock_source_ref_count[MAX_CLOCK_SOURCES]; uint8_t clock_source_ref_count[MAX_CLOCK_SOURCES];
uint8_t dp_clock_source_ref_count; uint8_t dp_clock_source_ref_count;
bool is_dsc_acquired[MAX_PIPES]; bool is_dsc_acquired[MAX_PIPES];
/* A table/array of encoder-to-link assignments. One entry per stream. struct link_enc_cfg_context link_enc_cfg_ctx;
* Indexed by stream index in dc_state.
*/
struct link_enc_assignment link_enc_assignments[MAX_PIPES];
/* List of available link encoders. Uses engine ID as encoder identifier. */
enum engine_id link_enc_avail[MAX_DIG_LINK_ENCODERS];
#if defined(CONFIG_DRM_AMD_DC_DCN) #if defined(CONFIG_DRM_AMD_DC_DCN)
bool is_hpo_dp_stream_enc_acquired[MAX_HPO_DP2_ENCODERS]; bool is_hpo_dp_stream_enc_acquired[MAX_HPO_DP2_ENCODERS];
#endif #endif
......
...@@ -215,6 +215,11 @@ struct link_enc_assignment { ...@@ -215,6 +215,11 @@ struct link_enc_assignment {
struct dc_stream_state *stream; struct dc_stream_state *stream;
}; };
enum link_enc_cfg_mode {
LINK_ENC_CFG_STEADY, /* Normal operation - use current_state. */
LINK_ENC_CFG_TRANSIENT /* During commit state - use state to be committed. */
};
#if defined(CONFIG_DRM_AMD_DC_DCN) #if defined(CONFIG_DRM_AMD_DC_DCN)
enum dp2_link_mode { enum dp2_link_mode {
DP2_LINK_TRAINING_TPS1, DP2_LINK_TRAINING_TPS1,
......
...@@ -70,29 +70,35 @@ void link_enc_cfg_link_enc_unassign( ...@@ -70,29 +70,35 @@ void link_enc_cfg_link_enc_unassign(
* endpoint. * endpoint.
*/ */
bool link_enc_cfg_is_transmitter_mappable( bool link_enc_cfg_is_transmitter_mappable(
struct dc_state *state, struct dc *dc,
struct link_encoder *link_enc); struct link_encoder *link_enc);
/* Return stream using DIG link encoder resource. NULL if unused. */
struct dc_stream_state *link_enc_cfg_get_stream_using_link_enc(
struct dc *dc,
enum engine_id eng_id);
/* Return link using DIG link encoder resource. NULL if unused. */ /* Return link using DIG link encoder resource. NULL if unused. */
struct dc_link *link_enc_cfg_get_link_using_link_enc( struct dc_link *link_enc_cfg_get_link_using_link_enc(
struct dc_state *state, struct dc *dc,
enum engine_id eng_id); enum engine_id eng_id);
/* Return DIG link encoder used by link. NULL if unused. */ /* Return DIG link encoder used by link. NULL if unused. */
struct link_encoder *link_enc_cfg_get_link_enc_used_by_link( struct link_encoder *link_enc_cfg_get_link_enc_used_by_link(
struct dc_state *state, struct dc *dc,
const struct dc_link *link); const struct dc_link *link);
/* Return next available DIG link encoder. NULL if none available. */ /* Return next available DIG link encoder. NULL if none available. */
struct link_encoder *link_enc_cfg_get_next_avail_link_enc( struct link_encoder *link_enc_cfg_get_next_avail_link_enc(struct dc *dc);
const struct dc *dc,
const struct dc_state *state);
/* Return DIG link encoder used by stream. NULL if unused. */ /* Return DIG link encoder used by stream. NULL if unused. */
struct link_encoder *link_enc_cfg_get_link_enc_used_by_stream( struct link_encoder *link_enc_cfg_get_link_enc_used_by_stream(
struct dc_state *state, struct dc *dc,
const struct dc_stream_state *stream); const struct dc_stream_state *stream);
/* Return true if encoder available to use. */
bool link_enc_cfg_is_link_enc_avail(struct dc *dc, enum engine_id eng_id);
/* Returns true if encoder assignments in supplied state pass validity checks. */ /* Returns true if encoder assignments in supplied state pass validity checks. */
bool link_enc_cfg_validate(struct dc *dc, struct dc_state *state); bool link_enc_cfg_validate(struct dc *dc, struct dc_state *state);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment