Commit 06484134 authored by Alvin Lee's avatar Alvin Lee Committed by Alex Deucher
Browse files

drm/amd/display: Fix pipe split prediction



[Why & How]
- Pipe split prediction previously only took into
account MPC split. We must also consider when
ODM combine is required, and when we apply ODM
combine by policy.

- Also re-work DET allocation function as it wasn't
properly splitting the DET per stream, per plane.

Reviewed-by: default avatarJun Lei <Jun.Lei@amd.com>
Acked-by: default avatarWayne Lin <wayne.lin@amd.com>
Signed-off-by: default avatarAlvin Lee <Alvin.Lee2@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 710080a3
Loading
Loading
Loading
Loading
+27 −23
Original line number Diff line number Diff line
@@ -1850,12 +1850,36 @@ int dcn32_populate_dml_pipes_from_context(
	int i, pipe_cnt;
	struct resource_context *res_ctx = &context->res_ctx;
	struct pipe_ctx *pipe;
	bool subvp_in_use = false, is_pipe_split_expected[MAX_PIPES];
	bool subvp_in_use = false;
	uint8_t is_pipe_split_expected[MAX_PIPES] = {0};
	int plane_count = 0;
	struct dc_crtc_timing *timing;

	dcn20_populate_dml_pipes_from_context(dc, context, pipes, fast_validate);

	/* 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.
	 *
	 * Apply pipe split policy first so we can predict the pipe split correctly
	 * (dcn32_predict_pipe_split).
	 */
	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;
		if (context->stream_count == 1 && !dc_is_hdmi_signal(res_ctx->pipe_ctx[i].stream->signal)) {
			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;
			}
		}
		pipe_cnt++;
	}

	for (i = 0, pipe_cnt = 0; i < dc->res_pool->pipe_count; i++) {

		if (!res_ctx->pipe_ctx[i].stream)
@@ -1916,32 +1940,12 @@ int dcn32_populate_dml_pipes_from_context(
			++plane_count;

		DC_FP_START();
		is_pipe_split_expected[i] = dcn32_predict_pipe_split(context, pipes[i].pipe, i);
		is_pipe_split_expected[i] = dcn32_predict_pipe_split(context, &pipes[pipe_cnt]);
		DC_FP_END();

		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;
		if (context->stream_count == 1 && !dc_is_hdmi_signal(res_ctx->pipe_ctx[i].stream->signal)) {
			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;
			}
		}
		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.
@@ -1958,7 +1962,7 @@ int dcn32_populate_dml_pipes_from_context(
			}
		}
	} else
		dcn32_determine_det_override(context, pipes, is_pipe_split_expected, dc->res_pool->pipe_count);
		dcn32_determine_det_override(dc, context, pipes, is_pipe_split_expected);

	// In general cases we want to keep the dram clock change requirement
	// (prefer configs that support MCLK switch). Only override to false
+4 −2
Original line number Diff line number Diff line
@@ -109,8 +109,10 @@ struct pipe_ctx *dcn32_acquire_idle_pipe_for_head_pipe_in_layer(
		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);
void dcn32_determine_det_override(struct dc *dc,
		struct dc_state *context,
		display_e2e_pipe_params_st *pipes,
		uint8_t *is_pipe_split_expected);

/* definitions for run time init of reg offsets */

+89 −19
Original line number Diff line number Diff line
@@ -225,36 +225,106 @@ bool dcn32_mpo_in_use(struct dc_state *context)
	return false;
}

void dcn32_determine_det_override(struct dc_state *context, display_e2e_pipe_params_st *pipes,
		bool *is_pipe_split_expected, int pipe_cnt)
/**
 * *******************************************************************************************
 * dcn32_determine_det_override: Determine DET allocation for each pipe
 *
 * This function determines how much DET to allocate for each pipe. The total number of
 * DET segments will be split equally among each of the streams, and after that the DET
 * segments per stream will be split equally among the planes for the given stream.
 *
 * If there is a plane that's driven by more than 1 pipe (i.e. pipe split), then the
 * number of DET for that given plane will be split among the pipes driving that plane.
 *
 * The pipe split prediction (is_pipe_split_expected) has to work 100% of the time in
 * order for this function to work properly.
 *
 * High level algorithm:
 * 1. Split total DET among number of streams
 * 2. For each stream, split DET among the planes
 * 3. For each plane, check if pipe split is expected. If yes, split the DET for that plane
 *    among the number of splits we expect (i.e. 2 [2:1] or 4 [4:1])
 *    - NOTE: Make sure not to double count the pipe splits (i.e. the pipes could
 *            already be split in the context).
 * 4. Assign the DET override to the DML pipes.
 *
 * @param [in]: dc: Current DC state
 * @param [in]: context: New DC state to be programmed
 * @param [in]: pipes: Array of DML pipes
 * @param [in]: is_pipe_split_expected: Array indicating pipe split prediction for each pipe
 *
 * @return: void
 *
 * *******************************************************************************************
 */
void dcn32_determine_det_override(struct dc *dc,
		struct dc_state *context,
		display_e2e_pipe_params_st *pipes,
		uint8_t *is_pipe_split_expected)
{
	int i, j, count, stream_segments, pipe_segments[MAX_PIPES];
	uint8_t i, j, pipe_plane_count, stream_segments, plane_segments, pipe_segments[MAX_PIPES] = {0};
	uint8_t pipe_counted[MAX_PIPES] = {0};
	uint8_t pipe_cnt = 0;
	struct dc_plane_state *current_plane = NULL;
	struct pipe_ctx *next_odm_pipe = NULL;
	struct pipe_ctx *bottom_pipe = NULL;

	if (context->stream_count > 0) {
		stream_segments = 18 / context->stream_count;
		for (i = 0; i < context->stream_count; i++) {
			count = 0;
			for (j = 0; j < pipe_cnt; j++) {
				if (context->res_ctx.pipe_ctx[j].stream == context->streams[i]) {
					count++;
					if (is_pipe_split_expected[j])
						count++;
				}
			if (context->stream_status[i].plane_count > 0)
				plane_segments = stream_segments / context->stream_status[i].plane_count;
			else
				plane_segments = stream_segments;
			for (j = 0; j < dc->res_pool->pipe_count; j++) {
				pipe_plane_count = 0;
				if (context->res_ctx.pipe_ctx[j].stream == context->streams[i] &&
						pipe_counted[j] != 1) {
					/* Note: pipe_plane_count indicates the number of pipes to be used for a
					 * given plane. e.g. pipe_plane_count = 1 means single pipe (i.e. not split),
					 * pipe_plane_count = 2 means 2:1 split, etc.
					 */
					pipe_plane_count++;
					pipe_counted[j] = 1;
					current_plane = context->res_ctx.pipe_ctx[j].plane_state;
					if (is_pipe_split_expected[j] != 0) {
						pipe_plane_count += is_pipe_split_expected[j];

						next_odm_pipe = context->res_ctx.pipe_ctx[j].next_odm_pipe;
						bottom_pipe = context->res_ctx.pipe_ctx[j].bottom_pipe;

						/* If pipe already happens to be split in context, mark as already
						 * counted so we don't double count the pipe split.
						 */
						while (next_odm_pipe) {
							if (next_odm_pipe->plane_state == current_plane) {
								pipe_counted[next_odm_pipe->pipe_idx] = 1;
								pipe_segments[next_odm_pipe->pipe_idx] = plane_segments / pipe_plane_count;
							}
			pipe_segments[i] = stream_segments / count;
							next_odm_pipe = next_odm_pipe->next_odm_pipe;
						}

		for (i = 0; i < pipe_cnt; i++) {
			pipes[i].pipe.src.det_size_override = 0;
			for (j = 0; j < context->stream_count; j++) {
				if (context->res_ctx.pipe_ctx[i].stream == context->streams[j]) {
					pipes[i].pipe.src.det_size_override = pipe_segments[j] * DCN3_2_DET_SEG_SIZE;
					break;
						while (bottom_pipe) {
							if (bottom_pipe->plane_state == current_plane) {
								pipe_counted[bottom_pipe->pipe_idx] = 1;
								pipe_segments[bottom_pipe->pipe_idx] = plane_segments / pipe_plane_count;
							}
							bottom_pipe = bottom_pipe->bottom_pipe;
						}
					}
					pipe_segments[j] = plane_segments / pipe_plane_count;
				}
			}
		}

		for (i = 0, pipe_cnt = 0; i < dc->res_pool->pipe_count; i++) {
			if (!context->res_ctx.pipe_ctx[i].stream)
				continue;
			pipes[pipe_cnt].pipe.src.det_size_override = pipe_segments[i] * DCN3_2_DET_SEG_SIZE;
			pipe_cnt++;
		}
	} else {
		for (i = 0; i < pipe_cnt; i++)
		for (i = 0; i < dc->res_pool->pipe_count; i++)
			pipes[i].pipe.src.det_size_override = 4 * DCN3_2_DET_SEG_SIZE; //DCN3_2_DEFAULT_DET_SIZE
	}
}
+72 −21
Original line number Diff line number Diff line
@@ -286,28 +286,75 @@ void dcn32_helper_populate_phantom_dlg_params(struct dc *dc,
	}
}

bool dcn32_predict_pipe_split(struct dc_state *context, display_pipe_params_st pipe, int index)
/**
 * *******************************************************************************************
 * dcn32_predict_pipe_split: Predict if pipe split will occur for a given DML pipe
 *
 * This function takes in a DML pipe (pipe_e2e) and predicts if pipe split is required (both
 * ODM and MPC). For pipe split, ODM combine is determined by the ODM mode, and MPC combine is
 * determined by DPPClk requirements
 *
 * This function follows the same policy as DML:
 * - Check for ODM combine requirements / policy first
 * - MPC combine is only chosen if there is no ODM combine requirements / policy in place, and
 *   MPC is required
 *
 * @param [in]: context: New DC state to be programmed
 * @param [in]: pipe_e2e: DML pipe end to end context
 *
 * @return: Number of splits expected (1 for 2:1 split, 3 for 4:1 split, 0 for no splits).
 *
 * *******************************************************************************************
 */
uint8_t dcn32_predict_pipe_split(struct dc_state *context,
				  display_e2e_pipe_params_st *pipe_e2e)
{
	double pscl_throughput;
	double pscl_throughput_chroma;
	double dpp_clk_single_dpp, clock;
	double clk_frequency = 0.0;
	double vco_speed = context->bw_ctx.dml.soc.dispclk_dppclk_vco_speed_mhz;
	bool total_available_pipes_support = false;
	uint32_t number_of_dpp = 0;
	enum odm_combine_mode odm_mode = dm_odm_combine_mode_disabled;
	double req_dispclk_per_surface = 0;
	uint8_t num_splits = 0;

	dc_assert_fp_enabled();

	dml32_CalculateSinglePipeDPPCLKAndSCLThroughput(pipe.scale_ratio_depth.hscl_ratio,
							pipe.scale_ratio_depth.hscl_ratio_c,
							pipe.scale_ratio_depth.vscl_ratio,
							pipe.scale_ratio_depth.vscl_ratio_c,
	dml32_CalculateODMMode(context->bw_ctx.dml.ip.maximum_pixels_per_line_per_dsc_unit,
			pipe_e2e->pipe.dest.hactive,
			pipe_e2e->dout.output_format,
			pipe_e2e->dout.output_type,
			pipe_e2e->pipe.dest.odm_combine_policy,
			context->bw_ctx.dml.soc.clock_limits[context->bw_ctx.dml.soc.num_states - 1].dispclk_mhz,
			context->bw_ctx.dml.soc.clock_limits[context->bw_ctx.dml.soc.num_states - 1].dispclk_mhz,
			pipe_e2e->dout.dsc_enable != 0,
			0, /* TotalNumberOfActiveDPP can be 0 since we're predicting pipe split requirement */
			context->bw_ctx.dml.ip.max_num_dpp,
			pipe_e2e->pipe.dest.pixel_rate_mhz,
			context->bw_ctx.dml.soc.dcn_downspread_percent,
			context->bw_ctx.dml.ip.dispclk_ramp_margin_percent,
			context->bw_ctx.dml.soc.dispclk_dppclk_vco_speed_mhz,
			pipe_e2e->dout.dsc_slices,
			/* Output */
			&total_available_pipes_support,
			&number_of_dpp,
			&odm_mode,
			&req_dispclk_per_surface);

	dml32_CalculateSinglePipeDPPCLKAndSCLThroughput(pipe_e2e->pipe.scale_ratio_depth.hscl_ratio,
			pipe_e2e->pipe.scale_ratio_depth.hscl_ratio_c,
			pipe_e2e->pipe.scale_ratio_depth.vscl_ratio,
			pipe_e2e->pipe.scale_ratio_depth.vscl_ratio_c,
			context->bw_ctx.dml.ip.max_dchub_pscl_bw_pix_per_clk,
			context->bw_ctx.dml.ip.max_pscl_lb_bw_pix_per_clk,
							pipe.dest.pixel_rate_mhz,
							pipe.src.source_format,
							pipe.scale_taps.htaps,
							pipe.scale_taps.htaps_c,
							pipe.scale_taps.vtaps,
							pipe.scale_taps.vtaps_c,
			pipe_e2e->pipe.dest.pixel_rate_mhz,
			pipe_e2e->pipe.src.source_format,
			pipe_e2e->pipe.scale_taps.htaps,
			pipe_e2e->pipe.scale_taps.htaps_c,
			pipe_e2e->pipe.scale_taps.vtaps,
			pipe_e2e->pipe.scale_taps.vtaps_c,
			/* Output */
			&pscl_throughput, &pscl_throughput_chroma,
			&dpp_clk_single_dpp);
@@ -315,12 +362,16 @@ bool dcn32_predict_pipe_split(struct dc_state *context, display_pipe_params_st p
	clock = dpp_clk_single_dpp * (1 + context->bw_ctx.dml.soc.dcn_downspread_percent / 100);

	if (clock > 0)
		clk_frequency = vco_speed * 4.0 / ((int)(vco_speed * 4.0));
		clk_frequency = vco_speed * 4.0 / ((int)(vco_speed * 4.0) / clock);

	if (clk_frequency > context->bw_ctx.dml.soc.clock_limits[index].dppclk_mhz)
		return true;
	else
		return false;
	if (odm_mode == dm_odm_combine_mode_2to1)
		num_splits = 1;
	else if (odm_mode == dm_odm_combine_mode_4to1)
		num_splits = 3;
	else if (clk_frequency > context->bw_ctx.dml.soc.clock_limits[context->bw_ctx.dml.soc.num_states - 1].dppclk_mhz)
		num_splits = 1;

	return num_splits;
}

static float calculate_net_bw_in_kbytes_sec(struct _vcs_dpi_voltage_scaling_st *entry)
+2 −3
Original line number Diff line number Diff line
@@ -41,9 +41,8 @@ void dcn32_helper_populate_phantom_dlg_params(struct dc *dc,
					      display_e2e_pipe_params_st *pipes,
					      int pipe_cnt);

bool dcn32_predict_pipe_split(struct dc_state *context,
			      display_pipe_params_st pipe,
			      int index);
uint8_t dcn32_predict_pipe_split(struct dc_state *context,
				  display_e2e_pipe_params_st *pipe_e2e);

void insert_entry_into_table_sorted(struct _vcs_dpi_voltage_scaling_st *table,
				    unsigned int *num_entries,