mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-02 15:43:35 -04:00
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: Jun Lei <Jun.Lei@amd.com> Acked-by: Wayne Lin <wayne.lin@amd.com> Signed-off-by: Alvin Lee <Alvin.Lee2@amd.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
next_odm_pipe = next_odm_pipe->next_odm_pipe;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
pipe_segments[i] = stream_segments / count;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,41 +286,92 @@ 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,
|
||||
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,
|
||||
/* Output */
|
||||
&pscl_throughput, &pscl_throughput_chroma,
|
||||
&dpp_clk_single_dpp);
|
||||
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_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);
|
||||
|
||||
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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user