Commit eb54e014 authored by Samson Tam's avatar Samson Tam Committed by Alex Deucher

drm/amd/display: Fix two MPO videos in single display ODM combine mode

[Why]
In single display ODM combine mode, two MPO videos ( three
 planes ) are not working

[How]
When we detect three planes, don't set odm combine 2to1 policy
 for the MPO planes.  Otherwise, we run out of pipes available
Add support for two MPO videos in dc_add_plane_to_context().
 Don't allow both videos to be on the same side of the
 display.
Add extra check when fetching free pipe for two MPO videos.
Reviewed-by: default avatarAlvin Lee <Alvin.Lee2@amd.com>
Acked-by: default avatarAlex Hung <alex.hung@amd.com>
Signed-off-by: default avatarSamson Tam <Samson.Tam@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent a7cefb0b
......@@ -1463,6 +1463,7 @@ bool dc_add_plane_to_context(
struct dc_stream_status *stream_status = NULL;
struct pipe_ctx *prev_right_head = NULL;
struct pipe_ctx *free_right_pipe = NULL;
struct pipe_ctx *prev_left_head = NULL;
DC_LOGGER_INIT(stream->ctx->logger);
for (i = 0; i < context->stream_count; i++)
......@@ -1514,8 +1515,16 @@ bool dc_add_plane_to_context(
/* ODM + window MPO, where MPO window is on right half only */
if (free_pipe->plane_state &&
(free_pipe->plane_state->clip_rect.x >= free_pipe->stream->src.x + free_pipe->stream->src.width/2) &&
tail_pipe->next_odm_pipe) {
(free_pipe->plane_state->clip_rect.x >= free_pipe->stream->src.x + free_pipe->stream->src.width/2) &&
tail_pipe->next_odm_pipe) {
/* For ODM + window MPO, in 3 plane case, if we already have a MPO window on
* the right side, then we will invalidate a 2nd one on the right side
*/
if (head_pipe->next_odm_pipe && tail_pipe->next_odm_pipe->bottom_pipe) {
dc_plane_state_release(plane_state);
return false;
}
DC_LOG_SCALER("%s - ODM + window MPO(right). free_pipe:%d tail_pipe->next_odm_pipe:%d\n",
__func__,
......@@ -1530,20 +1539,42 @@ bool dc_add_plane_to_context(
* - If not, continue to use free_pipe
* - If the right side already has a pipe, use that pipe instead if its available
*/
/*
* We also want to avoid the case where with three plane ( 2 MPO videos ), we have
* both videos on the left side so one of the videos is invalidated. Then we
* move the invalidated video back to the right side. If the order of the plane
* states is such that the right MPO plane is processed first, the free pipe
* selected by the head will be the left MPO pipe. But since there was no right
* MPO pipe, it will assign the free pipe to the right MPO pipe instead and
* a pipe reallocation will occur.
* Check the old context to see if the left side already has a pipe allocated
* - If not, continue to use free_pipe
* - If the left side is already using this pipe, then pick another pipe for right
*/
prev_right_head = &dc->current_state->res_ctx.pipe_ctx[tail_pipe->next_odm_pipe->pipe_idx];
if ((prev_right_head->bottom_pipe) && (free_pipe->pipe_idx != prev_right_head->bottom_pipe->pipe_idx)) {
if ((prev_right_head->bottom_pipe) &&
(free_pipe->pipe_idx != prev_right_head->bottom_pipe->pipe_idx)) {
free_right_pipe = acquire_free_pipe_for_head(context, pool, tail_pipe->next_odm_pipe);
if (free_right_pipe) {
free_pipe->stream = NULL;
memset(&free_pipe->stream_res, 0, sizeof(struct stream_resource));
memset(&free_pipe->plane_res, 0, sizeof(struct plane_resource));
free_pipe->plane_state = NULL;
free_pipe->pipe_idx = 0;
free_right_pipe->plane_state = plane_state;
free_pipe = free_right_pipe;
} else {
prev_left_head = &dc->current_state->res_ctx.pipe_ctx[head_pipe->pipe_idx];
if ((prev_left_head->bottom_pipe) &&
(free_pipe->pipe_idx == prev_left_head->bottom_pipe->pipe_idx)) {
free_right_pipe = acquire_free_pipe_for_head(context, pool, head_pipe);
}
}
if (free_right_pipe) {
free_pipe->stream = NULL;
memset(&free_pipe->stream_res, 0, sizeof(struct stream_resource));
memset(&free_pipe->plane_res, 0, sizeof(struct plane_resource));
free_pipe->plane_state = NULL;
free_pipe->pipe_idx = 0;
free_right_pipe->plane_state = plane_state;
free_pipe = free_right_pipe;
}
free_pipe->stream_res.tg = tail_pipe->next_odm_pipe->stream_res.tg;
free_pipe->stream_res.abm = tail_pipe->next_odm_pipe->stream_res.abm;
free_pipe->stream_res.opp = tail_pipe->next_odm_pipe->stream_res.opp;
......@@ -1553,7 +1584,63 @@ bool dc_add_plane_to_context(
free_pipe->top_pipe = tail_pipe->next_odm_pipe;
tail_pipe->next_odm_pipe->bottom_pipe = free_pipe;
} else if (free_pipe->plane_state &&
(free_pipe->plane_state->clip_rect.x >= free_pipe->stream->src.x + free_pipe->stream->src.width/2)
&& head_pipe->next_odm_pipe) {
/* For ODM + window MPO, support 3 plane ( 2 MPO ) case.
* Here we have a desktop ODM + left window MPO and a new MPO window appears
* on the right side only. It fails the first case, because tail_pipe is the
* left window MPO, so it has no next_odm_pipe. So in this scenario, we check
* for head_pipe->next_odm_pipe instead
*/
DC_LOG_SCALER("%s - ODM + win MPO (left) + win MPO (right). free_pipe:%d head_pipe->next_odm:%d\n",
__func__,
free_pipe->pipe_idx,
head_pipe->next_odm_pipe ? head_pipe->next_odm_pipe->pipe_idx : -1);
/*
* We want to avoid the case where the right side already has a pipe assigned to
* it and is different from free_pipe ( which would cause trigger a pipe
* reallocation ).
* Check the old context to see if the right side already has a pipe allocated
* - If not, continue to use free_pipe
* - If the right side already has a pipe, use that pipe instead if its available
*/
prev_right_head = &dc->current_state->res_ctx.pipe_ctx[head_pipe->next_odm_pipe->pipe_idx];
if ((prev_right_head->bottom_pipe) &&
(free_pipe->pipe_idx != prev_right_head->bottom_pipe->pipe_idx)) {
free_right_pipe = acquire_free_pipe_for_head(context, pool, head_pipe->next_odm_pipe);
if (free_right_pipe) {
free_pipe->stream = NULL;
memset(&free_pipe->stream_res, 0, sizeof(struct stream_resource));
memset(&free_pipe->plane_res, 0, sizeof(struct plane_resource));
free_pipe->plane_state = NULL;
free_pipe->pipe_idx = 0;
free_right_pipe->plane_state = plane_state;
free_pipe = free_right_pipe;
}
}
free_pipe->stream_res.tg = head_pipe->next_odm_pipe->stream_res.tg;
free_pipe->stream_res.abm = head_pipe->next_odm_pipe->stream_res.abm;
free_pipe->stream_res.opp = head_pipe->next_odm_pipe->stream_res.opp;
free_pipe->stream_res.stream_enc = head_pipe->next_odm_pipe->stream_res.stream_enc;
free_pipe->stream_res.audio = head_pipe->next_odm_pipe->stream_res.audio;
free_pipe->clock_source = head_pipe->next_odm_pipe->clock_source;
free_pipe->top_pipe = head_pipe->next_odm_pipe;
head_pipe->next_odm_pipe->bottom_pipe = free_pipe;
} else {
/* For ODM + window MPO, in 3 plane case, if we already have a MPO window on
* the left side, then we will invalidate a 2nd one on the left side
*/
if (head_pipe->next_odm_pipe && tail_pipe->top_pipe) {
dc_plane_state_release(plane_state);
return false;
}
free_pipe->stream_res.tg = tail_pipe->stream_res.tg;
free_pipe->stream_res.abm = tail_pipe->stream_res.abm;
free_pipe->stream_res.opp = tail_pipe->stream_res.opp;
......@@ -1564,21 +1651,28 @@ bool dc_add_plane_to_context(
free_pipe->top_pipe = tail_pipe;
tail_pipe->bottom_pipe = free_pipe;
if (!free_pipe->next_odm_pipe && tail_pipe->next_odm_pipe && tail_pipe->next_odm_pipe->bottom_pipe) {
free_pipe->next_odm_pipe = tail_pipe->next_odm_pipe->bottom_pipe;
tail_pipe->next_odm_pipe->bottom_pipe->prev_odm_pipe = free_pipe;
}
if (!free_pipe->prev_odm_pipe && tail_pipe->prev_odm_pipe && tail_pipe->prev_odm_pipe->bottom_pipe) {
free_pipe->prev_odm_pipe = tail_pipe->prev_odm_pipe->bottom_pipe;
tail_pipe->prev_odm_pipe->bottom_pipe->next_odm_pipe = free_pipe;
/* Connect MPO pipes together if MPO window is in the centre */
if (!(free_pipe->plane_state &&
(free_pipe->plane_state->clip_rect.x + free_pipe->plane_state->clip_rect.width <=
free_pipe->stream->src.x + free_pipe->stream->src.width/2))) {
if (!free_pipe->next_odm_pipe &&
tail_pipe->next_odm_pipe && tail_pipe->next_odm_pipe->bottom_pipe) {
free_pipe->next_odm_pipe = tail_pipe->next_odm_pipe->bottom_pipe;
tail_pipe->next_odm_pipe->bottom_pipe->prev_odm_pipe = free_pipe;
}
if (!free_pipe->prev_odm_pipe &&
tail_pipe->prev_odm_pipe && tail_pipe->prev_odm_pipe->bottom_pipe) {
free_pipe->prev_odm_pipe = tail_pipe->prev_odm_pipe->bottom_pipe;
tail_pipe->prev_odm_pipe->bottom_pipe->next_odm_pipe = free_pipe;
}
}
}
}
/* ODM + window MPO, where MPO window is on left half only */
if (free_pipe->plane_state &&
(free_pipe->plane_state->clip_rect.x + free_pipe->plane_state->clip_rect.width <=
free_pipe->stream->src.x + free_pipe->stream->src.width/2)) {
(free_pipe->plane_state->clip_rect.x + free_pipe->plane_state->clip_rect.width <=
free_pipe->stream->src.x + free_pipe->stream->src.width/2)) {
DC_LOG_SCALER("%s - ODM + window MPO(left). free_pipe:%d\n",
__func__,
free_pipe->pipe_idx);
......@@ -1586,7 +1680,7 @@ bool dc_add_plane_to_context(
}
/* ODM + window MPO, where MPO window is on right half only */
if (free_pipe->plane_state &&
(free_pipe->plane_state->clip_rect.x >= free_pipe->stream->src.x + free_pipe->stream->src.width/2)) {
(free_pipe->plane_state->clip_rect.x >= free_pipe->stream->src.x + free_pipe->stream->src.width/2)) {
DC_LOG_SCALER("%s - ODM + window MPO(right). free_pipe:%d\n",
__func__,
free_pipe->pipe_idx);
......
......@@ -1820,11 +1820,12 @@ int dcn32_populate_dml_pipes_from_context(
struct resource_context *res_ctx = &context->res_ctx;
struct pipe_ctx *pipe;
bool subvp_in_use = false, is_pipe_split_expected[MAX_PIPES];
int plane_count = 0;
struct dc_crtc_timing *timing;
dcn20_populate_dml_pipes_from_context(dc, context, pipes, fast_validate);
for (i = 0, pipe_cnt = 0; i < dc->res_pool->pipe_count; i++) {
struct dc_crtc_timing *timing;
if (!res_ctx->pipe_ctx[i].stream)
continue;
......@@ -1876,11 +1877,12 @@ int dcn32_populate_dml_pipes_from_context(
}
}
pipes[pipe_cnt].pipe.dest.odm_combine_policy = dm_odm_combine_policy_dal;
if (context->stream_count == 1) {
if (dc->debug.enable_single_display_2to1_odm_policy)
pipes[pipe_cnt].pipe.dest.odm_combine_policy = dm_odm_combine_policy_2to1;
}
/* Calculate the number of planes we have so we can determine
* whether to apply ODM 2to1 policy or not
*/
if (pipe->stream && !pipe->prev_odm_pipe &&
(!pipe->top_pipe || pipe->top_pipe->plane_state != pipe->plane_state))
++plane_count;
DC_FP_START();
is_pipe_split_expected[i] = dcn32_predict_pipe_split(context, pipes[i].pipe, i);
......@@ -1889,6 +1891,28 @@ int dcn32_populate_dml_pipes_from_context(
pipe_cnt++;
}
/* Determine whether we will apply ODM 2to1 policy
* Applies to single display and where the number of planes is less than 3
* For 3 plane case ( 2 MPO planes ), we will not set the policy for the MPO pipes
*/
for (i = 0, pipe_cnt = 0; i < dc->res_pool->pipe_count; i++) {
if (!res_ctx->pipe_ctx[i].stream)
continue;
pipe = &res_ctx->pipe_ctx[i];
timing = &pipe->stream->timing;
pipes[pipe_cnt].pipe.dest.odm_combine_policy = dm_odm_combine_policy_dal;
res_ctx->pipe_ctx[i].stream->odm_2to1_policy_applied = false;
if (context->stream_count == 1 && timing->dsc_cfg.num_slices_h != 1) {
if (dc->debug.enable_single_display_2to1_odm_policy) {
if (!((plane_count > 2) && pipe->top_pipe))
pipes[pipe_cnt].pipe.dest.odm_combine_policy = dm_odm_combine_policy_2to1;
}
res_ctx->pipe_ctx[i].stream->odm_2to1_policy_applied = true;
}
pipe_cnt++;
}
/* For DET allocation, we don't want to use DML policy (not optimal for utilizing all
* the DET available for each pipe). Use the DET override input to maintain our driver
* policy.
......@@ -1947,7 +1971,7 @@ static struct resource_funcs dcn32_res_pool_funcs = {
.validate_bandwidth = dcn32_validate_bandwidth,
.calculate_wm_and_dlg = dcn32_calculate_wm_and_dlg,
.populate_dml_pipes = dcn32_populate_dml_pipes_from_context,
.acquire_idle_pipe_for_layer = dcn20_acquire_idle_pipe_for_layer,
.acquire_idle_pipe_for_head_pipe_in_layer = dcn32_acquire_idle_pipe_for_head_pipe_in_layer,
.add_stream_to_ctx = dcn30_add_stream_to_ctx,
.add_dsc_to_stream_resource = dcn20_add_dsc_to_stream_resource,
.remove_stream_from_ctx = dcn20_remove_stream_from_ctx,
......@@ -1976,7 +2000,7 @@ static bool dcn32_resource_construct(
uint32_t pipe_fuses = 0;
uint32_t num_pipes = 4;
DC_FP_START();
DC_FP_START();
ctx->dc_bios->regs = &bios_regs;
......@@ -2316,13 +2340,13 @@ static bool dcn32_resource_construct(
pool->base.oem_device = NULL;
}
DC_FP_END();
DC_FP_END();
return true;
create_fail:
DC_FP_END();
DC_FP_END();
dcn32_resource_destruct(pool);
......@@ -2346,3 +2370,108 @@ struct resource_pool *dcn32_create_resource_pool(
kfree(pool);
return NULL;
}
static struct pipe_ctx *find_idle_secondary_pipe_check_mpo(
struct resource_context *res_ctx,
const struct resource_pool *pool,
const struct pipe_ctx *primary_pipe)
{
int i;
struct pipe_ctx *secondary_pipe = NULL;
struct pipe_ctx *next_odm_mpo_pipe = NULL;
int primary_index, preferred_pipe_idx;
struct pipe_ctx *old_primary_pipe = NULL;
/*
* Modified from find_idle_secondary_pipe
* With windowed MPO and ODM, we want to avoid the case where we want a
* free pipe for the left side but the free pipe is being used on the
* right side.
* Add check on current_state if the primary_pipe is the left side,
* to check the right side ( primary_pipe->next_odm_pipe ) to see if
* it is using a pipe for MPO ( primary_pipe->next_odm_pipe->bottom_pipe )
* - If so, then don't use this pipe
* EXCEPTION - 3 plane ( 2 MPO plane ) case
* - in this case, the primary pipe has already gotten a free pipe for the
* MPO window in the left
* - when it tries to get a free pipe for the MPO window on the right,
* it will see that it is already assigned to the right side
* ( primary_pipe->next_odm_pipe ). But in this case, we want this
* free pipe, since it will be for the right side. So add an
* additional condition, that skipping the free pipe on the right only
* applies if the primary pipe has no bottom pipe currently assigned
*/
if (primary_pipe) {
primary_index = primary_pipe->pipe_idx;
old_primary_pipe = &primary_pipe->stream->ctx->dc->current_state->res_ctx.pipe_ctx[primary_index];
if ((old_primary_pipe->next_odm_pipe) && (old_primary_pipe->next_odm_pipe->bottom_pipe)
&& (!primary_pipe->bottom_pipe))
next_odm_mpo_pipe = old_primary_pipe->next_odm_pipe->bottom_pipe;
preferred_pipe_idx = (pool->pipe_count - 1) - primary_pipe->pipe_idx;
if ((res_ctx->pipe_ctx[preferred_pipe_idx].stream == NULL) &&
!(next_odm_mpo_pipe && next_odm_mpo_pipe->pipe_idx == preferred_pipe_idx)) {
secondary_pipe = &res_ctx->pipe_ctx[preferred_pipe_idx];
secondary_pipe->pipe_idx = preferred_pipe_idx;
}
}
/*
* search backwards for the second pipe to keep pipe
* assignment more consistent
*/
if (!secondary_pipe)
for (i = pool->pipe_count - 1; i >= 0; i--) {
if ((res_ctx->pipe_ctx[i].stream == NULL) &&
!(next_odm_mpo_pipe && next_odm_mpo_pipe->pipe_idx == i)) {
secondary_pipe = &res_ctx->pipe_ctx[i];
secondary_pipe->pipe_idx = i;
break;
}
}
return secondary_pipe;
}
struct pipe_ctx *dcn32_acquire_idle_pipe_for_head_pipe_in_layer(
struct dc_state *state,
const struct resource_pool *pool,
struct dc_stream_state *stream,
struct pipe_ctx *head_pipe)
{
struct resource_context *res_ctx = &state->res_ctx;
struct pipe_ctx *idle_pipe, *pipe;
struct resource_context *old_ctx = &stream->ctx->dc->current_state->res_ctx;
int head_index;
if (!head_pipe)
ASSERT(0);
/*
* Modified from dcn20_acquire_idle_pipe_for_layer
* Check if head_pipe in old_context already has bottom_pipe allocated.
* - If so, check if that pipe is available in the current context.
* -- If so, reuse pipe from old_context
*/
head_index = head_pipe->pipe_idx;
pipe = &old_ctx->pipe_ctx[head_index];
if (pipe->bottom_pipe && res_ctx->pipe_ctx[pipe->bottom_pipe->pipe_idx].stream == NULL) {
idle_pipe = &res_ctx->pipe_ctx[pipe->bottom_pipe->pipe_idx];
idle_pipe->pipe_idx = pipe->bottom_pipe->pipe_idx;
} else {
idle_pipe = find_idle_secondary_pipe_check_mpo(res_ctx, pool, head_pipe);
if (!idle_pipe)
return NULL;
}
idle_pipe->stream = head_pipe->stream;
idle_pipe->stream_res.tg = head_pipe->stream_res.tg;
idle_pipe->stream_res.opp = head_pipe->stream_res.opp;
idle_pipe->plane_res.hubp = pool->hubps[idle_pipe->pipe_idx];
idle_pipe->plane_res.ipp = pool->ipps[idle_pipe->pipe_idx];
idle_pipe->plane_res.dpp = pool->dpps[idle_pipe->pipe_idx];
idle_pipe->plane_res.mpcc_inst = pool->dpps[idle_pipe->pipe_idx]->inst;
return idle_pipe;
}
......@@ -99,6 +99,12 @@ bool dcn32_subvp_in_use(struct dc *dc,
bool dcn32_mpo_in_use(struct dc_state *context);
struct pipe_ctx *dcn32_acquire_idle_pipe_for_head_pipe_in_layer(
struct dc_state *state,
const struct resource_pool *pool,
struct dc_stream_state *stream,
struct pipe_ctx *head_pipe);
void dcn32_determine_det_override(struct dc_state *context, display_e2e_pipe_params_st *pipes,
bool *is_pipe_split_expected, int pipe_cnt);
......
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