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