From: Nicholas Kazlauskas <[email protected]>

[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 <[email protected]>
Signed-off-by: Nicholas Kazlauskas <[email protected]>
Signed-off-by: Roman Li <[email protected]>
---
 .../gpu/drm/amd/display/dc/dml2_0/Makefile    |   1 +
 .../dml21/src/dml2_pmo/dml2_pmo_dcn42.c       | 192 ++++++++++++++++++
 .../dml21/src/dml2_pmo/dml2_pmo_dcn42.h       |  17 ++
 .../dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c  |  16 +-
 .../dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h  |  10 +
 5 files changed, 229 insertions(+), 7 deletions(-)
 create mode 100644 
drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.c
 create mode 100644 
drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.h

diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile 
b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile
index 2625943d7f7e..8a451c36fdb3 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile
@@ -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
diff --git 
a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.c 
b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.c
new file mode 100644
index 000000000000..30fd5efe4b87
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.c
@@ -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;
+}
diff --git 
a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.h 
b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.h
new file mode 100644
index 000000000000..31ba8575351d
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.h
@@ -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__ */
diff --git 
a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c
 
b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c
index f7c10dbfc154..b348c65a0f75 100644
--- 
a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c
+++ 
b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c
@@ -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;
                        }
diff --git 
a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h
 
b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h
index 6baab7ad6ecc..f0afa8002a2f 100644
--- 
a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h
+++ 
b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h
@@ -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);
-- 
2.34.1

Reply via email to