xref: /linux/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn3.c (revision 2c1ed907520c50326b8f604907a8478b27881a2e)
1 // SPDX-License-Identifier: MIT
2 //
3 // Copyright 2024 Advanced Micro Devices, Inc.
4 
5 #include "dml2_pmo_factory.h"
6 #include "dml2_pmo_dcn3.h"
7 
sort(double * list_a,int list_a_size)8 static void sort(double *list_a, int list_a_size)
9 {
10 	// For all elements b[i] in list_b[]
11 	for (int i = 0; i < list_a_size - 1; i++) {
12 		// Find the first element of list_a that's larger than b[i]
13 		for (int j = i; j < list_a_size - 1; j++) {
14 			if (list_a[j] > list_a[j + 1])
15 				swap(list_a[j], list_a[j + 1]);
16 		}
17 	}
18 }
19 
get_max_reserved_time_on_all_planes_with_stream_index(struct display_configuation_with_meta * config,unsigned int stream_index)20 static double get_max_reserved_time_on_all_planes_with_stream_index(struct display_configuation_with_meta *config, unsigned int stream_index)
21 {
22 	struct dml2_plane_parameters *plane_descriptor;
23 	long max_reserved_time_ns = 0;
24 
25 	for (unsigned int i = 0; i < config->display_config.num_planes; i++) {
26 		plane_descriptor = &config->display_config.plane_descriptors[i];
27 
28 		if (plane_descriptor->stream_index == stream_index)
29 			if (plane_descriptor->overrides.reserved_vblank_time_ns > max_reserved_time_ns)
30 				max_reserved_time_ns = plane_descriptor->overrides.reserved_vblank_time_ns;
31 	}
32 
33 	return (max_reserved_time_ns / 1000.0);
34 }
35 
36 
set_reserved_time_on_all_planes_with_stream_index(struct display_configuation_with_meta * config,unsigned int stream_index,double reserved_time_us)37 static void set_reserved_time_on_all_planes_with_stream_index(struct display_configuation_with_meta *config, unsigned int stream_index, double reserved_time_us)
38 {
39 	struct dml2_plane_parameters *plane_descriptor;
40 
41 	for (unsigned int i = 0; i < config->display_config.num_planes; i++) {
42 		plane_descriptor = &config->display_config.plane_descriptors[i];
43 
44 		if (plane_descriptor->stream_index == stream_index)
45 			plane_descriptor->overrides.reserved_vblank_time_ns = (long int)(reserved_time_us * 1000);
46 	}
47 }
48 
remove_duplicates(double * list_a,int * list_a_size)49 static void remove_duplicates(double *list_a, int *list_a_size)
50 {
51 	int cur_element = 0;
52 	// For all elements b[i] in list_b[]
53 	while (cur_element < *list_a_size - 1) {
54 		if (list_a[cur_element] == list_a[cur_element + 1]) {
55 			for (int j = cur_element + 1; j < *list_a_size - 1; j++) {
56 				list_a[j] = list_a[j + 1];
57 			}
58 			*list_a_size = *list_a_size - 1;
59 		} else {
60 			cur_element++;
61 		}
62 	}
63 }
64 
increase_mpc_combine_factor(unsigned int * mpc_combine_factor,unsigned int limit)65 static bool increase_mpc_combine_factor(unsigned int *mpc_combine_factor, unsigned int limit)
66 {
67 	if (*mpc_combine_factor < limit) {
68 		(*mpc_combine_factor)++;
69 		return true;
70 	}
71 
72 	return false;
73 }
74 
optimize_dcc_mcache_no_odm(struct dml2_pmo_optimize_dcc_mcache_in_out * in_out,int free_pipes)75 static bool optimize_dcc_mcache_no_odm(struct dml2_pmo_optimize_dcc_mcache_in_out *in_out,
76 	int free_pipes)
77 {
78 	struct dml2_pmo_instance *pmo = in_out->instance;
79 
80 	unsigned int i;
81 	bool result = true;
82 
83 	for (i = 0; i < in_out->optimized_display_cfg->num_planes; i++) {
84 		// For pipes that failed dcc mcache check, we want to increase the pipe count.
85 		// The logic for doing this depends on how many pipes is already being used,
86 		// and whether it's mpcc or odm combine.
87 		if (!in_out->dcc_mcache_supported[i]) {
88 			// For the general case of "n displays", we can only optimize streams with an ODM combine factor of 1
89 			if (in_out->cfg_support_info->stream_support_info[in_out->optimized_display_cfg->plane_descriptors[i].stream_index].odms_used == 1) {
90 				in_out->optimized_display_cfg->plane_descriptors[i].overrides.mpcc_combine_factor =
91 					in_out->cfg_support_info->plane_support_info[i].dpps_used;
92 				// For each plane that is not passing mcache validation, just add another pipe to it, up to the limit.
93 				if (free_pipes > 0) {
94 					if (!increase_mpc_combine_factor(&in_out->optimized_display_cfg->plane_descriptors[i].overrides.mpcc_combine_factor,
95 						pmo->mpc_combine_limit)) {
96 						// We've reached max pipes allocatable to a single plane, so we fail.
97 						result = false;
98 						break;
99 					} else {
100 						// Successfully added another pipe to this failing plane.
101 						free_pipes--;
102 					}
103 				} else {
104 					// No free pipes to add.
105 					result = false;
106 					break;
107 				}
108 			} else {
109 				// If the stream of this plane needs ODM combine, no further optimization can be done.
110 				result = false;
111 				break;
112 			}
113 		}
114 	}
115 
116 	return result;
117 }
118 
iterate_to_next_candidiate(struct dml2_pmo_instance * pmo,int size)119 static bool iterate_to_next_candidiate(struct dml2_pmo_instance *pmo, int size)
120 {
121 	int borrow_from, i;
122 	bool success = false;
123 
124 	if (pmo->scratch.pmo_dcn3.current_candidate[0] > 0) {
125 		pmo->scratch.pmo_dcn3.current_candidate[0]--;
126 		success = true;
127 	} else {
128 		for (borrow_from = 1; borrow_from < size && pmo->scratch.pmo_dcn3.current_candidate[borrow_from] == 0; borrow_from++)
129 			;
130 
131 		if (borrow_from < size) {
132 			pmo->scratch.pmo_dcn3.current_candidate[borrow_from]--;
133 			for (i = 0; i < borrow_from; i++) {
134 				pmo->scratch.pmo_dcn3.current_candidate[i] = pmo->scratch.pmo_dcn3.reserved_time_candidates_count[i] - 1;
135 			}
136 
137 			success = true;
138 		}
139 	}
140 
141 	return success;
142 }
143 
increase_odm_combine_factor(enum dml2_odm_mode * odm_mode,int odms_calculated)144 static bool increase_odm_combine_factor(enum dml2_odm_mode *odm_mode, int odms_calculated)
145 {
146 	bool result = true;
147 
148 	if (*odm_mode == dml2_odm_mode_auto) {
149 		switch (odms_calculated) {
150 		case 1:
151 			*odm_mode = dml2_odm_mode_bypass;
152 			break;
153 		case 2:
154 			*odm_mode = dml2_odm_mode_combine_2to1;
155 			break;
156 		case 3:
157 			*odm_mode = dml2_odm_mode_combine_3to1;
158 			break;
159 		case 4:
160 			*odm_mode = dml2_odm_mode_combine_4to1;
161 			break;
162 		default:
163 			result = false;
164 			break;
165 		}
166 	}
167 
168 	if (result) {
169 		if (*odm_mode == dml2_odm_mode_bypass) {
170 			*odm_mode = dml2_odm_mode_combine_2to1;
171 		} else if (*odm_mode == dml2_odm_mode_combine_2to1) {
172 			*odm_mode = dml2_odm_mode_combine_3to1;
173 		} else if (*odm_mode == dml2_odm_mode_combine_3to1) {
174 			*odm_mode = dml2_odm_mode_combine_4to1;
175 		} else {
176 			result = false;
177 		}
178 	}
179 
180 	return result;
181 }
182 
count_planes_with_stream_index(const struct dml2_display_cfg * display_cfg,unsigned int stream_index)183 static int count_planes_with_stream_index(const struct dml2_display_cfg *display_cfg, unsigned int stream_index)
184 {
185 	unsigned int i, count;
186 
187 	count = 0;
188 	for (i = 0; i < display_cfg->num_planes; i++) {
189 		if (display_cfg->plane_descriptors[i].stream_index == stream_index)
190 			count++;
191 	}
192 
193 	return count;
194 }
195 
are_timings_trivially_synchronizable(struct display_configuation_with_meta * display_config,int mask)196 static bool are_timings_trivially_synchronizable(struct display_configuation_with_meta *display_config, int mask)
197 {
198 	unsigned int i;
199 	bool identical = true;
200 	bool contains_drr = false;
201 	unsigned int remap_array[DML2_MAX_PLANES];
202 	unsigned int remap_array_size = 0;
203 
204 	// Create a remap array to enable simple iteration through only masked stream indicies
205 	for (i = 0; i < display_config->display_config.num_streams; i++) {
206 		if (mask & (0x1 << i)) {
207 			remap_array[remap_array_size++] = i;
208 		}
209 	}
210 
211 	// 0 or 1 display is always trivially synchronizable
212 	if (remap_array_size <= 1)
213 		return true;
214 
215 	for (i = 1; i < remap_array_size; i++) {
216 		if (memcmp(&display_config->display_config.stream_descriptors[remap_array[i - 1]].timing,
217 			&display_config->display_config.stream_descriptors[remap_array[i]].timing,
218 			sizeof(struct dml2_timing_cfg))) {
219 			identical = false;
220 			break;
221 		}
222 	}
223 
224 	for (i = 0; i < remap_array_size; i++) {
225 		if (display_config->display_config.stream_descriptors[remap_array[i]].timing.drr_config.enabled) {
226 			contains_drr = true;
227 			break;
228 		}
229 	}
230 
231 	return !contains_drr && identical;
232 }
233 
pmo_dcn3_initialize(struct dml2_pmo_initialize_in_out * in_out)234 bool pmo_dcn3_initialize(struct dml2_pmo_initialize_in_out *in_out)
235 {
236 	struct dml2_pmo_instance *pmo = in_out->instance;
237 
238 	pmo->soc_bb = in_out->soc_bb;
239 	pmo->ip_caps = in_out->ip_caps;
240 	pmo->mpc_combine_limit = 2;
241 	pmo->odm_combine_limit = 4;
242 	pmo->mcg_clock_table_size = in_out->mcg_clock_table_size;
243 
244 	pmo->options = in_out->options;
245 
246 	return true;
247 }
248 
is_h_timing_divisible_by(const struct dml2_timing_cfg * timing,unsigned char denominator)249 static bool is_h_timing_divisible_by(const struct dml2_timing_cfg *timing, unsigned char denominator)
250 {
251 	/*
252 	 * Htotal, Hblank start/end, and Hsync start/end all must be divisible
253 	 * in order for the horizontal timing params to be considered divisible
254 	 * by 2. Hsync start is always 0.
255 	 */
256 	unsigned long h_blank_start = timing->h_total - timing->h_front_porch;
257 
258 	return (timing->h_total % denominator == 0) &&
259 			(h_blank_start % denominator == 0) &&
260 			(timing->h_blank_end % denominator == 0) &&
261 			(timing->h_sync_width % denominator == 0);
262 }
263 
is_dp_encoder(enum dml2_output_encoder_class encoder_type)264 static bool is_dp_encoder(enum dml2_output_encoder_class encoder_type)
265 {
266 	switch (encoder_type) {
267 	case dml2_dp:
268 	case dml2_edp:
269 	case dml2_dp2p0:
270 	case dml2_none:
271 		return true;
272 	case dml2_hdmi:
273 	case dml2_hdmifrl:
274 	default:
275 		return false;
276 	}
277 }
278 
pmo_dcn3_init_for_vmin(struct dml2_pmo_init_for_vmin_in_out * in_out)279 bool pmo_dcn3_init_for_vmin(struct dml2_pmo_init_for_vmin_in_out *in_out)
280 {
281 	unsigned int i;
282 	const struct dml2_display_cfg *display_config =
283 			&in_out->base_display_config->display_config;
284 	const struct dml2_core_mode_support_result *mode_support_result =
285 			&in_out->base_display_config->mode_support_result;
286 
287 	if (in_out->instance->options->disable_dyn_odm ||
288 			(in_out->instance->options->disable_dyn_odm_for_multi_stream && display_config->num_streams > 1))
289 		return false;
290 
291 	for (i = 0; i < display_config->num_planes; i++)
292 		/*
293 		 * vmin optimization is required to be seamlessly switched off
294 		 * at any time when the new configuration is no longer
295 		 * supported. However switching from ODM combine to MPC combine
296 		 * is not always seamless. When there not enough free pipes, we
297 		 * will have to use the same secondary OPP heads as secondary
298 		 * DPP pipes in MPC combine in new state. This transition is
299 		 * expected to cause glitches. To avoid the transition, we only
300 		 * allow vmin optimization if the stream's base configuration
301 		 * doesn't require MPC combine. This condition checks if MPC
302 		 * combine is enabled. If so do not optimize the stream.
303 		 */
304 		if (mode_support_result->cfg_support_info.plane_support_info[i].dpps_used > 1 &&
305 				mode_support_result->cfg_support_info.stream_support_info[display_config->plane_descriptors[i].stream_index].odms_used == 1)
306 			in_out->base_display_config->stage4.unoptimizable_streams[display_config->plane_descriptors[i].stream_index] = true;
307 
308 	for (i = 0; i < display_config->num_streams; i++) {
309 		if (display_config->stream_descriptors[i].overrides.disable_dynamic_odm)
310 			in_out->base_display_config->stage4.unoptimizable_streams[i] = true;
311 		else if (in_out->base_display_config->stage3.stream_svp_meta[i].valid &&
312 				in_out->instance->options->disable_dyn_odm_for_stream_with_svp)
313 			in_out->base_display_config->stage4.unoptimizable_streams[i] = true;
314 		/*
315 		 * ODM Combine requires horizontal timing divisible by 2 so each
316 		 * ODM segment has the same size.
317 		 */
318 		else if (!is_h_timing_divisible_by(&display_config->stream_descriptors[i].timing, 2))
319 			in_out->base_display_config->stage4.unoptimizable_streams[i] = true;
320 		/*
321 		 * Our hardware support seamless ODM transitions for DP encoders
322 		 * only.
323 		 */
324 		else if (!is_dp_encoder(display_config->stream_descriptors[i].output.output_encoder))
325 			in_out->base_display_config->stage4.unoptimizable_streams[i] = true;
326 	}
327 
328 	return true;
329 }
330 
pmo_dcn3_test_for_vmin(struct dml2_pmo_test_for_vmin_in_out * in_out)331 bool pmo_dcn3_test_for_vmin(struct dml2_pmo_test_for_vmin_in_out *in_out)
332 {
333 	bool is_vmin = true;
334 
335 	if (in_out->vmin_limits->dispclk_khz > 0 &&
336 		in_out->display_config->mode_support_result.global.dispclk_khz > in_out->vmin_limits->dispclk_khz)
337 		is_vmin = false;
338 
339 	return is_vmin;
340 }
341 
find_highest_odm_load_stream_index(const struct dml2_display_cfg * display_config,const struct dml2_core_mode_support_result * mode_support_result)342 static int find_highest_odm_load_stream_index(
343 		const struct dml2_display_cfg *display_config,
344 		const struct dml2_core_mode_support_result *mode_support_result)
345 {
346 	unsigned int i;
347 	int odm_load, highest_odm_load = -1, highest_odm_load_index = -1;
348 
349 	for (i = 0; i < display_config->num_streams; i++) {
350 		if (mode_support_result->cfg_support_info.stream_support_info[i].odms_used > 0)
351 			odm_load = display_config->stream_descriptors[i].timing.pixel_clock_khz
352 				/ mode_support_result->cfg_support_info.stream_support_info[i].odms_used;
353 		else
354 			odm_load = 0;
355 
356 		if (odm_load > highest_odm_load) {
357 			highest_odm_load_index = i;
358 			highest_odm_load = odm_load;
359 		}
360 	}
361 
362 	return highest_odm_load_index;
363 }
364 
pmo_dcn3_optimize_for_vmin(struct dml2_pmo_optimize_for_vmin_in_out * in_out)365 bool pmo_dcn3_optimize_for_vmin(struct dml2_pmo_optimize_for_vmin_in_out *in_out)
366 {
367 	int stream_index;
368 	const struct dml2_display_cfg *display_config =
369 			&in_out->base_display_config->display_config;
370 	const struct dml2_core_mode_support_result *mode_support_result =
371 			&in_out->base_display_config->mode_support_result;
372 	unsigned int odms_used;
373 	struct dml2_stream_parameters *stream_descriptor;
374 	bool optimizable = false;
375 
376 	/*
377 	 * highest odm load stream must be optimizable to continue as dispclk is
378 	 * bounded by it.
379 	 */
380 	stream_index = find_highest_odm_load_stream_index(display_config,
381 			mode_support_result);
382 
383 	if (stream_index < 0 ||
384 			in_out->base_display_config->stage4.unoptimizable_streams[stream_index])
385 		return false;
386 
387 	odms_used = mode_support_result->cfg_support_info.stream_support_info[stream_index].odms_used;
388 	if ((int)odms_used >= in_out->instance->odm_combine_limit)
389 		return false;
390 
391 	memcpy(in_out->optimized_display_config,
392 			in_out->base_display_config,
393 			sizeof(struct display_configuation_with_meta));
394 
395 	stream_descriptor = &in_out->optimized_display_config->display_config.stream_descriptors[stream_index];
396 	while (!optimizable && increase_odm_combine_factor(
397 			&stream_descriptor->overrides.odm_mode,
398 			odms_used)) {
399 		switch (stream_descriptor->overrides.odm_mode) {
400 		case dml2_odm_mode_combine_2to1:
401 			optimizable = true;
402 			break;
403 		case dml2_odm_mode_combine_3to1:
404 			/*
405 			 * In ODM Combine 3:1 OTG_valid_pixel rate is 1/4 of
406 			 * actual pixel rate. Therefore horizontal timing must
407 			 * be divisible by 4.
408 			 */
409 			if (is_h_timing_divisible_by(&display_config->stream_descriptors[stream_index].timing, 4)) {
410 				if (mode_support_result->cfg_support_info.stream_support_info[stream_index].dsc_enable) {
411 					/*
412 					 * DSC h slice count must be divisible
413 					 * by 3.
414 					 */
415 					if (mode_support_result->cfg_support_info.stream_support_info[stream_index].num_dsc_slices % 3 == 0)
416 						optimizable = true;
417 				} else {
418 					optimizable = true;
419 				}
420 			}
421 			break;
422 		case dml2_odm_mode_combine_4to1:
423 			/*
424 			 * In ODM Combine 4:1 OTG_valid_pixel rate is 1/4 of
425 			 * actual pixel rate. Therefore horizontal timing must
426 			 * be divisible by 4.
427 			 */
428 			if (is_h_timing_divisible_by(&display_config->stream_descriptors[stream_index].timing, 4)) {
429 				if (mode_support_result->cfg_support_info.stream_support_info[stream_index].dsc_enable) {
430 					/*
431 					 * DSC h slice count must be divisible
432 					 * by 4.
433 					 */
434 					if (mode_support_result->cfg_support_info.stream_support_info[stream_index].num_dsc_slices % 4 == 0)
435 						optimizable = true;
436 				} else {
437 					optimizable = true;
438 				}
439 			}
440 			break;
441 		case dml2_odm_mode_auto:
442 		case dml2_odm_mode_bypass:
443 		case dml2_odm_mode_split_1to2:
444 		case dml2_odm_mode_mso_1to2:
445 		case dml2_odm_mode_mso_1to4:
446 		default:
447 			break;
448 		}
449 	}
450 
451 	return optimizable;
452 }
453 
pmo_dcn3_optimize_dcc_mcache(struct dml2_pmo_optimize_dcc_mcache_in_out * in_out)454 bool pmo_dcn3_optimize_dcc_mcache(struct dml2_pmo_optimize_dcc_mcache_in_out *in_out)
455 {
456 	struct dml2_pmo_instance *pmo = in_out->instance;
457 
458 	unsigned int i, used_pipes, free_pipes, planes_on_stream;
459 	bool result;
460 
461 	if (in_out->display_config != in_out->optimized_display_cfg) {
462 		memcpy(in_out->optimized_display_cfg, in_out->display_config, sizeof(struct dml2_display_cfg));
463 	}
464 
465 	//Count number of free pipes, and check if any odm combine is in use.
466 	used_pipes = 0;
467 	for (i = 0; i < in_out->optimized_display_cfg->num_planes; i++) {
468 		used_pipes += in_out->cfg_support_info->plane_support_info[i].dpps_used;
469 	}
470 	free_pipes = pmo->ip_caps->pipe_count - used_pipes;
471 
472 	// Optimization loop
473 	// The goal here is to add more pipes to any planes
474 	// which are failing mcache admissibility
475 	result = true;
476 
477 	// The optimization logic depends on whether ODM combine is enabled, and the stream count.
478 	if (in_out->optimized_display_cfg->num_streams > 1) {
479 		// If there are multiple streams, we are limited to only be able to optimize mcache failures on planes
480 		// which are not ODM combined.
481 
482 		result = optimize_dcc_mcache_no_odm(in_out, free_pipes);
483 	} else if (in_out->optimized_display_cfg->num_streams == 1) {
484 		// In single stream cases, we still optimize mcache failures when there's ODM combine with some
485 		// additional logic.
486 
487 		if (in_out->cfg_support_info->stream_support_info[0].odms_used > 1) {
488 			// If ODM combine is enabled, then the logic is to increase ODM combine factor.
489 
490 			// Optimization for streams with > 1 ODM combine factor is only supported for single display.
491 			planes_on_stream = count_planes_with_stream_index(in_out->optimized_display_cfg, 0);
492 
493 			for (i = 0; i < in_out->optimized_display_cfg->num_planes; i++) {
494 				// For pipes that failed dcc mcache check, we want to increase the pipe count.
495 				// The logic for doing this depends on how many pipes is already being used,
496 				// and whether it's mpcc or odm combine.
497 				if (!in_out->dcc_mcache_supported[i]) {
498 					// Increasing ODM combine factor on a stream requires a free pipe for each plane on the stream.
499 					if (free_pipes >= planes_on_stream) {
500 						if (!increase_odm_combine_factor(&in_out->optimized_display_cfg->stream_descriptors[i].overrides.odm_mode,
501 							in_out->cfg_support_info->plane_support_info[i].dpps_used)) {
502 							result = false;
503 						} else {
504 							break;
505 						}
506 					} else {
507 						result = false;
508 						break;
509 					}
510 				}
511 			}
512 		} else {
513 			// If ODM combine is not enabled, then we can actually use the same logic as before.
514 
515 			result = optimize_dcc_mcache_no_odm(in_out, free_pipes);
516 		}
517 	} else {
518 		result = true;
519 	}
520 
521 	return result;
522 }
523 
pmo_dcn3_init_for_pstate_support(struct dml2_pmo_init_for_pstate_support_in_out * in_out)524 bool pmo_dcn3_init_for_pstate_support(struct dml2_pmo_init_for_pstate_support_in_out *in_out)
525 {
526 	struct dml2_pmo_instance *pmo = in_out->instance;
527 	struct dml2_optimization_stage3_state *state = &in_out->base_display_config->stage3;
528 	const struct dml2_stream_parameters *stream_descriptor;
529 	const struct dml2_plane_parameters *plane_descriptor;
530 	unsigned int stream_index, plane_index, candidate_count;
531 	double min_reserved_vblank_time = 0;
532 	int fclk_twait_needed_mask = 0x0;
533 	int uclk_twait_needed_mask = 0x0;
534 
535 	state->performed = true;
536 	state->min_clk_index_for_latency = in_out->base_display_config->stage1.min_clk_index_for_latency;
537 	pmo->scratch.pmo_dcn3.min_latency_index = in_out->base_display_config->stage1.min_clk_index_for_latency;
538 	pmo->scratch.pmo_dcn3.max_latency_index = pmo->mcg_clock_table_size - 1;
539 	pmo->scratch.pmo_dcn3.cur_latency_index = in_out->base_display_config->stage1.min_clk_index_for_latency;
540 
541 	pmo->scratch.pmo_dcn3.stream_mask = 0xF;
542 
543 	for (plane_index = 0; plane_index < in_out->base_display_config->display_config.num_planes; plane_index++) {
544 		plane_descriptor = &in_out->base_display_config->display_config.plane_descriptors[plane_index];
545 		stream_descriptor = &in_out->base_display_config->display_config.stream_descriptors[plane_descriptor->stream_index];
546 
547 		if (in_out->base_display_config->mode_support_result.cfg_support_info.plane_support_info[plane_index].active_latency_hiding_us <
548 			in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us &&
549 			stream_descriptor->overrides.hw.twait_budgeting.uclk_pstate == dml2_twait_budgeting_setting_if_needed)
550 			uclk_twait_needed_mask |= (0x1 << plane_descriptor->stream_index);
551 
552 		if (stream_descriptor->overrides.hw.twait_budgeting.uclk_pstate == dml2_twait_budgeting_setting_try)
553 			uclk_twait_needed_mask |= (0x1 << plane_descriptor->stream_index);
554 
555 		if (in_out->base_display_config->mode_support_result.cfg_support_info.plane_support_info[plane_index].active_latency_hiding_us <
556 			in_out->instance->soc_bb->power_management_parameters.fclk_change_blackout_us &&
557 			stream_descriptor->overrides.hw.twait_budgeting.fclk_pstate == dml2_twait_budgeting_setting_if_needed)
558 			fclk_twait_needed_mask |= (0x1 << plane_descriptor->stream_index);
559 
560 		if (stream_descriptor->overrides.hw.twait_budgeting.fclk_pstate == dml2_twait_budgeting_setting_try)
561 			fclk_twait_needed_mask |= (0x1 << plane_descriptor->stream_index);
562 
563 		if (plane_descriptor->overrides.legacy_svp_config != dml2_svp_mode_override_auto) {
564 			pmo->scratch.pmo_dcn3.stream_mask &= ~(0x1 << plane_descriptor->stream_index);
565 		}
566 	}
567 
568 	for (stream_index = 0; stream_index < in_out->base_display_config->display_config.num_streams; stream_index++) {
569 		stream_descriptor = &in_out->base_display_config->display_config.stream_descriptors[stream_index];
570 
571 		// The absolute minimum required time is the minimum of all the required budgets
572 		/*
573 		if (stream_descriptor->overrides.hw.twait_budgeting.fclk_pstate
574 			== dml2_twait_budgeting_setting_require)
575 
576 			if (are_timings_trivially_synchronizable(in_out->base_display_config, pmo->scratch.pmo_dcn3.stream_mask)) {
577 				min_reserved_vblank_time = max_double2(min_reserved_vblank_time,
578 					in_out->instance->soc_bb->power_management_parameters.fclk_change_blackout_us);
579 			}
580 
581 		if (stream_descriptor->overrides.hw.twait_budgeting.uclk_pstate
582 			== dml2_twait_budgeting_setting_require) {
583 
584 			if (are_timings_trivially_synchronizable(in_out->base_display_config, pmo->scratch.pmo_dcn3.stream_mask)) {
585 				min_reserved_vblank_time = max_double2(min_reserved_vblank_time,
586 					in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us);
587 			}
588 		}
589 
590 		if (stream_descriptor->overrides.hw.twait_budgeting.stutter_enter_exit
591 			== dml2_twait_budgeting_setting_require)
592 			min_reserved_vblank_time = max_double2(min_reserved_vblank_time,
593 				in_out->instance->soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us);
594 		*/
595 
596 		min_reserved_vblank_time = get_max_reserved_time_on_all_planes_with_stream_index(in_out->base_display_config, stream_index);
597 
598 		// Insert the absolute minimum into the array
599 		candidate_count = 1;
600 		pmo->scratch.pmo_dcn3.reserved_time_candidates[stream_index][0] = min_reserved_vblank_time;
601 		pmo->scratch.pmo_dcn3.reserved_time_candidates_count[stream_index] = candidate_count;
602 
603 		if (!(pmo->scratch.pmo_dcn3.stream_mask & (0x1 << stream_index)))
604 			continue;
605 
606 		// For every optional feature, we create a candidate for it only if it's larger minimum.
607 		if ((fclk_twait_needed_mask & (0x1 << stream_index)) &&
608 			in_out->instance->soc_bb->power_management_parameters.fclk_change_blackout_us > min_reserved_vblank_time) {
609 
610 			if (are_timings_trivially_synchronizable(in_out->base_display_config, pmo->scratch.pmo_dcn3.stream_mask)) {
611 				pmo->scratch.pmo_dcn3.reserved_time_candidates[stream_index][candidate_count++] =
612 					in_out->instance->soc_bb->power_management_parameters.fclk_change_blackout_us;
613 			}
614 		}
615 
616 		if ((uclk_twait_needed_mask & (0x1 << stream_index)) &&
617 			in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us > min_reserved_vblank_time) {
618 
619 			if (are_timings_trivially_synchronizable(in_out->base_display_config, pmo->scratch.pmo_dcn3.stream_mask)) {
620 				pmo->scratch.pmo_dcn3.reserved_time_candidates[stream_index][candidate_count++] =
621 					in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us;
622 			}
623 		}
624 
625 		if ((stream_descriptor->overrides.hw.twait_budgeting.stutter_enter_exit == dml2_twait_budgeting_setting_try ||
626 			stream_descriptor->overrides.hw.twait_budgeting.stutter_enter_exit == dml2_twait_budgeting_setting_if_needed) &&
627 			in_out->instance->soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us > min_reserved_vblank_time) {
628 
629 			pmo->scratch.pmo_dcn3.reserved_time_candidates[stream_index][candidate_count++] =
630 				in_out->instance->soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us;
631 		}
632 
633 		pmo->scratch.pmo_dcn3.reserved_time_candidates_count[stream_index] = candidate_count;
634 
635 		// Finally sort the array of candidates
636 		sort(pmo->scratch.pmo_dcn3.reserved_time_candidates[stream_index],
637 			pmo->scratch.pmo_dcn3.reserved_time_candidates_count[stream_index]);
638 
639 		remove_duplicates(pmo->scratch.pmo_dcn3.reserved_time_candidates[stream_index],
640 			&pmo->scratch.pmo_dcn3.reserved_time_candidates_count[stream_index]);
641 
642 		pmo->scratch.pmo_dcn3.current_candidate[stream_index] =
643 			pmo->scratch.pmo_dcn3.reserved_time_candidates_count[stream_index] - 1;
644 	}
645 
646 	return true;
647 }
648 
pmo_dcn3_test_for_pstate_support(struct dml2_pmo_test_for_pstate_support_in_out * in_out)649 bool pmo_dcn3_test_for_pstate_support(struct dml2_pmo_test_for_pstate_support_in_out *in_out)
650 {
651 	struct dml2_pmo_instance *pmo = in_out->instance;
652 
653 	unsigned int i, stream_index;
654 
655 	for (i = 0; i < in_out->base_display_config->display_config.num_planes; i++) {
656 		stream_index = in_out->base_display_config->display_config.plane_descriptors[i].stream_index;
657 
658 		if (in_out->base_display_config->display_config.plane_descriptors[i].overrides.reserved_vblank_time_ns <
659 			pmo->scratch.pmo_dcn3.reserved_time_candidates[stream_index][pmo->scratch.pmo_dcn3.current_candidate[stream_index]] * 1000) {
660 			return false;
661 		}
662 	}
663 
664 	return true;
665 }
666 
pmo_dcn3_optimize_for_pstate_support(struct dml2_pmo_optimize_for_pstate_support_in_out * in_out)667 bool pmo_dcn3_optimize_for_pstate_support(struct dml2_pmo_optimize_for_pstate_support_in_out *in_out)
668 {
669 	struct dml2_pmo_instance *pmo = in_out->instance;
670 	unsigned int stream_index;
671 	bool success = false;
672 	bool reached_end;
673 
674 	memcpy(in_out->optimized_display_config, in_out->base_display_config, sizeof(struct display_configuation_with_meta));
675 
676 	if (in_out->last_candidate_failed) {
677 		if (pmo->scratch.pmo_dcn3.cur_latency_index < pmo->scratch.pmo_dcn3.max_latency_index) {
678 			// If we haven't tried all the clock bounds to support this state, try a higher one
679 			pmo->scratch.pmo_dcn3.cur_latency_index++;
680 
681 			success = true;
682 		} else {
683 			// If there's nothing higher to try, then we have to have a smaller canadidate
684 			reached_end = !iterate_to_next_candidiate(pmo, in_out->optimized_display_config->display_config.num_streams);
685 
686 			if (!reached_end) {
687 				pmo->scratch.pmo_dcn3.cur_latency_index = pmo->scratch.pmo_dcn3.min_latency_index;
688 				success = true;
689 			}
690 		}
691 	} else {
692 		success = true;
693 	}
694 
695 	if (success) {
696 		in_out->optimized_display_config->stage3.min_clk_index_for_latency = pmo->scratch.pmo_dcn3.cur_latency_index;
697 
698 		for (stream_index = 0; stream_index < in_out->optimized_display_config->display_config.num_streams; stream_index++) {
699 			set_reserved_time_on_all_planes_with_stream_index(in_out->optimized_display_config, stream_index,
700 				pmo->scratch.pmo_dcn3.reserved_time_candidates[stream_index][pmo->scratch.pmo_dcn3.current_candidate[stream_index]]);
701 		}
702 	}
703 
704 	return success;
705 }
706