/* SPDX-License-Identifier: MIT */ /* * Copyright 2023 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: AMD *
*/
staticbool optimize_configuration(struct dml2_context *dml2, struct dml2_wrapper_optimize_configuration_params *p)
{ int unused_dpps = p->ip_params->max_num_dpp; int i; int odms_needed; int largest_blend_and_timing = 0; bool optimization_done = false;
for (i = 0; i < (int) p->cur_display_config->num_timings; i++) { if (p->cur_display_config->plane.BlendingAndTiming[i] > largest_blend_and_timing)
largest_blend_and_timing = p->cur_display_config->plane.BlendingAndTiming[i];
}
if (p->new_policy != p->cur_policy)
*p->new_policy = *p->cur_policy;
if (p->new_display_config != p->cur_display_config)
*p->new_display_config = *p->cur_display_config;
/* Zero out before each call before proceeding */
memset(s, 0, sizeof(struct dml2_calculate_lowest_supported_state_for_temp_read_scratch));
memset(&s_global->mode_support_params, 0, sizeof(struct dml_mode_support_ex_params_st));
memset(&s_global->dml_to_dc_pipe_mapping, 0, sizeof(struct dml2_dml_to_dc_pipe_mapping));
for (i = 0; i < dml2->config.dcn_pipe_count; i++) { /* Calling resource_build_scaling_params will populate the pipe params * with the necessary information needed for correct DML calculations * This is also done in DML1 driver code path and hence display_state * cannot be const.
*/ struct pipe_ctx *pipe = &display_state->res_ctx.pipe_ctx[i];
if (pipe->plane_state) { if (!dml2->config.callbacks.build_scaling_params(pipe)) {
ASSERT(false); returnfalse;
}
}
}
for (i = 0; i < dml2->v20.dml_core_ctx.states.num_states; i++) {
s->uclk_change_latencies[i] = dml2->v20.dml_core_ctx.states.state_array[i].dram_clock_change_latency_us;
}
for (i = 0; i < 4; i++) { for (j = 0; j < dml2->v20.dml_core_ctx.states.num_states; j++) {
dml2->v20.dml_core_ctx.states.state_array[j].dram_clock_change_latency_us = s_global->dummy_pstate_table[i].dummy_pstate_latency_us;
}
result = s_global->mode_support_params.out_lowest_state_idx;
while (dml2->v20.dml_core_ctx.states.state_array[result].dram_speed_mts < s_global->dummy_pstate_table[i].dram_speed_mts)
result++;
break;
}
}
for (i = 0; i < dml2->v20.dml_core_ctx.states.num_states; i++) {
dml2->v20.dml_core_ctx.states.state_array[i].dram_clock_change_latency_us = s->uclk_change_latencies[i];
}
return result;
}
staticvoid copy_dummy_pstate_table(struct dummy_pstate_entry *dest, struct dummy_pstate_entry *src, unsignedint num_entries)
{ for (int i = 0; i < num_entries; i++) {
dest[i] = src[i];
}
}
/* Zero out before each call before proceeding */
memset(&s->cur_display_config, 0, sizeof(struct dml_display_cfg_st));
memset(&s->mode_support_params, 0, sizeof(struct dml_mode_support_ex_params_st));
memset(&s->dml_to_dc_pipe_mapping, 0, sizeof(struct dml2_dml_to_dc_pipe_mapping));
memset(&s->optimize_configuration_params, 0, sizeof(struct dml2_wrapper_optimize_configuration_params));
for (i = 0; i < dml2->config.dcn_pipe_count; i++) { /* Calling resource_build_scaling_params will populate the pipe params * with the necessary information needed for correct DML calculations * This is also done in DML1 driver code path and hence display_state * cannot be const.
*/ struct pipe_ctx *pipe = &display_state->res_ctx.pipe_ctx[i];
if (pipe->plane_state) { if (!dml2->config.callbacks.build_scaling_params(pipe)) {
ASSERT(false); returnfalse;
}
}
}
map_dc_state_into_dml_display_cfg(dml2, display_state, &s->cur_display_config); if (!dml2->config.skip_hw_state_mapping)
dml2_apply_det_buffer_allocation_policy(dml2, &s->cur_display_config);
result = pack_and_call_dml_mode_support_ex(dml2,
&s->cur_display_config,
&s->mode_support_info,
validate_mode);
if (result)
result = does_configuration_meet_sw_policies(dml2, &s->cur_display_config, &s->mode_support_info);
if (optimized_result)
optimized_result = does_configuration_meet_sw_policies(dml2, &s->new_display_config, &s->mode_support_info);
// If the new optimized state is supposed, then set current = new if (optimized_result) {
s->cur_display_config = s->new_display_config;
s->cur_policy = s->new_policy;
} else { // Else, restore policy to current
dml2->v20.dml_core_ctx.policy = s->cur_policy;
}
}
// Optimize ended with a failed config, so we need to restore DML state to last passing if (!optimized_result) {
result = pack_and_call_dml_mode_support_ex(dml2,
&s->cur_display_config,
&s->mode_support_info,
validate_mode);
}
}
if (result)
map_hw_resources(dml2, &s->cur_display_config, &s->mode_support_info);
return result;
}
staticbool call_dml_mode_support_and_programming(struct dc_state *context, enum dc_validate_mode validate_mode)
{ unsignedint result = 0; unsignedint min_state = 0; int min_state_for_g6_temp_read = 0;
if (!context->streams[0]->sink->link->dc->caps.is_apu) {
min_state_for_g6_temp_read = calculate_lowest_supported_state_for_temp_read(dml2, context,
validate_mode);
ASSERT(min_state_for_g6_temp_read >= 0);
}
result = dml_mode_support_wrapper(dml2, context, validate_mode);
/* Upon trying to sett certain frequencies in FRL, min_state_for_g6_temp_read is reported as -1. This leads to an invalid value of min_state causing crashes later on. * Use the default logic for min_state only when min_state_for_g6_temp_read is a valid value. In other cases, use the value calculated by the DML directly.
*/ if (!context->streams[0]->sink->link->dc->caps.is_apu) { if (min_state_for_g6_temp_read >= 0)
min_state = min_state_for_g6_temp_read > s->mode_support_params.out_lowest_state_idx ? min_state_for_g6_temp_read : s->mode_support_params.out_lowest_state_idx; else
min_state = s->mode_support_params.out_lowest_state_idx;
}
if (result) { if (!context->streams[0]->sink->link->dc->caps.is_apu) {
result = dml_mode_programming(&dml2->v20.dml_core_ctx, min_state, &s->cur_display_config, true);
} else {
result = dml_mode_programming(&dml2->v20.dml_core_ctx, s->mode_support_params.out_lowest_state_idx, &s->cur_display_config, true);
}
} return result;
}
if (context->stream_count == 0) { unsignedint lowest_state_idx = 0;
out_clks.p_state_supported = true;
out_clks.dispclk_khz = 0; /* No requirement, and lowest index will generally be maximum dispclk. */
out_clks.dcfclk_khz = (unsignedint)dml2->v20.dml_core_ctx.states.state_array[lowest_state_idx].dcfclk_mhz * 1000;
out_clks.fclk_khz = (unsignedint)dml2->v20.dml_core_ctx.states.state_array[lowest_state_idx].fabricclk_mhz * 1000;
out_clks.uclk_mts = (unsignedint)dml2->v20.dml_core_ctx.states.state_array[lowest_state_idx].dram_speed_mts;
out_clks.phyclk_khz = (unsignedint)dml2->v20.dml_core_ctx.states.state_array[lowest_state_idx].phyclk_mhz * 1000;
out_clks.socclk_khz = (unsignedint)dml2->v20.dml_core_ctx.states.state_array[lowest_state_idx].socclk_mhz * 1000;
out_clks.ref_dtbclk_khz = (unsignedint)dml2->v20.dml_core_ctx.states.state_array[lowest_state_idx].dtbclk_mhz * 1000;
context->bw_ctx.bw.dcn.clk.dtbclk_en = false;
dml2_copy_clocks_to_dc_state(&out_clks, context); returntrue;
}
/* Zero out before each call before proceeding */
memset(&dml2->v20.scratch, 0, sizeof(struct dml2_wrapper_scratch));
memset(&dml2->v20.dml_core_ctx.policy, 0, sizeof(struct dml_mode_eval_policy_st));
memset(&dml2->v20.dml_core_ctx.ms, 0, sizeof(struct mode_support_st));
memset(&dml2->v20.dml_core_ctx.mp, 0, sizeof(struct mode_program_st));
/* Initialize DET scratch */
dml2_initialize_det_scratch(dml2);
result = call_dml_mode_support_and_programming(context, validate_mode); /* Call map dc pipes to map the pipes based on the DML output. For correctly determining if recalculation * is required or not, the resource context needs to correctly reflect the number of active pipes. We would * only know the correct number if active pipes after dml2_map_dc_pipes is called.
*/ if (result && !dml2->config.skip_hw_state_mapping)
dml2_map_dc_pipes(dml2, context, &s->cur_display_config, &s->dml_to_dc_pipe_mapping, in_dc->current_state);
/* Verify and update DET Buffer configuration if needed. dml2_verify_det_buffer_configuration will check if DET Buffer * size needs to be updated. If yes it will update the DETOverride variable and set need_recalculation flag to true. * Based on that flag, run mode support again. Verification needs to be run after dml_mode_programming because the getters * return correct det buffer values only after dml_mode_programming is called.
*/ if (result && !dml2->config.skip_hw_state_mapping) {
need_recalculation = dml2_verify_det_buffer_configuration(dml2, context, &dml2->det_helper_scratch); if (need_recalculation) { /* Engage the DML again if recalculation is required. */
call_dml_mode_support_and_programming(context, validate_mode); if (!dml2->config.skip_hw_state_mapping) {
dml2_map_dc_pipes(dml2, context, &s->cur_display_config, &s->dml_to_dc_pipe_mapping, in_dc->current_state);
}
need_recalculation = dml2_verify_det_buffer_configuration(dml2, context, &dml2->det_helper_scratch);
ASSERT(need_recalculation == false);
}
}
if (!context || context->stream_count == 0) returntrue;
dml2 = context->bw_ctx.dml2;
/* Zero out before each call before proceeding */
memset(&dml2->v20.scratch, 0, sizeof(struct dml2_wrapper_scratch));
memset(&dml2->v20.dml_core_ctx.policy, 0, sizeof(struct dml_mode_eval_policy_st));
memset(&dml2->v20.dml_core_ctx.ms, 0, sizeof(struct mode_support_st));
memset(&dml2->v20.dml_core_ctx.mp, 0, sizeof(struct mode_program_st));
map_dc_state_into_dml_display_cfg(dml2, context, &dml2->v20.scratch.cur_display_config); if (!dml2->config.skip_hw_state_mapping)
dml2_apply_det_buffer_allocation_policy(dml2, &dml2->v20.scratch.cur_display_config);
result = pack_and_call_dml_mode_support_ex(dml2,
&dml2->v20.scratch.cur_display_config,
&dml2->v20.scratch.mode_support_info,
validate_mode);
if (result)
result = does_configuration_meet_sw_policies(dml2, &dml2->v20.scratch.cur_display_config, &dml2->v20.scratch.mode_support_info);
return (result == 1) ? true : false;
}
staticvoid dml2_apply_debug_options(conststruct dc *dc, struct dml2_context *dml2)
{ if (dc->debug.override_odm_optimization) {
dml2->config.minimize_dispclk_using_odm = dc->debug.minimize_dispclk_using_odm;
}
}
bool dml2_validate(conststruct dc *in_dc, struct dc_state *context, struct dml2_context *dml2, enum dc_validate_mode validate_mode)
{ bool out = false;
if (!dml2) returnfalse;
dml2_apply_debug_options(in_dc, dml2);
/* DML2.1 validation path */ if (dml2->architecture == dml2_architecture_21) {
out = dml21_validate(in_dc, context, dml2, validate_mode); return out;
}
DC_FP_START();
/* Use dml_validate_only for DC_VALIDATE_MODE_ONLY and DC_VALIDATE_MODE_AND_STATE_INDEX path */ if (validate_mode != DC_VALIDATE_MODE_AND_PROGRAMMING)
out = dml2_validate_only(context, validate_mode); else
out = dml2_validate_and_build_resource(in_dc, context, validate_mode);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.