drm/amd/display: enforce UCLK pstate support in mode_support

[Why]
mode_support does not require UCLK pstate today, so later PMO optimization
stages can push a plane's VActive latency-hiding margin below zero without
rechecking that the config still supports UCLK pstate. This can blank the
display on high-bandwidth configs.

[How]
Plumb the PMO-selected per-plane pstate method into mode_support and fail
the config only when UCLK pstate is required (method != na) but not
supported. For planes committed to a vactive method, require a non-negative
VActive latency-hiding margin, and skip the check when all streams are
blanked. No-op the PMO DCN42 pstate test (returning false only on the
initial candidate so the optimize/FAMS2 stage-3 setup still runs), since
reserved time is guaranteed by the override and the vactive margin is now
enforced in core mode_support.

Reviewed-by: Dillon Varone <dillon.varone@amd.com>
Signed-off-by: Alexander Chechik <alexander.chechik@amd.com>
Signed-off-by: Matthew Stewart <matthew.stewart2@amd.com>
Signed-off-by: Fangzhi Zuo <jerry.zuo@amd.com>
Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Alexander Chechik 2026-06-22 11:36:12 -04:00 committed by Alex Deucher
parent 6a8b6a7288
commit 137b565595
3 changed files with 36 additions and 22 deletions

View File

@ -554,6 +554,14 @@ bool core_dcn4_mode_support(struct dml2_core_mode_support_in_out *in_out)
l->mode_support_ex_params.min_clk_index = in_out->min_clk_index;
l->mode_support_ex_params.out_evaluation_info = &in_out->mode_support_result.cfg_support_info.clean_me_up.support_info;
for (i = 0; i < l->svp_expanded_display_cfg.num_planes; i++) {
if (i < in_out->display_cfg->display_config.num_planes)
core->clean_me_up.mode_lib.ms.uclk_pstate_switch_modes[i] =
in_out->display_cfg->stage3.pstate_switch_modes[i];
else
core->clean_me_up.mode_lib.ms.uclk_pstate_switch_modes[i] = dml2_pstate_method_na;
}
result = dml2_core_calcs_mode_support_ex(&l->mode_support_ex_params);
in_out->mode_support_result.cfg_support_info.is_supported = result;

View File

@ -9637,6 +9637,22 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
DML_LOG_VERBOSE("DML::%s: ROBSupport = %u\n", __func__, mode_lib->ms.support.ROBSupport);
#endif
mode_lib->ms.support.global_dram_clock_change_support_required = false;
if (!display_cfg->overrides.all_streams_blanked) {
for (k = 0; k < mode_lib->ms.num_active_planes; k++) {
if (mode_lib->ms.uclk_pstate_switch_modes[k] == dml2_pstate_method_na)
continue;
mode_lib->ms.support.global_dram_clock_change_support_required = true;
if ((mode_lib->ms.uclk_pstate_switch_modes[k] == dml2_pstate_method_vactive ||
mode_lib->ms.uclk_pstate_switch_modes[k] == dml2_pstate_method_fw_vactive_drr) &&
mode_lib->ms.VActiveLatencyHidingMargin[k] < 0)
mode_lib->ms.support.global_dram_clock_change_supported = false;
}
}
/*Mode Support, Voltage State and SOC Configuration*/
{
if (mode_lib->ms.support.ScaleRatioAndTapsSupport
@ -9683,6 +9699,8 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
&& mode_lib->ms.support.DCCMetaBufferSizeNotExceeded
&& !mode_lib->ms.support.ExceededMALLSize
&& mode_lib->ms.support.g6_temp_read_support
&& (mode_lib->ms.support.global_dram_clock_change_supported
|| !mode_lib->ms.support.global_dram_clock_change_support_required)
&& ((!display_cfg->hostvm_enable && !s->ImmediateFlipRequired) || mode_lib->ms.support.ImmediateFlipSupport)) {
DML_LOG_VERBOSE("DML::%s: mode is supported\n", __func__);
mode_lib->ms.support.ModeSupport = true;

View File

@ -13,8 +13,6 @@
* configurations, ensuring p-state watermark support in the blank period only.
*/
static const double MIN_VACTIVE_MARGIN_PCT = 0.25; // We need more than non-zero margin because DET buffer granularity can alter vactive latency hiding
static const struct dml2_pmo_pstate_strategy dcn42_strategy_list_1_display[] = {
// VBlank only
{
@ -179,7 +177,7 @@ bool pmo_dcn42_init_for_pstate_support(struct dml2_pmo_init_for_pstate_support_i
// Figure out which streams can do vactive, and also build up implicit SVP and FAMS2 meta
for (stream_index = 0; stream_index < display_config->display_config.num_streams; stream_index++) {
if (dcn4_get_vactive_pstate_margin(display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) >= (int)(MIN_VACTIVE_MARGIN_PCT * pmo->soc_bb->power_management_parameters.dram_clk_change_blackout_us))
if (dcn4_get_vactive_pstate_margin(display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) >= 0)
dcn42_set_bit_in_bitfield(&s->pmo_dcn4.stream_vactive_capability_mask, stream_index);
}
@ -259,27 +257,17 @@ bool pmo_dcn42_fams2_optimize_for_pstate_support(struct dml2_pmo_optimize_for_ps
bool pmo_dcn42_test_for_pstate_support(struct dml2_pmo_test_for_pstate_support_in_out *in_out)
{
const struct dml2_pmo_scratch *s = &in_out->instance->scratch;
bool p_state_supported = true;
unsigned int stream_index;
if (s->pmo_dcn4.cur_pstate_candidate < 0)
/* Return false on the initial candidate (cur_pstate_candidate == -1) so the
* optimization phase runs at least one optimize iteration; otherwise the
* FAMS2/stage-3 setup in the optimize callback is skipped.
*/
if (in_out->instance->scratch.pmo_dcn4.cur_pstate_candidate < 0)
return false;
for (stream_index = 0; stream_index < in_out->base_display_config->display_config.num_streams; stream_index++) {
if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_vactive) {
if (dcn4_get_minimum_reserved_time_us_for_planes(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < (int)in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us ||
dcn4_get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < (int)(MIN_VACTIVE_MARGIN_PCT * in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us)) {
p_state_supported = false;
break;
}
} else {
p_state_supported = false;
break;
}
}
return p_state_supported;
/* No-op: reserved time is guaranteed by the override and vactive p-state
* margin is now enforced in core mode support.
*/
return true;
}
bool pmo_dcn42_initialize(struct dml2_pmo_initialize_in_out *in_out)