drm/amd/display: Add DCN42 PMO policy for DML2.1

[Why]
The MinTTU policy in DML2.1 does not guarantee that we support p-state
in blank. This is a delta vs dml2 and earlier revisions as the prefetch
mode override has been removed in favor of a more configurable pstate
optimizer.

[How]
Split off DCN42 with its own PMO helpers so that we can use a simpler
strategy of only allowing the mode if we support p-state in vblank and
if vactive has enough latency hiding.

The actual hookup to use these helpers in the PMO factory will be
done in a later patch to satisfy build system requirements.

Reviewed-by: Dillon Varone <dillon.varone@amd.com>
Signed-off-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com>
Signed-off-by: Roman Li <roman.li@amd.com>
Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Nicholas Kazlauskas
2026-03-25 14:37:04 -04:00
committed by Alex Deucher
parent 136d15b077
commit bcfeed1748
5 changed files with 229 additions and 7 deletions

View File

@@ -100,6 +100,7 @@ DML21 += src/dml2_mcg/dml2_mcg_factory.o
DML21 += src/dml2_pmo/dml2_pmo_dcn3.o
DML21 += src/dml2_pmo/dml2_pmo_factory.o
DML21 += src/dml2_pmo/dml2_pmo_dcn4_fams2.o
DML21 += src/dml2_pmo/dml2_pmo_dcn42.o
DML21 += src/dml2_standalone_libraries/lib_float_math.o
DML21 += dml21_translation_helper.o
DML21 += dml21_wrapper.o

View File

@@ -0,0 +1,192 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2026 Advanced Micro Devices, Inc.
#include "dml2_pmo_dcn42.h"
#include "lib_float_math.h"
#include "dml2_debug.h"
#include "dml2_pmo_dcn4_fams2.h"
/*
* DCN42 PMO Policy Implementation
* This implementation provides VBlank-only strategies for 1, 2, 3, and 4 display
* configurations, ensuring p-state watermark support in the blank period only.
*/
static const struct dml2_pmo_pstate_strategy dcn42_strategy_list_1_display[] = {
// VBlank only
{
.per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_na, dml2_pstate_method_na, dml2_pstate_method_na },
.allow_state_increase = true,
},
};
static const int dcn42_strategy_list_1_display_size = sizeof(dcn42_strategy_list_1_display) / sizeof(struct dml2_pmo_pstate_strategy);
static const struct dml2_pmo_pstate_strategy dcn42_strategy_list_2_display[] = {
// VBlank only for both displays
{
.per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_na, dml2_pstate_method_na },
.allow_state_increase = true,
},
};
static const int dcn42_strategy_list_2_display_size = sizeof(dcn42_strategy_list_2_display) / sizeof(struct dml2_pmo_pstate_strategy);
static const struct dml2_pmo_pstate_strategy dcn42_strategy_list_3_display[] = {
// VBlank only for all three displays
{
.per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_na },
.allow_state_increase = true,
},
};
static const int dcn42_strategy_list_3_display_size = sizeof(dcn42_strategy_list_3_display) / sizeof(struct dml2_pmo_pstate_strategy);
static const struct dml2_pmo_pstate_strategy dcn42_strategy_list_4_display[] = {
// VBlank only for all four displays
{
.per_stream_pstate_method = { dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank, dml2_pstate_method_vblank },
.allow_state_increase = true,
},
};
static const int dcn42_strategy_list_4_display_size = sizeof(dcn42_strategy_list_4_display) / sizeof(struct dml2_pmo_pstate_strategy);
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;
const int REQUIRED_RESERVED_TIME =
(int)in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us;
bool p_state_supported = true;
unsigned int stream_index;
if (in_out->base_display_config->display_config.overrides.all_streams_blanked)
return true;
if (s->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_vblank) {
if (dcn4_get_minimum_reserved_time_us_for_planes(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < REQUIRED_RESERVED_TIME ||
dcn4_get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) > 0) {
p_state_supported = false;
break;
}
} else {
p_state_supported = false;
break;
}
}
return p_state_supported;
}
bool pmo_dcn42_initialize(struct dml2_pmo_initialize_in_out *in_out)
{
int i = 0;
struct dml2_pmo_instance *pmo = in_out->instance;
unsigned int base_list_size = 0;
const struct dml2_pmo_pstate_strategy *base_list = NULL;
unsigned int *expanded_list_size = NULL;
struct dml2_pmo_pstate_strategy *expanded_list = NULL;
DML_LOG_COMP_IF_ENTER();
pmo->soc_bb = in_out->soc_bb;
pmo->ip_caps = in_out->ip_caps;
pmo->mpc_combine_limit = 2;
pmo->odm_combine_limit = 4;
pmo->mcg_clock_table_size = in_out->mcg_clock_table_size;
/*
* DCN42 does not support FAMS features like SubVP and DRR.
* These parameters are initialized to safe values but won't be used
* since our strategies only use VBlank.
*/
pmo->fams_params.v2.subvp.refresh_rate_limit_max = 0;
pmo->fams_params.v2.subvp.refresh_rate_limit_min = 0;
pmo->fams_params.v2.drr.refresh_rate_limit_max = 0;
pmo->fams_params.v2.drr.refresh_rate_limit_min = 0;
pmo->options = in_out->options;
/* Generate permutations of p-state configs from base strategy list */
for (i = 0; i < PMO_DCN4_MAX_DISPLAYS; i++) {
switch (i+1) {
case 1:
if (pmo->options->override_strategy_lists[i] && pmo->options->num_override_strategies_per_list[i]) {
base_list = pmo->options->override_strategy_lists[i];
base_list_size = pmo->options->num_override_strategies_per_list[i];
} else {
base_list = dcn42_strategy_list_1_display;
base_list_size = dcn42_strategy_list_1_display_size;
}
expanded_list_size = &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i];
expanded_list = pmo->init_data.pmo_dcn4.expanded_strategy_list_1_display;
break;
case 2:
if (pmo->options->override_strategy_lists[i] && pmo->options->num_override_strategies_per_list[i]) {
base_list = pmo->options->override_strategy_lists[i];
base_list_size = pmo->options->num_override_strategies_per_list[i];
} else {
base_list = dcn42_strategy_list_2_display;
base_list_size = dcn42_strategy_list_2_display_size;
}
expanded_list_size = &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i];
expanded_list = pmo->init_data.pmo_dcn4.expanded_strategy_list_2_display;
break;
case 3:
if (pmo->options->override_strategy_lists[i] && pmo->options->num_override_strategies_per_list[i]) {
base_list = pmo->options->override_strategy_lists[i];
base_list_size = pmo->options->num_override_strategies_per_list[i];
} else {
base_list = dcn42_strategy_list_3_display;
base_list_size = dcn42_strategy_list_3_display_size;
}
expanded_list_size = &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i];
expanded_list = pmo->init_data.pmo_dcn4.expanded_strategy_list_3_display;
break;
case 4:
if (pmo->options->override_strategy_lists[i] && pmo->options->num_override_strategies_per_list[i]) {
base_list = pmo->options->override_strategy_lists[i];
base_list_size = pmo->options->num_override_strategies_per_list[i];
} else {
base_list = dcn42_strategy_list_4_display;
base_list_size = dcn42_strategy_list_4_display_size;
}
expanded_list_size = &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i];
expanded_list = pmo->init_data.pmo_dcn4.expanded_strategy_list_4_display;
break;
}
DML_ASSERT(base_list_size <= PMO_DCN4_MAX_BASE_STRATEGIES);
/*
* Populate list using DCN4 FAMS2 expansion function.
* Since our strategies only contain VBlank methods, the expansion
* will not introduce any FAMS-specific logic.
*/
pmo_dcn4_fams2_expand_base_pstate_strategies(
base_list,
base_list_size,
i + 1,
expanded_list,
expanded_list_size);
}
DML_LOG_DEBUG("%s exit with true\n", __func__);
DML_LOG_COMP_IF_EXIT();
return true;
}

View File

@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#ifndef __DML2_PMO_DCN42_H__
#define __DML2_PMO_DCN42_H__
#include "dml2_internal_shared_types.h"
struct dml2_pmo_initialize_in_out;
struct dml2_pmo_test_for_pstate_support_in_out;
bool pmo_dcn42_initialize(struct dml2_pmo_initialize_in_out *in_out);
bool pmo_dcn42_test_for_pstate_support(struct dml2_pmo_test_for_pstate_support_in_out *in_out);
#endif /* __DML2_PMO_DCN42_H__ */

View File

@@ -1662,7 +1662,7 @@ static bool validate_pstate_support_strategy_cofunctionality(struct dml2_pmo_ins
return is_config_schedulable(pmo, display_cfg, pstate_strategy);
}
static int get_vactive_pstate_margin(const struct display_configuation_with_meta *display_cfg, int plane_mask)
int dcn4_get_vactive_pstate_margin(const struct display_configuation_with_meta *display_cfg, int plane_mask)
{
unsigned int i;
int min_vactive_margin_us = 0xFFFFFFF;
@@ -1907,7 +1907,7 @@ bool pmo_dcn4_fams2_init_for_pstate_support(struct dml2_pmo_init_for_pstate_supp
// 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 (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]) >= (int)(MIN_VACTIVE_MARGIN_PCT * pmo->soc_bb->power_management_parameters.dram_clk_change_blackout_us))
set_bit_in_bitfield(&s->pmo_dcn4.stream_vactive_capability_mask, stream_index);
/* FAMS2 meta */
@@ -2182,7 +2182,9 @@ static bool setup_display_config(struct display_configuation_with_meta *display_
return success;
}
static int get_minimum_reserved_time_us_for_planes(struct display_configuation_with_meta *display_config, int plane_mask)
int dcn4_get_minimum_reserved_time_us_for_planes(
const struct display_configuation_with_meta *display_config,
int plane_mask)
{
int min_time_us = 0xFFFFFF;
unsigned int plane_index = 0;
@@ -2222,16 +2224,16 @@ bool pmo_dcn4_fams2_test_for_pstate_support(struct dml2_pmo_test_for_pstate_supp
if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_vactive ||
s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vactive_drr) {
if (get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < (MIN_VACTIVE_MARGIN_PCT * in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us) ||
if (dcn4_get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < (MIN_VACTIVE_MARGIN_PCT * in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us) ||
get_vactive_det_fill_latency_delay_us(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) > stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_us) {
p_state_supported = false;
break;
}
} else if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_vblank ||
s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vblank_drr) {
if (get_minimum_reserved_time_us_for_planes(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) <
if (dcn4_get_minimum_reserved_time_us_for_planes(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) <
REQUIRED_RESERVED_TIME ||
get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < MIN_VACTIVE_MARGIN_VBLANK) {
dcn4_get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < MIN_VACTIVE_MARGIN_VBLANK) {
p_state_supported = false;
break;
}
@@ -2243,7 +2245,7 @@ bool pmo_dcn4_fams2_test_for_pstate_support(struct dml2_pmo_test_for_pstate_supp
}
} else if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_drr) {
if (!all_planes_match_method(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index], dml2_pstate_method_fw_drr) ||
get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < MIN_VACTIVE_MARGIN_DRR) {
dcn4_get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < MIN_VACTIVE_MARGIN_DRR) {
p_state_supported = false;
break;
}

View File

@@ -7,6 +7,16 @@
#include "dml2_internal_shared_types.h"
struct display_configuation_with_meta;
int dcn4_get_vactive_pstate_margin(
const struct display_configuation_with_meta *display_cfg,
int plane_mask);
int dcn4_get_minimum_reserved_time_us_for_planes(
const struct display_configuation_with_meta *display_config,
int plane_mask);
bool pmo_dcn4_fams2_initialize(struct dml2_pmo_initialize_in_out *in_out);
bool pmo_dcn4_fams2_optimize_dcc_mcache(struct dml2_pmo_optimize_dcc_mcache_in_out *in_out);