diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c')
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c | 819 |
1 files changed, 583 insertions, 236 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c index cf3b400c86..fe2b67d745 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c @@ -41,7 +41,8 @@ static const struct subvp_high_refresh_list subvp_high_refresh_list = { .res = { {.width = 3840, .height = 2160, }, {.width = 3440, .height = 1440, }, - {.width = 2560, .height = 1440, }}, + {.width = 2560, .height = 1440, }, + {.width = 1920, .height = 1080, }}, }; struct _vcs_dpi_ip_params_st dcn3_2_ip = { @@ -347,90 +348,6 @@ void dcn32_helper_populate_phantom_dlg_params(struct dc *dc, } } -/** - * dcn32_predict_pipe_split - Predict if pipe split will occur for a given DML pipe - * @context: [in] New DC state to be programmed - * @pipe_e2e: [in] DML pipe end to end context - * - * 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 - * - * 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_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) / clock); - - 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) { float memory_bw_kbytes_sec; @@ -905,7 +822,7 @@ static bool subvp_drr_schedulable(struct dc *dc, struct dc_state *context) continue; if (drr_pipe->stream->mall_stream_config.type == SUBVP_NONE && drr_pipe->stream->ignore_msa_timing_param && - (drr_pipe->stream->allow_freesync || drr_pipe->stream->vrr_active_variable)) + (drr_pipe->stream->allow_freesync || drr_pipe->stream->vrr_active_variable || drr_pipe->stream->vrr_active_fixed)) break; } @@ -1144,6 +1061,341 @@ static bool subvp_validate_static_schedulability(struct dc *dc, return schedulable; } +static void assign_subvp_index(struct dc *dc, struct dc_state *context) +{ + int i; + int index = 0; + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (resource_is_pipe_type(pipe_ctx, OTG_MASTER) && + pipe_ctx->stream->mall_stream_config.type == SUBVP_MAIN) { + pipe_ctx->subvp_index = index++; + } else { + pipe_ctx->subvp_index = 0; + } + } +} + +struct pipe_slice_table { + struct { + struct dc_stream_state *stream; + int slice_count; + } odm_combines[MAX_STREAMS]; + int odm_combine_count; + + struct { + struct pipe_ctx *pri_pipe; + struct dc_plane_state *plane; + int slice_count; + } mpc_combines[MAX_PLANES]; + int mpc_combine_count; +}; + + +static void update_slice_table_for_stream(struct pipe_slice_table *table, + struct dc_stream_state *stream, int diff) +{ + int i; + + for (i = 0; i < table->odm_combine_count; i++) { + if (table->odm_combines[i].stream == stream) { + table->odm_combines[i].slice_count += diff; + break; + } + } + + if (i == table->odm_combine_count) { + table->odm_combine_count++; + table->odm_combines[i].stream = stream; + table->odm_combines[i].slice_count = diff; + } +} + +static void update_slice_table_for_plane(struct pipe_slice_table *table, + struct pipe_ctx *dpp_pipe, struct dc_plane_state *plane, int diff) +{ + int i; + struct pipe_ctx *pri_dpp_pipe = resource_get_primary_dpp_pipe(dpp_pipe); + + for (i = 0; i < table->mpc_combine_count; i++) { + if (table->mpc_combines[i].plane == plane && + table->mpc_combines[i].pri_pipe == pri_dpp_pipe) { + table->mpc_combines[i].slice_count += diff; + break; + } + } + + if (i == table->mpc_combine_count) { + table->mpc_combine_count++; + table->mpc_combines[i].plane = plane; + table->mpc_combines[i].pri_pipe = pri_dpp_pipe; + table->mpc_combines[i].slice_count = diff; + } +} + +static void init_pipe_slice_table_from_context( + struct pipe_slice_table *table, + struct dc_state *context) +{ + int i, j; + struct pipe_ctx *otg_master; + struct pipe_ctx *dpp_pipes[MAX_PIPES]; + struct dc_stream_state *stream; + int count; + + memset(table, 0, sizeof(*table)); + + for (i = 0; i < context->stream_count; i++) { + stream = context->streams[i]; + otg_master = resource_get_otg_master_for_stream( + &context->res_ctx, stream); + count = resource_get_odm_slice_count(otg_master); + update_slice_table_for_stream(table, stream, count); + + count = resource_get_dpp_pipes_for_opp_head(otg_master, + &context->res_ctx, dpp_pipes); + for (j = 0; j < count; j++) + if (dpp_pipes[j]->plane_state) + update_slice_table_for_plane(table, dpp_pipes[j], + dpp_pipes[j]->plane_state, 1); + } +} + +static bool update_pipe_slice_table_with_split_flags( + struct pipe_slice_table *table, + struct dc *dc, + struct dc_state *context, + struct vba_vars_st *vba, + int split[MAX_PIPES], + bool merge[MAX_PIPES]) +{ + /* NOTE: we are deprecating the support for the concept of pipe splitting + * or pipe merging. Instead we append slices to the end and remove + * slices from the end. The following code converts a pipe split or + * merge to an append or remove operation. + * + * For example: + * When split flags describe the following pipe connection transition + * + * from: + * pipe 0 (split=2) -> pipe 1 (split=2) + * to: (old behavior) + * pipe 0 -> pipe 2 -> pipe 1 -> pipe 3 + * + * the code below actually does: + * pipe 0 -> pipe 1 -> pipe 2 -> pipe 3 + * + * This is the new intended behavior and for future DCNs we will retire + * the old concept completely. + */ + struct pipe_ctx *pipe; + bool odm; + int dc_pipe_idx, dml_pipe_idx = 0; + bool updated = false; + + for (dc_pipe_idx = 0; + dc_pipe_idx < dc->res_pool->pipe_count; dc_pipe_idx++) { + pipe = &context->res_ctx.pipe_ctx[dc_pipe_idx]; + if (resource_is_pipe_type(pipe, FREE_PIPE)) + continue; + + if (merge[dc_pipe_idx]) { + if (resource_is_pipe_type(pipe, OPP_HEAD)) + /* merging OPP head means reducing ODM slice + * count by 1 + */ + update_slice_table_for_stream(table, pipe->stream, -1); + else if (resource_is_pipe_type(pipe, DPP_PIPE) && + resource_get_odm_slice_index(resource_get_opp_head(pipe)) == 0) + /* merging DPP pipe of the first ODM slice means + * reducing MPC slice count by 1 + */ + update_slice_table_for_plane(table, pipe, pipe->plane_state, -1); + updated = true; + } + + if (split[dc_pipe_idx]) { + odm = vba->ODMCombineEnabled[vba->pipe_plane[dml_pipe_idx]] != + dm_odm_combine_mode_disabled; + if (odm && resource_is_pipe_type(pipe, OPP_HEAD)) + update_slice_table_for_stream( + table, pipe->stream, split[dc_pipe_idx] - 1); + else if (!odm && resource_is_pipe_type(pipe, DPP_PIPE)) + update_slice_table_for_plane(table, pipe, + pipe->plane_state, split[dc_pipe_idx] - 1); + updated = true; + } + dml_pipe_idx++; + } + return updated; +} + +static void update_pipes_with_slice_table(struct dc *dc, struct dc_state *context, + struct pipe_slice_table *table) +{ + int i; + + for (i = 0; i < table->odm_combine_count; i++) + resource_update_pipes_for_stream_with_slice_count(context, + dc->current_state, dc->res_pool, + table->odm_combines[i].stream, + table->odm_combines[i].slice_count); + + for (i = 0; i < table->mpc_combine_count; i++) + resource_update_pipes_for_plane_with_slice_count(context, + dc->current_state, dc->res_pool, + table->mpc_combines[i].plane, + table->mpc_combines[i].slice_count); +} + +static bool update_pipes_with_split_flags(struct dc *dc, struct dc_state *context, + struct vba_vars_st *vba, int split[MAX_PIPES], + bool merge[MAX_PIPES]) +{ + struct pipe_slice_table slice_table; + bool updated; + + init_pipe_slice_table_from_context(&slice_table, context); + updated = update_pipe_slice_table_with_split_flags( + &slice_table, dc, context, vba, + split, merge); + update_pipes_with_slice_table(dc, context, &slice_table); + return updated; +} + +static bool should_allow_odm_power_optimization(struct dc *dc, + struct dc_state *context, struct vba_vars_st *v, int *split, + bool *merge) +{ + struct dc_stream_state *stream = context->streams[0]; + struct pipe_slice_table slice_table; + int i; + + /* + * this debug flag allows us to disable ODM power optimization feature + * unconditionally. we force the feature off if this is set to false. + */ + if (!dc->debug.enable_single_display_2to1_odm_policy) + return false; + + /* current design and test coverage is only limited to allow ODM power + * optimization for single stream. Supporting it for multiple streams + * use case would require additional algorithm to decide how to + * optimize power consumption when there are not enough free pipes to + * allocate for all the streams. This level of optimization would + * require multiple attempts of revalidation to make an optimized + * decision. Unfortunately We do not support revalidation flow in + * current version of DML. + */ + if (context->stream_count != 1) + return false; + + /* + * Our hardware doesn't support ODM for HDMI TMDS + */ + if (dc_is_hdmi_signal(stream->signal)) + return false; + + /* + * ODM Combine 2:1 requires horizontal timing divisible by 2 so each + * ODM segment has the same size. + */ + if (!is_h_timing_divisible_by_2(stream)) + return false; + + /* + * No power benefits if the timing's pixel clock is not high enough to + * raise display clock from minimum power state. + */ + if (stream->timing.pix_clk_100hz * 100 <= DCN3_2_VMIN_DISPCLK_HZ) + return false; + + if (dc->config.enable_windowed_mpo_odm) { + /* + * ODM power optimization should only be allowed if the feature + * can be seamlessly toggled off within an update. This would + * require that the feature is applied on top of a minimal + * state. A minimal state is defined as a state validated + * without the need of pipe split. Therefore, when transition to + * toggle the feature off, the same stream and plane + * configuration can be supported by the pipe resource in the + * first ODM slice alone without the need to acquire extra + * resources. + */ + init_pipe_slice_table_from_context(&slice_table, context); + update_pipe_slice_table_with_split_flags( + &slice_table, dc, context, v, + split, merge); + for (i = 0; i < slice_table.mpc_combine_count; i++) + if (slice_table.mpc_combines[i].slice_count > 1) + return false; + + for (i = 0; i < slice_table.odm_combine_count; i++) + if (slice_table.odm_combines[i].slice_count > 1) + return false; + } else { + /* + * the new ODM power optimization feature reduces software + * design limitation and allows ODM power optimization to be + * supported even with presence of overlay planes. The new + * feature is enabled based on enable_windowed_mpo_odm flag. If + * the flag is not set, we limit our feature scope due to + * previous software design limitation + */ + if (context->stream_status[0].plane_count != 1) + return false; + + if (memcmp(&context->stream_status[0].plane_states[0]->clip_rect, + &stream->src, sizeof(struct rect)) != 0) + return false; + + if (stream->src.width >= 5120 && + stream->src.width > stream->dst.width) + return false; + } + return true; +} + +static void try_odm_power_optimization_and_revalidate( + struct dc *dc, + struct dc_state *context, + display_e2e_pipe_params_st *pipes, + int *split, + bool *merge, + unsigned int *vlevel, + int pipe_cnt) +{ + int i; + unsigned int new_vlevel; + + for (i = 0; i < pipe_cnt; i++) + pipes[i].pipe.dest.odm_combine_policy = dm_odm_combine_policy_2to1; + + new_vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, pipe_cnt); + + if (new_vlevel < context->bw_ctx.dml.soc.num_states) { + memset(split, 0, MAX_PIPES * sizeof(int)); + memset(merge, 0, MAX_PIPES * sizeof(bool)); + *vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, new_vlevel, split, merge); + context->bw_ctx.dml.vba.VoltageLevel = *vlevel; + } +} + +static bool is_test_pattern_enabled( + struct dc_state *context) +{ + int i; + + for (i = 0; i < context->stream_count; i++) { + if (context->streams[i]->test_pattern.type != DP_TEST_PATTERN_VIDEO_MODE) + return true; + } + + return false; +} + static void dcn32_full_validate_bw_helper(struct dc *dc, struct dc_state *context, display_e2e_pipe_params_st *pipes, @@ -1187,7 +1439,7 @@ static void dcn32_full_validate_bw_helper(struct dc *dc, * 5. (Config doesn't support MCLK in VACTIVE/VBLANK || dc->debug.force_subvp_mclk_switch) */ if (!dc->debug.force_disable_subvp && !dc->caps.dmub_caps.gecc_enable && dcn32_all_pipes_have_stream_and_plane(dc, context) && - !dcn32_mpo_in_use(context) && !dcn32_any_surfaces_rotated(dc, context) && + !dcn32_mpo_in_use(context) && !dcn32_any_surfaces_rotated(dc, context) && !is_test_pattern_enabled(context) && (*vlevel == context->bw_ctx.dml.soc.num_states || vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] == dm_dram_clock_change_unsupported || dc->debug.force_subvp_mclk_switch)) { @@ -1294,8 +1546,14 @@ static void dcn32_full_validate_bw_helper(struct dc *dc, vba->VoltageLevel = *vlevel; // Note: We can't apply the phantom pipes to hardware at this time. We have to wait // until driver has acquired the DMCUB lock to do it safely. + assign_subvp_index(dc, context); } } + + if (should_allow_odm_power_optimization(dc, context, vba, split, merge)) + try_odm_power_optimization_and_revalidate( + dc, context, pipes, split, merge, vlevel, *pipe_cnt); + } static bool is_dtbclk_required(struct dc *dc, struct dc_state *context) @@ -1730,173 +1988,181 @@ bool dcn32_internal_validate_bw(struct dc *dc, pipe_idx++; } - /* merge pipes if necessary */ - for (i = 0; i < dc->res_pool->pipe_count; i++) { - struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; + if (dc->config.enable_windowed_mpo_odm) { + repopulate_pipes = update_pipes_with_split_flags( + dc, context, vba, split, merge); + } else { + /* the code below will be removed once windowed mpo odm is fully + * enabled. + */ + /* merge pipes if necessary */ + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; - /*skip pipes that don't need merging*/ - if (!merge[i]) - continue; + /*skip pipes that don't need merging*/ + if (!merge[i]) + continue; - /* if ODM merge we ignore mpc tree, mpo pipes will have their own flags */ - if (pipe->prev_odm_pipe) { - /*split off odm pipe*/ - pipe->prev_odm_pipe->next_odm_pipe = pipe->next_odm_pipe; - if (pipe->next_odm_pipe) - pipe->next_odm_pipe->prev_odm_pipe = pipe->prev_odm_pipe; - - /*2:1ODM+MPC Split MPO to Single Pipe + MPC Split MPO*/ - if (pipe->bottom_pipe) { - if (pipe->bottom_pipe->prev_odm_pipe || pipe->bottom_pipe->next_odm_pipe) { - /*MPC split rules will handle this case*/ - pipe->bottom_pipe->top_pipe = NULL; - } else { - /* when merging an ODM pipes, the bottom MPC pipe must now point to - * the previous ODM pipe and its associated stream assets - */ - if (pipe->prev_odm_pipe->bottom_pipe) { - /* 3 plane MPO*/ - pipe->bottom_pipe->top_pipe = pipe->prev_odm_pipe->bottom_pipe; - pipe->prev_odm_pipe->bottom_pipe->bottom_pipe = pipe->bottom_pipe; + /* if ODM merge we ignore mpc tree, mpo pipes will have their own flags */ + if (pipe->prev_odm_pipe) { + /*split off odm pipe*/ + pipe->prev_odm_pipe->next_odm_pipe = pipe->next_odm_pipe; + if (pipe->next_odm_pipe) + pipe->next_odm_pipe->prev_odm_pipe = pipe->prev_odm_pipe; + + /*2:1ODM+MPC Split MPO to Single Pipe + MPC Split MPO*/ + if (pipe->bottom_pipe) { + if (pipe->bottom_pipe->prev_odm_pipe || pipe->bottom_pipe->next_odm_pipe) { + /*MPC split rules will handle this case*/ + pipe->bottom_pipe->top_pipe = NULL; } else { - /* 2 plane MPO*/ - pipe->bottom_pipe->top_pipe = pipe->prev_odm_pipe; - pipe->prev_odm_pipe->bottom_pipe = pipe->bottom_pipe; + /* when merging an ODM pipes, the bottom MPC pipe must now point to + * the previous ODM pipe and its associated stream assets + */ + if (pipe->prev_odm_pipe->bottom_pipe) { + /* 3 plane MPO*/ + pipe->bottom_pipe->top_pipe = pipe->prev_odm_pipe->bottom_pipe; + pipe->prev_odm_pipe->bottom_pipe->bottom_pipe = pipe->bottom_pipe; + } else { + /* 2 plane MPO*/ + pipe->bottom_pipe->top_pipe = pipe->prev_odm_pipe; + pipe->prev_odm_pipe->bottom_pipe = pipe->bottom_pipe; + } + + memcpy(&pipe->bottom_pipe->stream_res, &pipe->bottom_pipe->top_pipe->stream_res, sizeof(struct stream_resource)); } + } - memcpy(&pipe->bottom_pipe->stream_res, &pipe->bottom_pipe->top_pipe->stream_res, sizeof(struct stream_resource)); + if (pipe->top_pipe) { + pipe->top_pipe->bottom_pipe = NULL; } - } - if (pipe->top_pipe) { - pipe->top_pipe->bottom_pipe = NULL; - } + pipe->bottom_pipe = NULL; + pipe->next_odm_pipe = NULL; + pipe->plane_state = NULL; + pipe->stream = NULL; + pipe->top_pipe = NULL; + pipe->prev_odm_pipe = NULL; + if (pipe->stream_res.dsc) + dcn20_release_dsc(&context->res_ctx, dc->res_pool, &pipe->stream_res.dsc); + memset(&pipe->plane_res, 0, sizeof(pipe->plane_res)); + memset(&pipe->stream_res, 0, sizeof(pipe->stream_res)); + memset(&pipe->link_res, 0, sizeof(pipe->link_res)); + repopulate_pipes = true; + } else if (pipe->top_pipe && pipe->top_pipe->plane_state == pipe->plane_state) { + struct pipe_ctx *top_pipe = pipe->top_pipe; + struct pipe_ctx *bottom_pipe = pipe->bottom_pipe; + + top_pipe->bottom_pipe = bottom_pipe; + if (bottom_pipe) + bottom_pipe->top_pipe = top_pipe; + + pipe->top_pipe = NULL; + pipe->bottom_pipe = NULL; + pipe->plane_state = NULL; + pipe->stream = NULL; + memset(&pipe->plane_res, 0, sizeof(pipe->plane_res)); + memset(&pipe->stream_res, 0, sizeof(pipe->stream_res)); + memset(&pipe->link_res, 0, sizeof(pipe->link_res)); + repopulate_pipes = true; + } else + ASSERT(0); /* Should never try to merge master pipe */ - pipe->bottom_pipe = NULL; - pipe->next_odm_pipe = NULL; - pipe->plane_state = NULL; - pipe->stream = NULL; - pipe->top_pipe = NULL; - pipe->prev_odm_pipe = NULL; - if (pipe->stream_res.dsc) - dcn20_release_dsc(&context->res_ctx, dc->res_pool, &pipe->stream_res.dsc); - memset(&pipe->plane_res, 0, sizeof(pipe->plane_res)); - memset(&pipe->stream_res, 0, sizeof(pipe->stream_res)); - memset(&pipe->link_res, 0, sizeof(pipe->link_res)); - repopulate_pipes = true; - } else if (pipe->top_pipe && pipe->top_pipe->plane_state == pipe->plane_state) { - struct pipe_ctx *top_pipe = pipe->top_pipe; - struct pipe_ctx *bottom_pipe = pipe->bottom_pipe; - - top_pipe->bottom_pipe = bottom_pipe; - if (bottom_pipe) - bottom_pipe->top_pipe = top_pipe; - - pipe->top_pipe = NULL; - pipe->bottom_pipe = NULL; - pipe->plane_state = NULL; - pipe->stream = NULL; - memset(&pipe->plane_res, 0, sizeof(pipe->plane_res)); - memset(&pipe->stream_res, 0, sizeof(pipe->stream_res)); - memset(&pipe->link_res, 0, sizeof(pipe->link_res)); - repopulate_pipes = true; - } else - ASSERT(0); /* Should never try to merge master pipe */ - - } - - for (i = 0, pipe_idx = -1; i < dc->res_pool->pipe_count; i++) { - struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; - struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i]; - struct pipe_ctx *hsplit_pipe = NULL; - bool odm; - int old_index = -1; + } - if (!pipe->stream || newly_split[i]) - continue; + for (i = 0, pipe_idx = -1; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; + struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i]; + struct pipe_ctx *hsplit_pipe = NULL; + bool odm; + int old_index = -1; - pipe_idx++; - odm = vba->ODMCombineEnabled[vba->pipe_plane[pipe_idx]] != dm_odm_combine_mode_disabled; + if (!pipe->stream || newly_split[i]) + continue; - if (!pipe->plane_state && !odm) - continue; + pipe_idx++; + odm = vba->ODMCombineEnabled[vba->pipe_plane[pipe_idx]] != dm_odm_combine_mode_disabled; - if (split[i]) { - if (odm) { - if (split[i] == 4 && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe) - old_index = old_pipe->next_odm_pipe->next_odm_pipe->pipe_idx; - else if (old_pipe->next_odm_pipe) + if (!pipe->plane_state && !odm) + continue; + + if (split[i]) { + if (odm) { + if (split[i] == 4 && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe) + old_index = old_pipe->next_odm_pipe->next_odm_pipe->pipe_idx; + else if (old_pipe->next_odm_pipe) + old_index = old_pipe->next_odm_pipe->pipe_idx; + } else { + if (split[i] == 4 && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe && + old_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state) + old_index = old_pipe->bottom_pipe->bottom_pipe->pipe_idx; + else if (old_pipe->bottom_pipe && + old_pipe->bottom_pipe->plane_state == old_pipe->plane_state) + old_index = old_pipe->bottom_pipe->pipe_idx; + } + hsplit_pipe = dcn32_find_split_pipe(dc, context, old_index); + ASSERT(hsplit_pipe); + if (!hsplit_pipe) + goto validate_fail; + + if (!dcn32_split_stream_for_mpc_or_odm( + dc, &context->res_ctx, + pipe, hsplit_pipe, odm)) + goto validate_fail; + + newly_split[hsplit_pipe->pipe_idx] = true; + repopulate_pipes = true; + } + if (split[i] == 4) { + struct pipe_ctx *pipe_4to1; + + if (odm && old_pipe->next_odm_pipe) old_index = old_pipe->next_odm_pipe->pipe_idx; - } else { - if (split[i] == 4 && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe && - old_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state) - old_index = old_pipe->bottom_pipe->bottom_pipe->pipe_idx; - else if (old_pipe->bottom_pipe && - old_pipe->bottom_pipe->plane_state == old_pipe->plane_state) + else if (!odm && old_pipe->bottom_pipe && + old_pipe->bottom_pipe->plane_state == old_pipe->plane_state) old_index = old_pipe->bottom_pipe->pipe_idx; + else + old_index = -1; + pipe_4to1 = dcn32_find_split_pipe(dc, context, old_index); + ASSERT(pipe_4to1); + if (!pipe_4to1) + goto validate_fail; + if (!dcn32_split_stream_for_mpc_or_odm( + dc, &context->res_ctx, + pipe, pipe_4to1, odm)) + goto validate_fail; + newly_split[pipe_4to1->pipe_idx] = true; + + if (odm && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe + && old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe) + old_index = old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe->pipe_idx; + else if (!odm && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe && + old_pipe->bottom_pipe->bottom_pipe->bottom_pipe && + old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state) + old_index = old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->pipe_idx; + else + old_index = -1; + pipe_4to1 = dcn32_find_split_pipe(dc, context, old_index); + ASSERT(pipe_4to1); + if (!pipe_4to1) + goto validate_fail; + if (!dcn32_split_stream_for_mpc_or_odm( + dc, &context->res_ctx, + hsplit_pipe, pipe_4to1, odm)) + goto validate_fail; + newly_split[pipe_4to1->pipe_idx] = true; } - hsplit_pipe = dcn32_find_split_pipe(dc, context, old_index); - ASSERT(hsplit_pipe); - if (!hsplit_pipe) - goto validate_fail; - - if (!dcn32_split_stream_for_mpc_or_odm( - dc, &context->res_ctx, - pipe, hsplit_pipe, odm)) - goto validate_fail; - - newly_split[hsplit_pipe->pipe_idx] = true; - repopulate_pipes = true; - } - if (split[i] == 4) { - struct pipe_ctx *pipe_4to1; - - if (odm && old_pipe->next_odm_pipe) - old_index = old_pipe->next_odm_pipe->pipe_idx; - else if (!odm && old_pipe->bottom_pipe && - old_pipe->bottom_pipe->plane_state == old_pipe->plane_state) - old_index = old_pipe->bottom_pipe->pipe_idx; - else - old_index = -1; - pipe_4to1 = dcn32_find_split_pipe(dc, context, old_index); - ASSERT(pipe_4to1); - if (!pipe_4to1) - goto validate_fail; - if (!dcn32_split_stream_for_mpc_or_odm( - dc, &context->res_ctx, - pipe, pipe_4to1, odm)) - goto validate_fail; - newly_split[pipe_4to1->pipe_idx] = true; - - if (odm && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe - && old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe) - old_index = old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe->pipe_idx; - else if (!odm && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe && - old_pipe->bottom_pipe->bottom_pipe->bottom_pipe && - old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state) - old_index = old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->pipe_idx; - else - old_index = -1; - pipe_4to1 = dcn32_find_split_pipe(dc, context, old_index); - ASSERT(pipe_4to1); - if (!pipe_4to1) - goto validate_fail; - if (!dcn32_split_stream_for_mpc_or_odm( - dc, &context->res_ctx, - hsplit_pipe, pipe_4to1, odm)) - goto validate_fail; - newly_split[pipe_4to1->pipe_idx] = true; + if (odm) + dcn20_build_mapped_resource(dc, context, pipe->stream); } - if (odm) - dcn20_build_mapped_resource(dc, context, pipe->stream); - } - for (i = 0; i < dc->res_pool->pipe_count; i++) { - struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; - if (pipe->plane_state) { - if (!resource_build_scaling_params(pipe)) - goto validate_fail; + if (pipe->plane_state) { + if (!resource_build_scaling_params(pipe)) + goto validate_fail; + } } } @@ -1934,6 +2200,7 @@ bool dcn32_internal_validate_bw(struct dc *dc, vba->VoltageLevel = i; vlevel = i; flags_valid = true; + break; } } @@ -2704,12 +2971,14 @@ void dcn32_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_pa /* Override from passed dc->bb_overrides if available*/ if ((int)(dcn3_2_soc.sr_exit_time_us * 1000) != dc->bb_overrides.sr_exit_time_ns && dc->bb_overrides.sr_exit_time_ns) { + dc->dml2_options.bbox_overrides.sr_exit_latency_us = dcn3_2_soc.sr_exit_time_us = dc->bb_overrides.sr_exit_time_ns / 1000.0; } if ((int)(dcn3_2_soc.sr_enter_plus_exit_time_us * 1000) != dc->bb_overrides.sr_enter_plus_exit_time_ns && dc->bb_overrides.sr_enter_plus_exit_time_ns) { + dc->dml2_options.bbox_overrides.sr_enter_plus_exit_latency_us = dcn3_2_soc.sr_enter_plus_exit_time_us = dc->bb_overrides.sr_enter_plus_exit_time_ns / 1000.0; } @@ -2717,12 +2986,14 @@ void dcn32_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_pa if ((int)(dcn3_2_soc.urgent_latency_us * 1000) != dc->bb_overrides.urgent_latency_ns && dc->bb_overrides.urgent_latency_ns) { dcn3_2_soc.urgent_latency_us = dc->bb_overrides.urgent_latency_ns / 1000.0; + dc->dml2_options.bbox_overrides.urgent_latency_us = dcn3_2_soc.urgent_latency_pixel_data_only_us = dc->bb_overrides.urgent_latency_ns / 1000.0; } if ((int)(dcn3_2_soc.dram_clock_change_latency_us * 1000) != dc->bb_overrides.dram_clock_change_latency_ns && dc->bb_overrides.dram_clock_change_latency_ns) { + dc->dml2_options.bbox_overrides.dram_clock_change_latency_us = dcn3_2_soc.dram_clock_change_latency_us = dc->bb_overrides.dram_clock_change_latency_ns / 1000.0; } @@ -2730,6 +3001,7 @@ void dcn32_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_pa if ((int)(dcn3_2_soc.fclk_change_latency_us * 1000) != dc->bb_overrides.fclk_clock_change_latency_ns && dc->bb_overrides.fclk_clock_change_latency_ns) { + dc->dml2_options.bbox_overrides.fclk_change_latency_us = dcn3_2_soc.fclk_change_latency_us = dc->bb_overrides.fclk_clock_change_latency_ns / 1000; } @@ -2747,14 +3019,17 @@ void dcn32_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_pa if (dc->ctx->dc_bios->funcs->get_soc_bb_info(dc->ctx->dc_bios, &bb_info) == BP_RESULT_OK) { if (bb_info.dram_clock_change_latency_100ns > 0) + dc->dml2_options.bbox_overrides.dram_clock_change_latency_us = dcn3_2_soc.dram_clock_change_latency_us = bb_info.dram_clock_change_latency_100ns * 10; if (bb_info.dram_sr_enter_exit_latency_100ns > 0) + dc->dml2_options.bbox_overrides.sr_enter_plus_exit_latency_us = dcn3_2_soc.sr_enter_plus_exit_time_us = bb_info.dram_sr_enter_exit_latency_100ns * 10; if (bb_info.dram_sr_exit_latency_100ns > 0) + dc->dml2_options.bbox_overrides.sr_exit_latency_us = dcn3_2_soc.sr_exit_time_us = bb_info.dram_sr_exit_latency_100ns * 10; } @@ -2762,12 +3037,14 @@ void dcn32_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_pa /* Override from VBIOS for num_chan */ if (dc->ctx->dc_bios->vram_info.num_chans) { + dc->dml2_options.bbox_overrides.dram_num_chan = dcn3_2_soc.num_chans = dc->ctx->dc_bios->vram_info.num_chans; dcn3_2_soc.mall_allocated_for_dcn_mbytes = (double)(dcn32_calc_num_avail_chans_for_mall(dc, dc->ctx->dc_bios->vram_info.num_chans) * dc->caps.mall_size_per_mem_channel); } if (dc->ctx->dc_bios->vram_info.dram_channel_width_bytes) + dc->dml2_options.bbox_overrides.dram_chanel_width_bytes = dcn3_2_soc.dram_channel_width_bytes = dc->ctx->dc_bios->vram_info.dram_channel_width_bytes; /* DML DSC delay factor workaround */ @@ -2778,6 +3055,10 @@ void dcn32_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_pa /* Override dispclk_dppclk_vco_speed_mhz from Clk Mgr */ dcn3_2_soc.dispclk_dppclk_vco_speed_mhz = dc->clk_mgr->dentist_vco_freq_khz / 1000.0; dc->dml.soc.dispclk_dppclk_vco_speed_mhz = dc->clk_mgr->dentist_vco_freq_khz / 1000.0; + dc->dml2_options.bbox_overrides.disp_pll_vco_speed_mhz = dc->clk_mgr->dentist_vco_freq_khz / 1000.0; + dc->dml2_options.bbox_overrides.xtalclk_mhz = dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency / 1000.0; + dc->dml2_options.bbox_overrides.dchub_refclk_mhz = dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000.0; + dc->dml2_options.bbox_overrides.dprefclk_mhz = dc->clk_mgr->dprefclk_khz / 1000.0; /* Overrides Clock levelsfrom CLK Mgr table entries as reported by PM FW */ if (bw_params->clk_table.entries[0].memclk_mhz) { @@ -2933,6 +3214,72 @@ void dcn32_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_pa if (dc->current_state) dml_init_instance(&dc->current_state->bw_ctx.dml, &dcn3_2_soc, &dcn3_2_ip, DML_PROJECT_DCN32); } + + if (dc->clk_mgr->bw_params->clk_table.num_entries > 1) { + unsigned int i = 0; + + dc->dml2_options.bbox_overrides.clks_table.num_states = dc->clk_mgr->bw_params->clk_table.num_entries; + + dc->dml2_options.bbox_overrides.clks_table.num_entries_per_clk.num_dcfclk_levels = + dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dcfclk_levels; + + dc->dml2_options.bbox_overrides.clks_table.num_entries_per_clk.num_fclk_levels = + dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_fclk_levels; + + dc->dml2_options.bbox_overrides.clks_table.num_entries_per_clk.num_memclk_levels = + dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_memclk_levels; + + dc->dml2_options.bbox_overrides.clks_table.num_entries_per_clk.num_socclk_levels = + dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_socclk_levels; + + dc->dml2_options.bbox_overrides.clks_table.num_entries_per_clk.num_dtbclk_levels = + dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dtbclk_levels; + + dc->dml2_options.bbox_overrides.clks_table.num_entries_per_clk.num_dispclk_levels = + dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dispclk_levels; + + dc->dml2_options.bbox_overrides.clks_table.num_entries_per_clk.num_dppclk_levels = + dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dppclk_levels; + + for (i = 0; i < dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dcfclk_levels; i++) { + if (dc->clk_mgr->bw_params->clk_table.entries[i].dcfclk_mhz) + dc->dml2_options.bbox_overrides.clks_table.clk_entries[i].dcfclk_mhz = + dc->clk_mgr->bw_params->clk_table.entries[i].dcfclk_mhz; + } + + for (i = 0; i < dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_fclk_levels; i++) { + if (dc->clk_mgr->bw_params->clk_table.entries[i].fclk_mhz) + dc->dml2_options.bbox_overrides.clks_table.clk_entries[i].fclk_mhz = + dc->clk_mgr->bw_params->clk_table.entries[i].fclk_mhz; + } + + for (i = 0; i < dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_memclk_levels; i++) { + if (dc->clk_mgr->bw_params->clk_table.entries[i].memclk_mhz) + dc->dml2_options.bbox_overrides.clks_table.clk_entries[i].memclk_mhz = + dc->clk_mgr->bw_params->clk_table.entries[i].memclk_mhz; + } + + for (i = 0; i < dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_socclk_levels; i++) { + if (dc->clk_mgr->bw_params->clk_table.entries[i].socclk_mhz) + dc->dml2_options.bbox_overrides.clks_table.clk_entries[i].socclk_mhz = + dc->clk_mgr->bw_params->clk_table.entries[i].socclk_mhz; + } + + for (i = 0; i < dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dtbclk_levels; i++) { + if (dc->clk_mgr->bw_params->clk_table.entries[i].dtbclk_mhz) + dc->dml2_options.bbox_overrides.clks_table.clk_entries[i].dtbclk_mhz = + dc->clk_mgr->bw_params->clk_table.entries[i].dtbclk_mhz; + } + + for (i = 0; i < dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dispclk_levels; i++) { + if (dc->clk_mgr->bw_params->clk_table.entries[i].dispclk_mhz) { + dc->dml2_options.bbox_overrides.clks_table.clk_entries[i].dispclk_mhz = + dc->clk_mgr->bw_params->clk_table.entries[i].dispclk_mhz; + dc->dml2_options.bbox_overrides.clks_table.clk_entries[i].dppclk_mhz = + dc->clk_mgr->bw_params->clk_table.entries[i].dispclk_mhz; + } + } + } } void dcn32_zero_pipe_dcc_fraction(display_e2e_pipe_params_st *pipes, |