xref: /linux/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c (revision 92c4c9fdc838d3b41a996bb700ea64b9e78fc7ea)
1 // SPDX-License-Identifier: MIT
2 //
3 // Copyright 2025 Advanced Micro Devices, Inc.
4 
5 #include "dcn42_soc_and_ip_translator.h"
6 #include "../dcn401/dcn401_soc_and_ip_translator.h"
7 #include "bounding_boxes/dcn42_soc_bb.h"
8 
9 /* soc_and_ip_translator component used to get up-to-date values for bounding box.
10  * Bounding box values are stored in several locations and locations can vary with DCN revision.
11  * This component provides an interface to get DCN-specific bounding box values.
12  */
13 
get_default_soc_bb(struct dml2_soc_bb * soc_bb,const struct dc * dc)14 static void get_default_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc)
15 {
16 	{
17 		memcpy(soc_bb, &dml2_socbb_dcn42, sizeof(struct dml2_soc_bb));
18 		memcpy(&soc_bb->qos_parameters, &dml_dcn42_variant_a_soc_qos_params, sizeof(struct dml2_soc_qos_parameters));
19 	}
20 }
21 
22 /*
23  * DC clock table is obtained from SMU during runtime.
24  * SMU stands for System Management Unit. It is a power management processor.
25  * It owns the initialization of dc's clock table and programming of clock values
26  * based on dc's requests.
27  * Our clock values in base soc bb is a dummy placeholder. The real clock values
28  * are retrieved from SMU firmware to dc clock table at runtime.
29  * This function overrides our dummy placeholder values with real values in dc
30  * clock table.
31  */
dcn42_convert_dc_clock_table_to_soc_bb_clock_table(struct dml2_soc_state_table * dml_clk_table,struct dml2_soc_vmin_clock_limits * vmin_limit,const struct clk_bw_params * dc_bw_params)32 static void dcn42_convert_dc_clock_table_to_soc_bb_clock_table(
33 		struct dml2_soc_state_table *dml_clk_table,
34 		struct dml2_soc_vmin_clock_limits *vmin_limit,
35 		const struct clk_bw_params *dc_bw_params)
36 {
37 	int i;
38 	const struct clk_limit_table *dc_clk_table;
39 
40 	if (dc_bw_params == NULL)
41 		/* skip if bw params could not be obtained from smu */
42 		return;
43 
44 	dc_clk_table = &dc_bw_params->clk_table;
45 
46 	/* fclk/dcfclk - dcn42 pmfw table can have 0 entries for inactive dpm levels
47 	 * for use with dml we need to fill in using an active value aiming for >= 2x DCFCLK
48 	 */
49 	if (dc_clk_table->num_entries_per_clk.num_fclk_levels && dc_clk_table->num_entries_per_clk.num_dcfclk_levels) {
50 		dml_clk_table->fclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dcfclk_levels;
51 		dml_clk_table->dcfclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dcfclk_levels;
52 		for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) {
53 			if (i < dc_clk_table->num_entries_per_clk.num_dcfclk_levels) {
54 				int j, max_fclk = 0;
55 
56 				dml_clk_table->dcfclk.clk_values_khz[i] = dc_clk_table->entries[i].dcfclk_mhz * 1000;
57 				for (j = 0; j < MAX_NUM_DPM_LVL; j++) {
58 					if (dc_clk_table->entries[j].fclk_mhz * 1000 > max_fclk)
59 						max_fclk = dc_clk_table->entries[j].fclk_mhz * 1000;
60 					dml_clk_table->fclk.clk_values_khz[i] = max_fclk;
61 					if (max_fclk >= 2 * dml_clk_table->dcfclk.clk_values_khz[i])
62 						break;
63 				}
64 			} else {
65 				dml_clk_table->dcfclk.clk_values_khz[i] = 0;
66 				dml_clk_table->fclk.clk_values_khz[i] = 0;
67 			}
68 		}
69 	}
70 
71 	/* uclk */
72 	if (dc_clk_table->num_entries_per_clk.num_memclk_levels) {
73 		dml_clk_table->uclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_memclk_levels;
74 		for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) {
75 			if (i < dml_clk_table->uclk.num_clk_values) {
76 				dml_clk_table->uclk.clk_values_khz[i] = dc_clk_table->entries[i].memclk_mhz * 1000;
77 				dml_clk_table->wck_ratio.clk_values_khz[i] = dc_clk_table->entries[i].wck_ratio;
78 			} else {
79 				dml_clk_table->uclk.clk_values_khz[i] = 0;
80 				dml_clk_table->wck_ratio.clk_values_khz[i] = 0;
81 			}
82 		}
83 	}
84 
85 	/* dispclk */
86 	if (dc_clk_table->num_entries_per_clk.num_dispclk_levels) {
87 		dml_clk_table->dispclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dispclk_levels;
88 		for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) {
89 			if (i < dml_clk_table->dispclk.num_clk_values) {
90 				dml_clk_table->dispclk.clk_values_khz[i] = dc_clk_table->entries[i].dispclk_mhz * 1000;
91 			} else {
92 				dml_clk_table->dispclk.clk_values_khz[i] = 0;
93 			}
94 		}
95 		vmin_limit->dispclk_khz = min(dc_clk_table->entries[0].dispclk_mhz * 1000, vmin_limit->dispclk_khz);
96 		/* dispclk is always fine-grain */
97 		dml_clk_table->dispclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dispclk_levels >= 2 ? 2 : 1;
98 		dml_clk_table->dispclk.clk_values_khz[0] = 0;
99 		dml_clk_table->dispclk.clk_values_khz[1] = dc_clk_table->entries[dc_clk_table->num_entries_per_clk.num_dispclk_levels - 1].dispclk_mhz * 1000;
100 	}
101 
102 	/* dppclk */
103 	if (dc_clk_table->num_entries_per_clk.num_dppclk_levels) {
104 		dml_clk_table->dppclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dppclk_levels;
105 		for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) {
106 			if (i < dml_clk_table->dppclk.num_clk_values) {
107 				dml_clk_table->dppclk.clk_values_khz[i] = dc_clk_table->entries[i].dppclk_mhz * 1000;
108 			} else {
109 				dml_clk_table->dppclk.clk_values_khz[i] = 0;
110 			}
111 		}
112 		/* dppclk is always fine-grain */
113 		dml_clk_table->dppclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dppclk_levels >= 2 ? 2 : 1;
114 		dml_clk_table->dppclk.clk_values_khz[0] = 0;
115 		dml_clk_table->dppclk.clk_values_khz[1] = dc_clk_table->entries[dc_clk_table->num_entries_per_clk.num_dppclk_levels - 1].dppclk_mhz * 1000;
116 	}
117 
118 	/* dtbclk */
119 	if (dc_clk_table->num_entries_per_clk.num_dtbclk_levels) {
120 		dml_clk_table->dtbclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_dtbclk_levels;
121 		for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) {
122 			if (i < dml_clk_table->dtbclk.num_clk_values) {
123 				dml_clk_table->dtbclk.clk_values_khz[i] = dc_clk_table->entries[i].dtbclk_mhz * 1000;
124 			} else {
125 				dml_clk_table->dtbclk.clk_values_khz[i] = 0;
126 			}
127 		}
128 	}
129 
130 	/* socclk */
131 	if (dc_clk_table->num_entries_per_clk.num_socclk_levels) {
132 		dml_clk_table->socclk.num_clk_values = dc_clk_table->num_entries_per_clk.num_socclk_levels;
133 		for (i = 0; i < min(DML_MAX_CLK_TABLE_SIZE, MAX_NUM_DPM_LVL); i++) {
134 			if (i < dml_clk_table->socclk.num_clk_values) {
135 				dml_clk_table->socclk.clk_values_khz[i] = dc_clk_table->entries[i].socclk_mhz * 1000;
136 			} else {
137 				dml_clk_table->socclk.clk_values_khz[i] = 0;
138 			}
139 		}
140 	}
141 
142 	/* dram config */
143 	dml_clk_table->dram_config.channel_count = dc_bw_params->num_channels;
144 	dml_clk_table->dram_config.channel_width_bytes = dc_bw_params->dram_channel_width_bytes;
145 }
146 
dcn42_update_soc_bb_with_values_from_clk_mgr(struct dml2_soc_bb * soc_bb,const struct dc * dc)147 static void dcn42_update_soc_bb_with_values_from_clk_mgr(struct dml2_soc_bb *soc_bb, const struct dc *dc)
148 {
149 	soc_bb->dprefclk_mhz = dc->clk_mgr->dprefclk_khz / 1000;
150 	soc_bb->dispclk_dppclk_vco_speed_mhz = dc->clk_mgr->dentist_vco_freq_khz / 1000.0;
151 	soc_bb->mall_allocated_for_dcn_mbytes = dc->caps.mall_size_total / (1024 * 1024);
152 
153 	if (dc->clk_mgr->funcs->is_smu_present &&
154 			dc->clk_mgr->funcs->is_smu_present(dc->clk_mgr)) {
155 		dcn42_convert_dc_clock_table_to_soc_bb_clock_table(&soc_bb->clk_table, &soc_bb->vmin_limit,
156 			dc->clk_mgr->bw_params);
157 	}
158 
159 	if (dc->clk_mgr->bw_params->vram_type == Ddr5MemType) {
160 		soc_bb->power_management_parameters = dcn42_ddr5_power_management_parameters;
161 	}
162 }
163 
apply_soc_bb_updates(struct dml2_soc_bb * soc_bb,const struct dc * dc,const struct dml2_configuration_options * config)164 static void apply_soc_bb_updates(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config)
165 {
166 	(void)config;
167 	/* Individual modification can be overwritten even if it was obtained by a previous function.
168 	 * Modifications are acquired in order of priority (lowest to highest).
169 	 */
170 	dc_assert_fp_enabled();
171 
172 	dcn42_update_soc_bb_with_values_from_clk_mgr(soc_bb, dc);
173 	dcn401_update_soc_bb_with_values_from_vbios(soc_bb, dc);
174 	dcn401_update_soc_bb_with_values_from_software_policy(soc_bb, dc);
175 }
176 
dcn42_get_soc_bb(struct dml2_soc_bb * soc_bb,const struct dc * dc,const struct dml2_configuration_options * config)177 void dcn42_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config)
178 {
179 	//get default soc_bb with static values
180 	get_default_soc_bb(soc_bb, dc);
181 	//update soc_bb values with more accurate values
182 	apply_soc_bb_updates(soc_bb, dc, config);
183 }
184 
dcn42_get_ip_caps(struct dml2_ip_capabilities * ip_caps)185 static void dcn42_get_ip_caps(struct dml2_ip_capabilities *ip_caps)
186 {
187 	*ip_caps = dml2_dcn42_max_ip_caps;
188 }
189 
190 static struct soc_and_ip_translator_funcs dcn42_translator_funcs = {
191 	.get_soc_bb = dcn42_get_soc_bb,
192 	.get_ip_caps = dcn42_get_ip_caps,
193 };
194 
dcn42_construct_soc_and_ip_translator(struct soc_and_ip_translator * soc_and_ip_translator)195 void dcn42_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator)
196 {
197 	soc_and_ip_translator->translator_funcs = &dcn42_translator_funcs;
198 }
199 
200