From: Samson Tam <Samson.Tam@xxxxxxx> [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: Alvin Lee <Alvin.Lee2@xxxxxxx> Acked-by: Alex Hung <alex.hung@xxxxxxx> Signed-off-by: Samson Tam <Samson.Tam@xxxxxxx> --- .../gpu/drm/amd/display/dc/core/dc_resource.c | 136 +++++++++++++--- .../drm/amd/display/dc/dcn32/dcn32_resource.c | 149 ++++++++++++++++-- .../drm/amd/display/dc/dcn32/dcn32_resource.h | 6 + 3 files changed, 260 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index 752ba4ab2b1e..ffc0f1c0ea93 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -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); diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c index e551d2936d03..314dec5712b5 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c @@ -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; +} diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.h b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.h index fc0fe48023a0..efd449804d7b 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.h +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.h @@ -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); -- 2.37.1