xref: /linux/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c (revision 92c4c9fdc838d3b41a996bb700ea64b9e78fc7ea)
1 // SPDX-License-Identifier: MIT
2 //
3 // Copyright 2024 Advanced Micro Devices, Inc.
4 
5 #include "dml2_dpmm_dcn4.h"
6 #include "dml2_internal_shared_types.h"
7 #include "dml_top_types.h"
8 #include "lib_float_math.h"
9 
dram_bw_kbps_to_uclk_khz(unsigned long long bandwidth_kbps,const struct dml2_dram_params * dram_config,struct dml2_mcg_dram_bw_to_min_clk_table * dram_bw_table)10 static double dram_bw_kbps_to_uclk_khz(unsigned long long bandwidth_kbps, const struct dml2_dram_params *dram_config, struct dml2_mcg_dram_bw_to_min_clk_table *dram_bw_table)
11 {
12 	double uclk_khz = 0;
13 
14 	if (!dram_config->alt_clock_bw_conversion) {
15 		unsigned long uclk_bytes_per_tick = 0;
16 
17 		uclk_bytes_per_tick = dram_config->channel_count * dram_config->channel_width_bytes * dram_config->transactions_per_clock;
18 		uclk_khz = (double)bandwidth_kbps / uclk_bytes_per_tick;
19 	} else {
20 		unsigned int i;
21 		/* For lpddr5 bytes per tick changes with mpstate, use table to find uclk*/
22 		for (i = 0; i < dram_bw_table->num_entries; i++)
23 			if (dram_bw_table->entries[i].pre_derate_dram_bw_kbps >= bandwidth_kbps) {
24 				uclk_khz = dram_bw_table->entries[i].min_uclk_khz;
25 				break;
26 			}
27 	}
28 
29 	return uclk_khz;
30 }
31 
get_minimum_clocks_for_latency(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out,double * uclk,double * fclk,double * dcfclk)32 static void get_minimum_clocks_for_latency(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out,
33 	double *uclk,
34 	double *fclk,
35 	double *dcfclk)
36 {
37 	int min_clock_index_for_latency;
38 
39 	if (in_out->display_cfg->stage3.success)
40 		min_clock_index_for_latency = in_out->display_cfg->stage3.min_clk_index_for_latency;
41 	else
42 		min_clock_index_for_latency = in_out->display_cfg->stage1.min_clk_index_for_latency;
43 
44 	*dcfclk = in_out->min_clk_table->dram_bw_table.entries[min_clock_index_for_latency].min_dcfclk_khz;
45 	*fclk = in_out->min_clk_table->dram_bw_table.entries[min_clock_index_for_latency].min_fclk_khz;
46 	*uclk = dram_bw_kbps_to_uclk_khz(in_out->min_clk_table->dram_bw_table.entries[min_clock_index_for_latency].pre_derate_dram_bw_kbps,
47 		&in_out->soc_bb->clk_table.dram_config, &in_out->min_clk_table->dram_bw_table);
48 }
49 
dml_round_up(double a)50 static unsigned long dml_round_up(double a)
51 {
52 	if (a - (unsigned long)a > 0) {
53 		return ((unsigned long)a) + 1;
54 	}
55 	return (unsigned long)a;
56 }
57 
calculate_system_active_minimums(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out)58 static void calculate_system_active_minimums(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out)
59 {
60 	double min_uclk_avg, min_uclk_urgent, min_uclk_bw;
61 	double min_fclk_avg, min_fclk_urgent, min_fclk_bw;
62 	double min_dcfclk_avg, min_dcfclk_urgent, min_dcfclk_bw;
63 	double min_uclk_latency, min_fclk_latency, min_dcfclk_latency;
64 	const struct dml2_core_mode_support_result *mode_support_result = &in_out->display_cfg->mode_support_result;
65 
66 	min_uclk_avg = dram_bw_kbps_to_uclk_khz((unsigned long long)(mode_support_result->global.active.average_bw_dram_kbps
67 											/ ((double)in_out->soc_bb->qos_parameters.derate_table.system_active_average.dram_derate_percent_pixel / 100)),
68 							&in_out->soc_bb->clk_table.dram_config, &in_out->min_clk_table->dram_bw_table);
69 
70 	if (in_out->display_cfg->display_config.hostvm_enable)
71 		min_uclk_urgent = dram_bw_kbps_to_uclk_khz((unsigned long long)(mode_support_result->global.active.urgent_bw_dram_kbps
72 										/ ((double)in_out->soc_bb->qos_parameters.derate_table.system_active_urgent.dram_derate_percent_pixel_and_vm / 100)),
73 							 &in_out->soc_bb->clk_table.dram_config, &in_out->min_clk_table->dram_bw_table);
74 	else
75 		min_uclk_urgent = dram_bw_kbps_to_uclk_khz((unsigned long long)(mode_support_result->global.active.urgent_bw_dram_kbps
76 										/ ((double)in_out->soc_bb->qos_parameters.derate_table.system_active_urgent.dram_derate_percent_pixel / 100)),
77 							 &in_out->soc_bb->clk_table.dram_config, &in_out->min_clk_table->dram_bw_table);
78 
79 	min_uclk_bw = min_uclk_urgent > min_uclk_avg ? min_uclk_urgent : min_uclk_avg;
80 
81 	min_fclk_avg = (double)mode_support_result->global.active.average_bw_sdp_kbps / in_out->soc_bb->fabric_datapath_to_dcn_data_return_bytes;
82 	min_fclk_avg = (double)min_fclk_avg / ((double)in_out->soc_bb->qos_parameters.derate_table.system_active_average.fclk_derate_percent / 100);
83 
84 	min_fclk_urgent = (double)mode_support_result->global.active.urgent_bw_sdp_kbps / in_out->soc_bb->fabric_datapath_to_dcn_data_return_bytes;
85 	min_fclk_urgent = (double)min_fclk_urgent / ((double)in_out->soc_bb->qos_parameters.derate_table.system_active_urgent.fclk_derate_percent / 100);
86 
87 	min_fclk_bw = min_fclk_urgent > min_fclk_avg ? min_fclk_urgent : min_fclk_avg;
88 
89 	min_dcfclk_avg = (double)mode_support_result->global.active.average_bw_sdp_kbps / in_out->soc_bb->return_bus_width_bytes;
90 	min_dcfclk_avg = (double)min_dcfclk_avg / ((double)in_out->soc_bb->qos_parameters.derate_table.system_active_average.dcfclk_derate_percent / 100);
91 
92 	min_dcfclk_urgent = (double)mode_support_result->global.active.urgent_bw_sdp_kbps / in_out->soc_bb->return_bus_width_bytes;
93 	min_dcfclk_urgent = (double)min_dcfclk_urgent / ((double)in_out->soc_bb->qos_parameters.derate_table.system_active_urgent.dcfclk_derate_percent / 100);
94 
95 	min_dcfclk_bw = min_dcfclk_urgent > min_dcfclk_avg ? min_dcfclk_urgent : min_dcfclk_avg;
96 
97 	get_minimum_clocks_for_latency(in_out, &min_uclk_latency, &min_fclk_latency, &min_dcfclk_latency);
98 
99 	in_out->programming->min_clocks.dcn4x.active.uclk_khz = dml_round_up(min_uclk_bw > min_uclk_latency ? min_uclk_bw : min_uclk_latency);
100 	in_out->programming->min_clocks.dcn4x.active.fclk_khz = dml_round_up(min_fclk_bw > min_fclk_latency ? min_fclk_bw : min_fclk_latency);
101 	in_out->programming->min_clocks.dcn4x.active.dcfclk_khz = dml_round_up(min_dcfclk_bw > min_dcfclk_latency ? min_dcfclk_bw : min_dcfclk_latency);
102 }
103 
calculate_svp_prefetch_minimums(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out)104 static void calculate_svp_prefetch_minimums(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out)
105 {
106 	double min_uclk_avg, min_uclk_urgent, min_uclk_bw;
107 	double min_fclk_avg, min_fclk_urgent, min_fclk_bw;
108 	double min_dcfclk_avg, min_dcfclk_urgent, min_dcfclk_bw;
109 	double min_fclk_latency, min_dcfclk_latency;
110 	double min_uclk_latency;
111 	const struct dml2_core_mode_support_result *mode_support_result = &in_out->display_cfg->mode_support_result;
112 
113 	/* assumes DF throttling is enabled */
114 	min_uclk_avg = dram_bw_kbps_to_uclk_khz((unsigned long long)(mode_support_result->global.svp_prefetch.average_bw_dram_kbps
115 								/ ((double)in_out->soc_bb->qos_parameters.derate_table.dcn_mall_prefetch_average.dram_derate_percent_pixel / 100)),
116 						&in_out->soc_bb->clk_table.dram_config, &in_out->min_clk_table->dram_bw_table);
117 
118 	min_uclk_urgent = dram_bw_kbps_to_uclk_khz((unsigned long long)(mode_support_result->global.svp_prefetch.urgent_bw_dram_kbps
119 								/ ((double)in_out->soc_bb->qos_parameters.derate_table.dcn_mall_prefetch_urgent.dram_derate_percent_pixel / 100)),
120 						 &in_out->soc_bb->clk_table.dram_config, &in_out->min_clk_table->dram_bw_table);
121 
122 	min_uclk_bw = min_uclk_urgent > min_uclk_avg ? min_uclk_urgent : min_uclk_avg;
123 
124 	min_fclk_avg = (double)mode_support_result->global.svp_prefetch.average_bw_sdp_kbps / in_out->soc_bb->fabric_datapath_to_dcn_data_return_bytes;
125 	min_fclk_avg = (double)min_fclk_avg / ((double)in_out->soc_bb->qos_parameters.derate_table.dcn_mall_prefetch_average.fclk_derate_percent / 100);
126 
127 	min_fclk_urgent = (double)mode_support_result->global.svp_prefetch.urgent_bw_sdp_kbps / in_out->soc_bb->fabric_datapath_to_dcn_data_return_bytes;
128 	min_fclk_urgent = (double)min_fclk_urgent / ((double)in_out->soc_bb->qos_parameters.derate_table.dcn_mall_prefetch_urgent.fclk_derate_percent / 100);
129 
130 	min_fclk_bw = min_fclk_urgent > min_fclk_avg ? min_fclk_urgent : min_fclk_avg;
131 
132 	min_dcfclk_avg = (double)mode_support_result->global.svp_prefetch.average_bw_sdp_kbps / in_out->soc_bb->return_bus_width_bytes;
133 	min_dcfclk_avg = (double)min_dcfclk_avg / ((double)in_out->soc_bb->qos_parameters.derate_table.dcn_mall_prefetch_average.dcfclk_derate_percent / 100);
134 
135 	min_dcfclk_urgent = (double)mode_support_result->global.svp_prefetch.urgent_bw_sdp_kbps / in_out->soc_bb->return_bus_width_bytes;
136 	min_dcfclk_urgent = (double)min_dcfclk_urgent / ((double)in_out->soc_bb->qos_parameters.derate_table.dcn_mall_prefetch_urgent.dcfclk_derate_percent / 100);
137 
138 	min_dcfclk_bw = min_dcfclk_urgent > min_dcfclk_avg ? min_dcfclk_urgent : min_dcfclk_avg;
139 
140 	get_minimum_clocks_for_latency(in_out, &min_uclk_latency, &min_fclk_latency, &min_dcfclk_latency);
141 
142 	in_out->programming->min_clocks.dcn4x.svp_prefetch.uclk_khz = dml_round_up(min_uclk_bw > min_uclk_latency ? min_uclk_bw : min_uclk_latency);
143 	in_out->programming->min_clocks.dcn4x.svp_prefetch.fclk_khz = dml_round_up(min_fclk_bw > min_fclk_latency ? min_fclk_bw : min_fclk_latency);
144 	in_out->programming->min_clocks.dcn4x.svp_prefetch.dcfclk_khz = dml_round_up(min_dcfclk_bw > min_dcfclk_latency ? min_dcfclk_bw : min_dcfclk_latency);
145 
146 	/* assumes DF throttling is disabled */
147 	min_uclk_avg = dram_bw_kbps_to_uclk_khz((unsigned long long)(mode_support_result->global.svp_prefetch.average_bw_dram_kbps
148 										/ ((double)in_out->soc_bb->qos_parameters.derate_table.system_active_average.dram_derate_percent_pixel / 100)),
149 								&in_out->soc_bb->clk_table.dram_config, &in_out->min_clk_table->dram_bw_table);
150 
151 	min_uclk_urgent = dram_bw_kbps_to_uclk_khz((unsigned long long)(mode_support_result->global.svp_prefetch.urgent_bw_dram_kbps
152 										/ ((double)in_out->soc_bb->qos_parameters.derate_table.system_active_urgent.dram_derate_percent_pixel / 100)),
153 								&in_out->soc_bb->clk_table.dram_config, &in_out->min_clk_table->dram_bw_table);
154 
155 	min_uclk_bw = min_uclk_urgent > min_uclk_avg ? min_uclk_urgent : min_uclk_avg;
156 
157 	min_fclk_avg = (double)mode_support_result->global.svp_prefetch.average_bw_sdp_kbps / in_out->soc_bb->fabric_datapath_to_dcn_data_return_bytes;
158 	min_fclk_avg = (double)min_fclk_avg / ((double)in_out->soc_bb->qos_parameters.derate_table.system_active_average.fclk_derate_percent / 100);
159 
160 	min_fclk_urgent = (double)mode_support_result->global.svp_prefetch.urgent_bw_sdp_kbps / in_out->soc_bb->fabric_datapath_to_dcn_data_return_bytes;
161 	min_fclk_urgent = (double)min_fclk_urgent / ((double)in_out->soc_bb->qos_parameters.derate_table.system_active_urgent.fclk_derate_percent / 100);
162 
163 	min_fclk_bw = min_fclk_urgent > min_fclk_avg ? min_fclk_urgent : min_fclk_avg;
164 
165 	min_dcfclk_avg = (double)mode_support_result->global.svp_prefetch.average_bw_sdp_kbps / in_out->soc_bb->return_bus_width_bytes;
166 	min_dcfclk_avg = (double)min_dcfclk_avg / ((double)in_out->soc_bb->qos_parameters.derate_table.system_active_average.dcfclk_derate_percent / 100);
167 
168 	min_dcfclk_urgent = (double)mode_support_result->global.svp_prefetch.urgent_bw_sdp_kbps / in_out->soc_bb->return_bus_width_bytes;
169 	min_dcfclk_urgent = (double)min_dcfclk_urgent / ((double)in_out->soc_bb->qos_parameters.derate_table.system_active_urgent.dcfclk_derate_percent / 100);
170 
171 	min_dcfclk_bw = min_dcfclk_urgent > min_dcfclk_avg ? min_dcfclk_urgent : min_dcfclk_avg;
172 
173 	get_minimum_clocks_for_latency(in_out, &min_uclk_latency, &min_fclk_latency, &min_dcfclk_latency);
174 
175 	in_out->programming->min_clocks.dcn4x.svp_prefetch_no_throttle.uclk_khz = dml_round_up(min_uclk_bw > min_uclk_latency ? min_uclk_bw : min_uclk_latency);
176 	in_out->programming->min_clocks.dcn4x.svp_prefetch_no_throttle.fclk_khz = dml_round_up(min_fclk_bw > min_fclk_latency ? min_fclk_bw : min_fclk_latency);
177 	in_out->programming->min_clocks.dcn4x.svp_prefetch_no_throttle.dcfclk_khz = dml_round_up(min_dcfclk_bw > min_dcfclk_latency ? min_dcfclk_bw : min_dcfclk_latency);
178 }
179 
calculate_idle_minimums(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out)180 static void calculate_idle_minimums(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out)
181 {
182 	double min_uclk_avg;
183 	double min_fclk_avg;
184 	double min_dcfclk_avg;
185 	double min_uclk_latency, min_fclk_latency, min_dcfclk_latency;
186 	const struct dml2_core_mode_support_result *mode_support_result = &in_out->display_cfg->mode_support_result;
187 
188 	min_uclk_avg = dram_bw_kbps_to_uclk_khz((unsigned long long)(mode_support_result->global.active.average_bw_dram_kbps
189 										/ ((double)in_out->soc_bb->qos_parameters.derate_table.system_idle_average.dram_derate_percent_pixel / 100)),
190 								&in_out->soc_bb->clk_table.dram_config, &in_out->min_clk_table->dram_bw_table);
191 
192 	min_fclk_avg = (double)mode_support_result->global.active.average_bw_sdp_kbps / in_out->soc_bb->fabric_datapath_to_dcn_data_return_bytes;
193 	min_fclk_avg = (double)min_fclk_avg / ((double)in_out->soc_bb->qos_parameters.derate_table.system_idle_average.fclk_derate_percent / 100);
194 
195 	min_dcfclk_avg = (double)mode_support_result->global.active.average_bw_sdp_kbps / in_out->soc_bb->return_bus_width_bytes;
196 	min_dcfclk_avg = (double)min_dcfclk_avg / ((double)in_out->soc_bb->qos_parameters.derate_table.system_idle_average.dcfclk_derate_percent / 100);
197 
198 	get_minimum_clocks_for_latency(in_out, &min_uclk_latency, &min_fclk_latency, &min_dcfclk_latency);
199 
200 	in_out->programming->min_clocks.dcn4x.idle.uclk_khz = dml_round_up(min_uclk_avg > min_uclk_latency ? min_uclk_avg : min_uclk_latency);
201 	in_out->programming->min_clocks.dcn4x.idle.fclk_khz = dml_round_up(min_fclk_avg > min_fclk_latency ? min_fclk_avg : min_fclk_latency);
202 	in_out->programming->min_clocks.dcn4x.idle.dcfclk_khz = dml_round_up(min_dcfclk_avg > min_dcfclk_latency ? min_dcfclk_avg : min_dcfclk_latency);
203 }
204 
add_margin_and_round_to_dfs_grainularity(double clock_khz,double margin,unsigned long vco_freq_khz,unsigned long * rounded_khz,uint32_t * divider_id)205 static bool add_margin_and_round_to_dfs_grainularity(double clock_khz, double margin, unsigned long vco_freq_khz, unsigned long *rounded_khz, uint32_t *divider_id)
206 {
207 	enum dentist_divider_range {
208 		DFS_DIVIDER_RANGE_1_START = 8, /* 2.00 */
209 		DFS_DIVIDER_RANGE_1_STEP = 1, /* 0.25 */
210 		DFS_DIVIDER_RANGE_2_START = 64, /* 16.00 */
211 		DFS_DIVIDER_RANGE_2_STEP = 2, /* 0.50 */
212 		DFS_DIVIDER_RANGE_3_START = 128, /* 32.00 */
213 		DFS_DIVIDER_RANGE_3_STEP = 4, /* 1.00 */
214 		DFS_DIVIDER_RANGE_4_START = 248, /* 62.00 */
215 		DFS_DIVIDER_RANGE_4_STEP = 264, /* 66.00 */
216 		DFS_DIVIDER_RANGE_SCALE_FACTOR = 4
217 	};
218 
219 	enum DFS_base_divider_id {
220 		DFS_BASE_DID_1 = 0x08,
221 		DFS_BASE_DID_2 = 0x40,
222 		DFS_BASE_DID_3 = 0x60,
223 		DFS_BASE_DID_4 = 0x7e,
224 		DFS_MAX_DID = 0x7f
225 	};
226 
227 	unsigned int divider;
228 
229 	if (clock_khz < 1 || vco_freq_khz < 1 || clock_khz > vco_freq_khz)
230 		return false;
231 
232 	clock_khz *= 1.0 + margin;
233 
234 	divider = (unsigned int)((int)DFS_DIVIDER_RANGE_SCALE_FACTOR * (vco_freq_khz / clock_khz));
235 
236 	/* we want to floor here to get higher clock than required rather than lower */
237 	if (divider < DFS_DIVIDER_RANGE_2_START) {
238 		if (divider < DFS_DIVIDER_RANGE_1_START)
239 			*divider_id = DFS_BASE_DID_1;
240 		else
241 			*divider_id = DFS_BASE_DID_1 + ((divider - DFS_DIVIDER_RANGE_1_START) / DFS_DIVIDER_RANGE_1_STEP);
242 	} else if (divider < DFS_DIVIDER_RANGE_3_START) {
243 		*divider_id = DFS_BASE_DID_2 + ((divider - DFS_DIVIDER_RANGE_2_START) / DFS_DIVIDER_RANGE_2_STEP);
244 	} else if (divider < DFS_DIVIDER_RANGE_4_START) {
245 		*divider_id = DFS_BASE_DID_3 + ((divider - DFS_DIVIDER_RANGE_3_START) / DFS_DIVIDER_RANGE_3_STEP);
246 	} else {
247 		*divider_id = DFS_BASE_DID_4 + ((divider - DFS_DIVIDER_RANGE_4_START) / DFS_DIVIDER_RANGE_4_STEP);
248 		if (*divider_id > DFS_MAX_DID)
249 			*divider_id = DFS_MAX_DID;
250 	}
251 
252 	*rounded_khz = vco_freq_khz * DFS_DIVIDER_RANGE_SCALE_FACTOR / divider;
253 
254 	return true;
255 }
256 
round_to_non_dfs_granularity(unsigned long dispclk_khz,unsigned long dpprefclk_khz,unsigned long dtbrefclk_khz,unsigned long * rounded_dispclk_khz,unsigned long * rounded_dpprefclk_khz,unsigned long * rounded_dtbrefclk_khz)257 static bool round_to_non_dfs_granularity(unsigned long dispclk_khz, unsigned long dpprefclk_khz, unsigned long dtbrefclk_khz,
258 	unsigned long *rounded_dispclk_khz, unsigned long *rounded_dpprefclk_khz, unsigned long *rounded_dtbrefclk_khz)
259 {
260 	unsigned long pll_frequency_khz;
261 
262 	pll_frequency_khz = (unsigned long) math_max2(600000, math_ceil2(math_max3(dispclk_khz, dpprefclk_khz, dtbrefclk_khz), 1000));
263 
264 	*rounded_dispclk_khz = pll_frequency_khz / (unsigned long) math_min2(pll_frequency_khz / dispclk_khz, 32);
265 
266 	*rounded_dpprefclk_khz = pll_frequency_khz / (unsigned long) math_min2(pll_frequency_khz / dpprefclk_khz, 32);
267 
268 	if (dtbrefclk_khz > 0) {
269 		*rounded_dtbrefclk_khz = pll_frequency_khz / (unsigned long) math_min2(pll_frequency_khz / dtbrefclk_khz, 32);
270 	} else {
271 		*rounded_dtbrefclk_khz = 0;
272 	}
273 
274 	return true;
275 }
276 
round_up_and_copy_to_next_dpm(unsigned long min_value,unsigned long * rounded_value,const struct dml2_clk_table * clock_table)277 static bool round_up_and_copy_to_next_dpm(unsigned long min_value, unsigned long *rounded_value, const struct dml2_clk_table *clock_table)
278 {
279 	bool result = false;
280 	int index = 0;
281 
282 	/* Guard against empty clock tables (e.g. DTBCLK on DCN42B where the
283 	 * clock is tied off and num_clk_values == 0).  Without this check the
284 	 * else-if branch below would evaluate
285 	 * clk_values_khz[num_clk_values - 1] with num_clk_values == 0, which
286 	 * wraps the unsigned char index to 255 — a 235-element out-of-bounds
287 	 * read on an array of DML_MAX_CLK_TABLE_SIZE (20) entries.
288 	 *
289 	 * Semantic: if the clock doesn't exist on this ASIC but no frequency
290 	 * is required (min_value == 0), the request is trivially satisfied.
291 	 * If a non-zero frequency is required but the clock is absent, the
292 	 * configuration is unsupportable.
293 	 */
294 	if (clock_table->num_clk_values == 0) {
295 		if (min_value == 0) {
296 			*rounded_value = 0;
297 			return true;
298 		}
299 		return false;
300 	}
301 
302 	if (clock_table->num_clk_values > 2) {
303 		while (index < clock_table->num_clk_values && clock_table->clk_values_khz[index] < min_value)
304 			index++;
305 
306 		if (index < clock_table->num_clk_values) {
307 			*rounded_value = clock_table->clk_values_khz[index];
308 			result = true;
309 		}
310 	} else if (clock_table->clk_values_khz[clock_table->num_clk_values - 1] >= min_value) {
311 		*rounded_value = min_value;
312 		result = true;
313 	}
314 	return result;
315 }
316 
round_up_to_next_dpm(unsigned long * clock_value,const struct dml2_clk_table * clock_table)317 static bool round_up_to_next_dpm(unsigned long *clock_value, const struct dml2_clk_table *clock_table)
318 {
319 	return round_up_and_copy_to_next_dpm(*clock_value, clock_value, clock_table);
320 }
321 
map_soc_min_clocks_to_dpm_fine_grained(struct dml2_display_cfg_programming * display_cfg,const struct dml2_soc_state_table * state_table)322 static bool map_soc_min_clocks_to_dpm_fine_grained(struct dml2_display_cfg_programming *display_cfg, const struct dml2_soc_state_table *state_table)
323 {
324 	bool result;
325 
326 	result = round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.active.dcfclk_khz, &state_table->dcfclk);
327 	if (result)
328 		result = round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.active.fclk_khz, &state_table->fclk);
329 	if (result)
330 		result = round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.active.uclk_khz, &state_table->uclk);
331 
332 	if (result)
333 		result = round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.svp_prefetch.dcfclk_khz, &state_table->dcfclk);
334 	if (result)
335 		result = round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.svp_prefetch.fclk_khz, &state_table->fclk);
336 	if (result)
337 		result = round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.svp_prefetch.uclk_khz, &state_table->uclk);
338 
339 	if (result)
340 		result = round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.idle.dcfclk_khz, &state_table->dcfclk);
341 	if (result)
342 		result = round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.idle.fclk_khz, &state_table->fclk);
343 	if (result)
344 		result = round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.idle.uclk_khz, &state_table->uclk);
345 
346 	/* these clocks are optional, so they can fail to map, in which case map all to 0 */
347 	if (result) {
348 		if (!round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.svp_prefetch_no_throttle.dcfclk_khz, &state_table->dcfclk) ||
349 			!round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.svp_prefetch_no_throttle.fclk_khz, &state_table->fclk) ||
350 			!round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.svp_prefetch_no_throttle.uclk_khz, &state_table->uclk)) {
351 			display_cfg->min_clocks.dcn4x.svp_prefetch_no_throttle.dcfclk_khz = 0;
352 			display_cfg->min_clocks.dcn4x.svp_prefetch_no_throttle.fclk_khz = 0;
353 			display_cfg->min_clocks.dcn4x.svp_prefetch_no_throttle.uclk_khz = 0;
354 		}
355 	}
356 
357 	return result;
358 }
359 
map_soc_min_clocks_to_dpm_coarse_grained(struct dml2_display_cfg_programming * display_cfg,const struct dml2_soc_state_table * state_table)360 static bool map_soc_min_clocks_to_dpm_coarse_grained(struct dml2_display_cfg_programming *display_cfg, const struct dml2_soc_state_table *state_table)
361 {
362 	bool result;
363 	int index;
364 
365 	result = false;
366 	for (index = 0; index < state_table->uclk.num_clk_values; index++) {
367 		if (display_cfg->min_clocks.dcn4x.active.dcfclk_khz <= state_table->dcfclk.clk_values_khz[index] &&
368 			display_cfg->min_clocks.dcn4x.active.fclk_khz <= state_table->fclk.clk_values_khz[index] &&
369 			display_cfg->min_clocks.dcn4x.active.uclk_khz <= state_table->uclk.clk_values_khz[index]) {
370 			display_cfg->min_clocks.dcn4x.active.dcfclk_khz = state_table->dcfclk.clk_values_khz[index];
371 			display_cfg->min_clocks.dcn4x.active.fclk_khz = state_table->fclk.clk_values_khz[index];
372 			display_cfg->min_clocks.dcn4x.active.uclk_khz = state_table->uclk.clk_values_khz[index];
373 			result = true;
374 			break;
375 		}
376 	}
377 
378 	if (result) {
379 		result = false;
380 		for (index = 0; index < state_table->uclk.num_clk_values; index++) {
381 			if (display_cfg->min_clocks.dcn4x.idle.dcfclk_khz <= state_table->dcfclk.clk_values_khz[index] &&
382 				display_cfg->min_clocks.dcn4x.idle.fclk_khz <= state_table->fclk.clk_values_khz[index] &&
383 				display_cfg->min_clocks.dcn4x.idle.uclk_khz <= state_table->uclk.clk_values_khz[index]) {
384 				display_cfg->min_clocks.dcn4x.idle.dcfclk_khz = state_table->dcfclk.clk_values_khz[index];
385 				display_cfg->min_clocks.dcn4x.idle.fclk_khz = state_table->fclk.clk_values_khz[index];
386 				display_cfg->min_clocks.dcn4x.idle.uclk_khz = state_table->uclk.clk_values_khz[index];
387 				result = true;
388 				break;
389 			}
390 		}
391 	}
392 
393 	// SVP is not supported on any coarse grained SoCs
394 	display_cfg->min_clocks.dcn4x.svp_prefetch.dcfclk_khz = 0;
395 	display_cfg->min_clocks.dcn4x.svp_prefetch.fclk_khz = 0;
396 	display_cfg->min_clocks.dcn4x.svp_prefetch.uclk_khz = 0;
397 
398 	return result;
399 }
400 
map_min_clocks_to_dpm(const struct dml2_core_mode_support_result * mode_support_result,struct dml2_display_cfg_programming * display_cfg,const struct dml2_soc_state_table * state_table)401 static bool map_min_clocks_to_dpm(const struct dml2_core_mode_support_result *mode_support_result, struct dml2_display_cfg_programming *display_cfg, const struct dml2_soc_state_table *state_table)
402 {
403 	bool result = false;
404 	bool dcfclk_fine_grained = false, fclk_fine_grained = false, clock_state_count_identical = false;
405 	unsigned int i;
406 
407 	if (!state_table || !display_cfg)
408 		return false;
409 
410 	if (state_table->dcfclk.num_clk_values == 2) {
411 		dcfclk_fine_grained = true;
412 	}
413 
414 	if (state_table->fclk.num_clk_values == 2) {
415 		fclk_fine_grained = true;
416 	}
417 
418 	if (state_table->fclk.num_clk_values == state_table->dcfclk.num_clk_values &&
419 		state_table->fclk.num_clk_values == state_table->uclk.num_clk_values) {
420 		clock_state_count_identical = true;
421 	}
422 
423 	if (dcfclk_fine_grained || fclk_fine_grained || !clock_state_count_identical)
424 		result = map_soc_min_clocks_to_dpm_fine_grained(display_cfg, state_table);
425 	else
426 		result = map_soc_min_clocks_to_dpm_coarse_grained(display_cfg, state_table);
427 
428 	if (result)
429 		result = round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.dispclk_khz, &state_table->dispclk);
430 
431 	for (i = 0; i < DML2_MAX_DCN_PIPES; i++) {
432 		if (result)
433 			result = round_up_to_next_dpm(&display_cfg->plane_programming[i].min_clocks.dcn4x.dppclk_khz, &state_table->dppclk);
434 	}
435 
436 	for (i = 0; i < display_cfg->display_config.num_streams; i++) {
437 		if (result)
438 			result = round_up_and_copy_to_next_dpm(mode_support_result->per_stream[i].dscclk_khz, &display_cfg->stream_programming[i].min_clocks.dcn4x.dscclk_khz, &state_table->dscclk);
439 		if (result)
440 			result = round_up_and_copy_to_next_dpm(mode_support_result->per_stream[i].dtbclk_khz, &display_cfg->stream_programming[i].min_clocks.dcn4x.dtbclk_khz, &state_table->dtbclk);
441 		if (result)
442 			result = round_up_and_copy_to_next_dpm(mode_support_result->per_stream[i].phyclk_khz, &display_cfg->stream_programming[i].min_clocks.dcn4x.phyclk_khz, &state_table->phyclk);
443 	}
444 
445 	if (result)
446 		result = round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.dpprefclk_khz, &state_table->dppclk);
447 
448 	if (result)
449 		result = round_up_to_next_dpm(&display_cfg->min_clocks.dcn4x.dtbrefclk_khz, &state_table->dtbclk);
450 
451 	return result;
452 }
453 
are_timings_trivially_synchronizable(struct dml2_display_cfg * display_config,int mask)454 static bool are_timings_trivially_synchronizable(struct dml2_display_cfg *display_config, int mask)
455 {
456 	unsigned int i;
457 	bool identical = true;
458 	bool contains_drr = false;
459 	unsigned int remap_array[DML2_MAX_PLANES];
460 	unsigned int remap_array_size = 0;
461 
462 	// Create a remap array to enable simple iteration through only masked stream indicies
463 	for (i = 0; i < display_config->num_streams; i++) {
464 		if (mask & (0x1 << i)) {
465 			remap_array[remap_array_size++] = i;
466 		}
467 	}
468 
469 	// 0 or 1 display is always trivially synchronizable
470 	if (remap_array_size <= 1)
471 		return true;
472 
473 	// Check that all displays timings are the same
474 	for (i = 1; i < remap_array_size; i++) {
475 		if (memcmp(&display_config->stream_descriptors[remap_array[i - 1]].timing, &display_config->stream_descriptors[remap_array[i]].timing, sizeof(struct dml2_timing_cfg))) {
476 			identical = false;
477 			break;
478 		}
479 	}
480 
481 	// Check if any displays are drr
482 	for (i = 0; i < remap_array_size; i++) {
483 		if (display_config->stream_descriptors[remap_array[i]].timing.drr_config.enabled) {
484 			contains_drr = true;
485 			break;
486 		}
487 	}
488 
489 	// Trivial sync is possible if all displays are identical and none are DRR
490 	return !contains_drr && identical;
491 }
492 
find_smallest_idle_time_in_vblank_us(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out,int mask)493 static int find_smallest_idle_time_in_vblank_us(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out, int mask)
494 {
495 	unsigned int i;
496 	int min_idle_us = 0;
497 	unsigned int remap_array[DML2_MAX_PLANES];
498 	unsigned int remap_array_size = 0;
499 	const struct dml2_core_mode_support_result *mode_support_result = &in_out->display_cfg->mode_support_result;
500 
501 	// Create a remap array to enable simple iteration through only masked stream indicies
502 	for (i = 0; i < in_out->programming->display_config.num_streams; i++) {
503 		if (mask & (0x1 << i)) {
504 			remap_array[remap_array_size++] = i;
505 		}
506 	}
507 
508 	if (remap_array_size == 0)
509 		return 0;
510 
511 	min_idle_us = mode_support_result->cfg_support_info.stream_support_info[remap_array[0]].vblank_reserved_time_us;
512 
513 	for (i = 1; i < remap_array_size; i++) {
514 		if (min_idle_us > mode_support_result->cfg_support_info.stream_support_info[remap_array[i]].vblank_reserved_time_us)
515 			min_idle_us = mode_support_result->cfg_support_info.stream_support_info[remap_array[i]].vblank_reserved_time_us;
516 	}
517 
518 	return min_idle_us;
519 }
520 
determine_power_management_features_with_vblank_only(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out)521 static bool determine_power_management_features_with_vblank_only(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out)
522 {
523 	int min_idle_us;
524 
525 	if (are_timings_trivially_synchronizable(&in_out->programming->display_config, 0xF)) {
526 		min_idle_us = find_smallest_idle_time_in_vblank_us(in_out, 0xF);
527 
528 		if (min_idle_us >= in_out->soc_bb->power_management_parameters.dram_clk_change_blackout_us)
529 			in_out->programming->uclk_pstate_supported = true;
530 
531 		if (min_idle_us >= in_out->soc_bb->power_management_parameters.fclk_change_blackout_us)
532 			in_out->programming->fclk_pstate_supported = true;
533 	}
534 
535 	return true;
536 }
537 
get_displays_without_vactive_margin_mask(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out,int latency_hiding_requirement_us)538 static int get_displays_without_vactive_margin_mask(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out, int latency_hiding_requirement_us)
539 {
540 	unsigned int i;
541 	int displays_without_vactive_margin_mask = 0x0;
542 	const struct dml2_core_mode_support_result *mode_support_result = &in_out->display_cfg->mode_support_result;
543 
544 	for (i = 0; i < in_out->programming->display_config.num_planes; i++) {
545 		if (mode_support_result->cfg_support_info.plane_support_info[i].active_latency_hiding_us
546 			< latency_hiding_requirement_us)
547 			displays_without_vactive_margin_mask |= (0x1 << i);
548 	}
549 
550 	return displays_without_vactive_margin_mask;
551 }
552 
get_displays_with_fams_mask(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out,int latency_hiding_requirement_us)553 static int get_displays_with_fams_mask(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out, int latency_hiding_requirement_us)
554 {
555 	(void)latency_hiding_requirement_us;
556 	unsigned int i;
557 	int displays_with_fams_mask = 0x0;
558 
559 	for (i = 0; i < in_out->programming->display_config.num_planes; i++) {
560 		if (in_out->programming->display_config.plane_descriptors->overrides.legacy_svp_config != dml2_svp_mode_override_auto)
561 			displays_with_fams_mask |= (0x1 << i);
562 	}
563 
564 	return displays_with_fams_mask;
565 }
566 
determine_power_management_features_with_vactive_and_vblank(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out)567 static bool determine_power_management_features_with_vactive_and_vblank(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out)
568 {
569 	int displays_without_vactive_margin_mask = 0x0;
570 	int min_idle_us = 0;
571 
572 	if (in_out->programming->uclk_pstate_supported == false) {
573 		displays_without_vactive_margin_mask =
574 			get_displays_without_vactive_margin_mask(in_out, (int)(in_out->soc_bb->power_management_parameters.dram_clk_change_blackout_us));
575 
576 		if (are_timings_trivially_synchronizable(&in_out->programming->display_config, displays_without_vactive_margin_mask)) {
577 			min_idle_us = find_smallest_idle_time_in_vblank_us(in_out, displays_without_vactive_margin_mask);
578 
579 			if (min_idle_us >= in_out->soc_bb->power_management_parameters.dram_clk_change_blackout_us)
580 				in_out->programming->uclk_pstate_supported = true;
581 		}
582 	}
583 
584 	if (in_out->programming->fclk_pstate_supported == false) {
585 		displays_without_vactive_margin_mask =
586 			get_displays_without_vactive_margin_mask(in_out, (int)(in_out->soc_bb->power_management_parameters.fclk_change_blackout_us));
587 
588 		if (are_timings_trivially_synchronizable(&in_out->programming->display_config, displays_without_vactive_margin_mask)) {
589 			min_idle_us = find_smallest_idle_time_in_vblank_us(in_out, displays_without_vactive_margin_mask);
590 
591 			if (min_idle_us >= in_out->soc_bb->power_management_parameters.fclk_change_blackout_us)
592 				in_out->programming->fclk_pstate_supported = true;
593 		}
594 	}
595 
596 	return true;
597 }
598 
determine_power_management_features_with_fams(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out)599 static bool determine_power_management_features_with_fams(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out)
600 {
601 	int displays_without_vactive_margin_mask = 0x0;
602 	int displays_without_fams_mask = 0x0;
603 
604 	displays_without_vactive_margin_mask =
605 		get_displays_without_vactive_margin_mask(in_out, (int)(in_out->soc_bb->power_management_parameters.dram_clk_change_blackout_us));
606 
607 	displays_without_fams_mask =
608 		get_displays_with_fams_mask(in_out, (int)(in_out->soc_bb->power_management_parameters.dram_clk_change_blackout_us));
609 
610 	if ((displays_without_vactive_margin_mask & ~displays_without_fams_mask) == 0)
611 		in_out->programming->uclk_pstate_supported = true;
612 
613 	return true;
614 }
615 
clamp_uclk_to_max(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out)616 static void clamp_uclk_to_max(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out)
617 {
618 	in_out->programming->min_clocks.dcn4x.active.uclk_khz = in_out->soc_bb->clk_table.uclk.clk_values_khz[in_out->soc_bb->clk_table.uclk.num_clk_values - 1];
619 	in_out->programming->min_clocks.dcn4x.svp_prefetch.uclk_khz = in_out->soc_bb->clk_table.uclk.clk_values_khz[in_out->soc_bb->clk_table.uclk.num_clk_values - 1];
620 	in_out->programming->min_clocks.dcn4x.idle.uclk_khz = in_out->soc_bb->clk_table.uclk.clk_values_khz[in_out->soc_bb->clk_table.uclk.num_clk_values - 1];
621 }
622 
clamp_fclk_to_max(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out)623 static void clamp_fclk_to_max(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out)
624 {
625 	in_out->programming->min_clocks.dcn4x.active.fclk_khz = in_out->soc_bb->clk_table.fclk.clk_values_khz[in_out->soc_bb->clk_table.fclk.num_clk_values - 1];
626 	in_out->programming->min_clocks.dcn4x.idle.fclk_khz = in_out->soc_bb->clk_table.fclk.clk_values_khz[in_out->soc_bb->clk_table.fclk.num_clk_values - 1];
627 }
628 
map_mode_to_soc_dpm(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out)629 static bool map_mode_to_soc_dpm(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out)
630 {
631 	int i;
632 	bool result;
633 	double dispclk_khz;
634 	const struct dml2_core_mode_support_result *mode_support_result = &in_out->display_cfg->mode_support_result;
635 
636 	calculate_system_active_minimums(in_out);
637 	calculate_svp_prefetch_minimums(in_out);
638 	calculate_idle_minimums(in_out);
639 
640 	// In NV4, there's no support for FCLK or DCFCLK DPM change before SVP prefetch starts, therefore
641 	// active minimums must be boosted to prefetch minimums
642 	if (in_out->programming->min_clocks.dcn4x.svp_prefetch.uclk_khz > in_out->programming->min_clocks.dcn4x.active.uclk_khz)
643 		in_out->programming->min_clocks.dcn4x.active.uclk_khz = in_out->programming->min_clocks.dcn4x.svp_prefetch.uclk_khz;
644 
645 	if (in_out->programming->min_clocks.dcn4x.svp_prefetch.fclk_khz > in_out->programming->min_clocks.dcn4x.active.fclk_khz)
646 		in_out->programming->min_clocks.dcn4x.active.fclk_khz = in_out->programming->min_clocks.dcn4x.svp_prefetch.fclk_khz;
647 
648 	if (in_out->programming->min_clocks.dcn4x.svp_prefetch.dcfclk_khz > in_out->programming->min_clocks.dcn4x.active.dcfclk_khz)
649 		in_out->programming->min_clocks.dcn4x.active.dcfclk_khz = in_out->programming->min_clocks.dcn4x.svp_prefetch.dcfclk_khz;
650 
651 	// need some massaging for the dispclk ramping cases:
652 	dispclk_khz = mode_support_result->global.dispclk_khz * (1 + in_out->soc_bb->dcn_downspread_percent / 100.0) * (1.0 + in_out->ip->dispclk_ramp_margin_percent / 100.0);
653 	// ramping margin should not make dispclk exceed the maximum dispclk speed:
654 	dispclk_khz = math_min2(dispclk_khz, in_out->min_clk_table->max_clocks_khz.dispclk);
655 	// but still the required dispclk can be more than the maximum dispclk speed:
656 	dispclk_khz = math_max2(dispclk_khz, mode_support_result->global.dispclk_khz * (1 + in_out->soc_bb->dcn_downspread_percent / 100.0));
657 
658 	// DPP Ref is always set to max of all DPP clocks
659 	for (i = 0; i < DML2_MAX_DCN_PIPES; i++) {
660 		if (in_out->programming->min_clocks.dcn4x.dpprefclk_khz < mode_support_result->per_plane[i].dppclk_khz)
661 			in_out->programming->min_clocks.dcn4x.dpprefclk_khz = mode_support_result->per_plane[i].dppclk_khz;
662 	}
663 	in_out->programming->min_clocks.dcn4x.dpprefclk_khz = (unsigned long) (in_out->programming->min_clocks.dcn4x.dpprefclk_khz * (1 + in_out->soc_bb->dcn_downspread_percent / 100.0));
664 
665 	// DTB Ref is always set to max of all DTB clocks
666 	for (i = 0; i < DML2_MAX_DCN_PIPES; i++) {
667 		if (in_out->programming->min_clocks.dcn4x.dtbrefclk_khz < mode_support_result->per_stream[i].dtbclk_khz)
668 			in_out->programming->min_clocks.dcn4x.dtbrefclk_khz = mode_support_result->per_stream[i].dtbclk_khz;
669 	}
670 	in_out->programming->min_clocks.dcn4x.dtbrefclk_khz = (unsigned long)(in_out->programming->min_clocks.dcn4x.dtbrefclk_khz * (1 + in_out->soc_bb->dcn_downspread_percent / 100.0));
671 
672 	if (in_out->soc_bb->no_dfs) {
673 		round_to_non_dfs_granularity((unsigned long)dispclk_khz, in_out->programming->min_clocks.dcn4x.dpprefclk_khz, in_out->programming->min_clocks.dcn4x.dtbrefclk_khz,
674 			&in_out->programming->min_clocks.dcn4x.dispclk_khz, &in_out->programming->min_clocks.dcn4x.dpprefclk_khz, &in_out->programming->min_clocks.dcn4x.dtbrefclk_khz);
675 	} else {
676 		add_margin_and_round_to_dfs_grainularity(dispclk_khz, 0.0,
677 			(unsigned long)(in_out->soc_bb->dispclk_dppclk_vco_speed_mhz * 1000), &in_out->programming->min_clocks.dcn4x.dispclk_khz, &in_out->programming->min_clocks.dcn4x.divider_ids.dispclk_did);
678 
679 		add_margin_and_round_to_dfs_grainularity(in_out->programming->min_clocks.dcn4x.dpprefclk_khz, 0.0,
680 			(unsigned long)(in_out->soc_bb->dispclk_dppclk_vco_speed_mhz * 1000), &in_out->programming->min_clocks.dcn4x.dpprefclk_khz, &in_out->programming->min_clocks.dcn4x.divider_ids.dpprefclk_did);
681 
682 		add_margin_and_round_to_dfs_grainularity(in_out->programming->min_clocks.dcn4x.dtbrefclk_khz, 0.0,
683 			(unsigned long)(in_out->soc_bb->dispclk_dppclk_vco_speed_mhz * 1000), &in_out->programming->min_clocks.dcn4x.dtbrefclk_khz, &in_out->programming->min_clocks.dcn4x.divider_ids.dtbrefclk_did);
684 	}
685 
686 
687 	for (i = 0; i < DML2_MAX_DCN_PIPES; i++) {
688 		in_out->programming->plane_programming[i].min_clocks.dcn4x.dppclk_khz = (unsigned long)(in_out->programming->min_clocks.dcn4x.dpprefclk_khz / 255.0
689 			* math_ceil2(in_out->display_cfg->mode_support_result.per_plane[i].dppclk_khz * (1.0 + in_out->soc_bb->dcn_downspread_percent / 100.0) * 255.0 / in_out->programming->min_clocks.dcn4x.dpprefclk_khz, 1.0));
690 	}
691 
692 	in_out->programming->min_clocks.dcn4x.deepsleep_dcfclk_khz = mode_support_result->global.dcfclk_deepsleep_khz;
693 	in_out->programming->min_clocks.dcn4x.socclk_khz = mode_support_result->global.socclk_khz;
694 
695 	result = map_min_clocks_to_dpm(mode_support_result, in_out->programming, &in_out->soc_bb->clk_table);
696 
697 	// By default, all power management features are not enabled
698 	in_out->programming->fclk_pstate_supported = false;
699 	in_out->programming->uclk_pstate_supported = false;
700 
701 	return result;
702 }
703 
dpmm_dcn3_map_mode_to_soc_dpm(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out)704 bool dpmm_dcn3_map_mode_to_soc_dpm(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out)
705 {
706 	bool result;
707 
708 	result = map_mode_to_soc_dpm(in_out);
709 
710 	// Check if any can be enabled by nominal vblank idle time
711 	determine_power_management_features_with_vblank_only(in_out);
712 
713 	// Check if any can be enabled in vactive/vblank
714 	determine_power_management_features_with_vactive_and_vblank(in_out);
715 
716 	// Check if any can be enabled via fams
717 	determine_power_management_features_with_fams(in_out);
718 
719 	if (in_out->programming->uclk_pstate_supported == false)
720 		clamp_uclk_to_max(in_out);
721 
722 	if (in_out->programming->fclk_pstate_supported == false)
723 		clamp_fclk_to_max(in_out);
724 
725 	return result;
726 }
727 
dpmm_dcn4_map_mode_to_soc_dpm(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out * in_out)728 bool dpmm_dcn4_map_mode_to_soc_dpm(struct dml2_dpmm_map_mode_to_soc_dpm_params_in_out *in_out)
729 {
730 	bool result;
731 	int displays_without_vactive_margin_mask = 0x0;
732 	int min_idle_us = 0;
733 
734 	result = map_mode_to_soc_dpm(in_out);
735 
736 	if (in_out->display_cfg->stage3.success)
737 		in_out->programming->uclk_pstate_supported = true;
738 
739 	displays_without_vactive_margin_mask =
740 		get_displays_without_vactive_margin_mask(in_out, (int)(in_out->soc_bb->power_management_parameters.fclk_change_blackout_us));
741 
742 	if (displays_without_vactive_margin_mask == 0) {
743 		in_out->programming->fclk_pstate_supported = true;
744 	} else {
745 		if (are_timings_trivially_synchronizable(&in_out->programming->display_config, displays_without_vactive_margin_mask)) {
746 			min_idle_us = find_smallest_idle_time_in_vblank_us(in_out, displays_without_vactive_margin_mask);
747 
748 			if (min_idle_us >= in_out->soc_bb->power_management_parameters.fclk_change_blackout_us)
749 				in_out->programming->fclk_pstate_supported = true;
750 		}
751 	}
752 
753 	if (in_out->programming->uclk_pstate_supported == false)
754 		clamp_uclk_to_max(in_out);
755 
756 	if (in_out->programming->fclk_pstate_supported == false)
757 		clamp_fclk_to_max(in_out);
758 
759 	min_idle_us = find_smallest_idle_time_in_vblank_us(in_out, 0xFF);
760 	if (in_out->soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us > 0 &&
761 		min_idle_us >= in_out->soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us)
762 		in_out->programming->stutter.supported_in_blank = true;
763 	else
764 		in_out->programming->stutter.supported_in_blank = false;
765 
766 	// TODO: Fix me Sam
767 	if (in_out->soc_bb->power_management_parameters.z8_min_idle_time > 0 &&
768 		in_out->programming->informative.power_management.z8.stutter_period >= in_out->soc_bb->power_management_parameters.z8_min_idle_time)
769 		in_out->programming->z8_stutter.meets_eco = true;
770 	else
771 		in_out->programming->z8_stutter.meets_eco = false;
772 
773 	if (in_out->soc_bb->power_management_parameters.z8_stutter_exit_latency_us > 0 &&
774 		min_idle_us >= in_out->soc_bb->power_management_parameters.z8_stutter_exit_latency_us)
775 		in_out->programming->z8_stutter.supported_in_blank = true;
776 	else
777 		in_out->programming->z8_stutter.supported_in_blank = false;
778 
779 	return result;
780 }
781 
dpmm_dcn4_map_watermarks(struct dml2_dpmm_map_watermarks_params_in_out * in_out)782 bool dpmm_dcn4_map_watermarks(struct dml2_dpmm_map_watermarks_params_in_out *in_out)
783 {
784 	const struct dml2_display_cfg *display_cfg = &in_out->display_cfg->display_config;
785 	const struct dml2_core_internal_display_mode_lib *mode_lib = &in_out->core->clean_me_up.mode_lib;
786 	struct dml2_dchub_global_register_set *dchubbub_regs = &in_out->programming->global_regs;
787 
788 	double refclk_freq_in_mhz = (display_cfg->overrides.hw.dlg_ref_clk_mhz > 0) ? (double)display_cfg->overrides.hw.dlg_ref_clk_mhz : mode_lib->soc.dchub_refclk_mhz;
789 
790 	/* set A */
791 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].fclk_pstate = (int unsigned)(mode_lib->mp.Watermark.FCLKChangeWatermark * refclk_freq_in_mhz);
792 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].sr_enter = (int unsigned)(mode_lib->mp.Watermark.StutterEnterPlusExitWatermark * refclk_freq_in_mhz);
793 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].sr_exit = (int unsigned)(mode_lib->mp.Watermark.StutterExitWatermark * refclk_freq_in_mhz);
794 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].sr_enter_z8 = (int unsigned)(mode_lib->mp.Watermark.Z8StutterEnterPlusExitWatermark * refclk_freq_in_mhz);
795 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].sr_exit_z8 = (int unsigned)(mode_lib->mp.Watermark.Z8StutterExitWatermark * refclk_freq_in_mhz);
796 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].temp_read_or_ppt = (int unsigned)(mode_lib->mp.Watermark.temp_read_or_ppt_watermark_us * refclk_freq_in_mhz);
797 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].uclk_pstate = (int unsigned)(mode_lib->mp.Watermark.DRAMClockChangeWatermark * refclk_freq_in_mhz);
798 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].urgent = (int unsigned)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz);
799 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].usr = (int unsigned)(mode_lib->mp.Watermark.USRRetrainingWatermark * refclk_freq_in_mhz);
800 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].refcyc_per_trip_to_mem = (unsigned int)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz);
801 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].refcyc_per_meta_trip_to_mem = (unsigned int)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz);
802 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].frac_urg_bw_flip = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidthImmediateFlip * 1000);
803 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].frac_urg_bw_nom = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidth * 1000);
804 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].frac_urg_bw_mall = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidthMALL * 1000);
805 
806 	/* set B */
807 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].fclk_pstate = (int unsigned)(mode_lib->mp.Watermark.FCLKChangeWatermark * refclk_freq_in_mhz);
808 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].sr_enter = (int unsigned)(mode_lib->mp.Watermark.StutterEnterPlusExitWatermark * refclk_freq_in_mhz);
809 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].sr_exit = (int unsigned)(mode_lib->mp.Watermark.StutterExitWatermark * refclk_freq_in_mhz);
810 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].sr_enter_z8 = (int unsigned)(mode_lib->mp.Watermark.Z8StutterEnterPlusExitWatermark * refclk_freq_in_mhz);
811 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].sr_exit_z8 = (int unsigned)(mode_lib->mp.Watermark.Z8StutterExitWatermark * refclk_freq_in_mhz);
812 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].temp_read_or_ppt = (int unsigned)(mode_lib->mp.Watermark.temp_read_or_ppt_watermark_us * refclk_freq_in_mhz);
813 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].uclk_pstate = (int unsigned)(mode_lib->mp.Watermark.DRAMClockChangeWatermark * refclk_freq_in_mhz);
814 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].urgent = (int unsigned)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz);
815 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].usr = (int unsigned)(mode_lib->mp.Watermark.USRRetrainingWatermark * refclk_freq_in_mhz);
816 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].refcyc_per_trip_to_mem = (unsigned int)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz);
817 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].refcyc_per_meta_trip_to_mem = (unsigned int)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz);
818 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].frac_urg_bw_flip = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidthImmediateFlip * 1000);
819 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].frac_urg_bw_nom = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidth * 1000);
820 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B].frac_urg_bw_mall = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidthMALL * 1000);
821 
822 	dchubbub_regs->num_watermark_sets = 2;
823 
824 	return true;
825 }
dpmm_dcn42_map_watermarks(struct dml2_dpmm_map_watermarks_params_in_out * in_out)826 bool dpmm_dcn42_map_watermarks(struct dml2_dpmm_map_watermarks_params_in_out *in_out)
827 {
828 	const struct dml2_display_cfg *display_cfg = &in_out->display_cfg->display_config;
829 	const struct dml2_core_internal_display_mode_lib *mode_lib = &in_out->core->clean_me_up.mode_lib;
830 	struct dml2_dchub_global_register_set *dchubbub_regs = &in_out->programming->global_regs;
831 
832 	double refclk_freq_in_mhz = (display_cfg->overrides.hw.dlg_ref_clk_mhz > 0) ? (double)display_cfg->overrides.hw.dlg_ref_clk_mhz : mode_lib->soc.dchub_refclk_mhz;
833 
834 	/* set A */
835 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].fclk_pstate = (int unsigned)(mode_lib->mp.Watermark.FCLKChangeWatermark * refclk_freq_in_mhz);
836 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].sr_enter = (int unsigned)(mode_lib->mp.Watermark.StutterEnterPlusExitWatermark * refclk_freq_in_mhz);
837 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].sr_exit = (int unsigned)(mode_lib->mp.Watermark.StutterExitWatermark * refclk_freq_in_mhz);
838 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].sr_enter_z8 = (int unsigned)(mode_lib->mp.Watermark.Z8StutterEnterPlusExitWatermark * refclk_freq_in_mhz);
839 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].sr_exit_z8 = (int unsigned)(mode_lib->mp.Watermark.Z8StutterExitWatermark * refclk_freq_in_mhz);
840 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].temp_read_or_ppt = (int unsigned)(mode_lib->mp.Watermark.temp_read_or_ppt_watermark_us * refclk_freq_in_mhz);
841 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].uclk_pstate = (int unsigned)(mode_lib->mp.Watermark.DRAMClockChangeWatermark * refclk_freq_in_mhz);
842 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].urgent = (int unsigned)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz);
843 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].usr = (int unsigned)(mode_lib->mp.Watermark.USRRetrainingWatermark * refclk_freq_in_mhz);
844 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].refcyc_per_trip_to_mem = (unsigned int)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz);
845 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].refcyc_per_meta_trip_to_mem = (unsigned int)(mode_lib->mp.Watermark.UrgentWatermark * refclk_freq_in_mhz);
846 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].frac_urg_bw_flip = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidthImmediateFlip * 1000);
847 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].frac_urg_bw_nom = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidth * 1000);
848 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].frac_urg_bw_mall = (unsigned int)(mode_lib->mp.FractionOfUrgentBandwidthMALL * 1000);
849 
850 	/* set B */
851 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B] = dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A];
852 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_C] = dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A];
853 	dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_D] = dchubbub_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A];
854 
855 	dchubbub_regs->num_watermark_sets = 4;
856 
857 	return true;
858 }
859