[PATCH 06/31] drm/amd/display: Fix two MPO videos in single display ODM combine mode

Alex Hung alex.hung at amd.com
Fri Jul 22 20:31:12 UTC 2022


From: Samson Tam <Samson.Tam at amd.com>

[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 at amd.com>
Acked-by: Alex Hung <alex.hung at amd.com>
Signed-off-by: Samson Tam <Samson.Tam at amd.com>
---
 .../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



More information about the amd-gfx mailing list