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

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
Loading
Loading
Loading
Loading
+115 −21
Original line number Diff line number Diff line
@@ -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++)
@@ -1517,6 +1518,14 @@ bool dc_add_plane_to_context(
				(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__,
						free_pipe->pipe_idx,
@@ -1530,9 +1539,32 @@ 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);
				} 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));
@@ -1542,7 +1574,6 @@ bool dc_add_plane_to_context(
					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;
@@ -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,16 +1651,23 @@ 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) {
				/* 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) {
					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 &&
+139 −10
Original line number Diff line number Diff line
@@ -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,
@@ -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;
}
+6 −0
Original line number Diff line number Diff line
@@ -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);