1 // SPDX-License-Identifier: MIT
2 //
3 // Copyright 2024 Advanced Micro Devices, Inc.
4 
5 #include "dml2_pmo_factory.h"
6 #include "dml2_debug.h"
7 #include "lib_float_math.h"
8 #include "dml2_pmo_dcn4_fams2.h"
9 
10 static const double MIN_VACTIVE_MARGIN_PCT = 0.25; // We need more than non-zero margin because DET buffer granularity can alter vactive latency hiding
11 static const double MIN_BLANK_STUTTER_FACTOR = 3.0;
12 
13 static const struct dml2_pmo_pstate_strategy base_strategy_list_1_display[] = {
14 	// VActive Preferred
15 	{
16 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na },
17 		.allow_state_increase = true,
18 	},
19 
20 	// Then SVP
21 	{
22 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_fw_svp, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na },
23 		.allow_state_increase = true,
24 	},
25 
26 	// Then VBlank
27 	{
28 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na },
29 		.allow_state_increase = false,
30 	},
31 
32 	// Then DRR
33 	{
34 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_fw_drr, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na },
35 		.allow_state_increase = true,
36 	},
37 
38 	// Finally VBlank, but allow base clocks for latency to increase
39 	/*
40 	{
41 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na },
42 		.allow_state_increase = true,
43 	},
44 	*/
45 };
46 
47 static const int base_strategy_list_1_display_size = sizeof(base_strategy_list_1_display) / sizeof(struct dml2_pmo_pstate_strategy);
48 
49 static const struct dml2_pmo_pstate_strategy base_strategy_list_2_display[] = {
50 	// VActive only is preferred
51 	{
52 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na },
53 		.allow_state_increase = true,
54 	},
55 
56 	// Then VActive + VBlank
57 	{
58 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na },
59 		.allow_state_increase = false,
60 	},
61 
62 	// Then VBlank only
63 	{
64 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na },
65 		.allow_state_increase = false,
66 	},
67 
68 	// Then SVP + VBlank
69 	{
70 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_fw_svp, dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na },
71 		.allow_state_increase = false,
72 	},
73 
74 	// Then SVP + DRR
75 	{
76 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_fw_svp, dml2_pmo_pstate_strategy_fw_drr, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na },
77 		.allow_state_increase = true,
78 	},
79 
80 	// Then SVP + SVP
81 	{
82 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_fw_svp, dml2_pmo_pstate_strategy_fw_svp, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na },
83 		.allow_state_increase = true,
84 	},
85 
86 	// Then DRR + VActive
87 	{
88 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_fw_drr, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na },
89 		.allow_state_increase = true,
90 	},
91 
92 	// Then DRR + DRR
93 	{
94 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_fw_drr, dml2_pmo_pstate_strategy_fw_drr, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na },
95 		.allow_state_increase = true,
96 	},
97 
98 	// Finally VBlank, but allow base clocks for latency to increase
99 	/*
100 	{
101 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_na, dml2_pmo_pstate_strategy_na },
102 		.allow_state_increase = true,
103 	},
104 	*/
105 };
106 
107 static const int base_strategy_list_2_display_size = sizeof(base_strategy_list_2_display) / sizeof(struct dml2_pmo_pstate_strategy);
108 
109 static const struct dml2_pmo_pstate_strategy base_strategy_list_3_display[] = {
110 	// All VActive
111 	{
112 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_na },
113 		.allow_state_increase = true,
114 	},
115 
116 	// VActive + 1 VBlank
117 	{
118 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_na },
119 		.allow_state_increase = false,
120 	},
121 
122 	// All VBlank
123 	{
124 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_na },
125 		.allow_state_increase = false,
126 	},
127 
128 	// All DRR
129 	{
130 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_fw_drr, dml2_pmo_pstate_strategy_fw_drr, dml2_pmo_pstate_strategy_fw_drr, dml2_pmo_pstate_strategy_na },
131 		.allow_state_increase = true,
132 	},
133 
134 	// All VBlank, with state increase allowed
135 	/*
136 	{
137 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_na },
138 		.allow_state_increase = true,
139 	},
140 	*/
141 };
142 
143 static const int base_strategy_list_3_display_size = sizeof(base_strategy_list_3_display) / sizeof(struct dml2_pmo_pstate_strategy);
144 
145 static const struct dml2_pmo_pstate_strategy base_strategy_list_4_display[] = {
146 	// All VActive
147 	{
148 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_vactive },
149 		.allow_state_increase = true,
150 	},
151 
152 	// VActive + 1 VBlank
153 	{
154 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_vactive, dml2_pmo_pstate_strategy_vblank },
155 		.allow_state_increase = false,
156 	},
157 
158 	// All Vblank
159 	{
160 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_vblank },
161 		.allow_state_increase = false,
162 	},
163 
164 	// All DRR
165 	{
166 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_fw_drr, dml2_pmo_pstate_strategy_fw_drr, dml2_pmo_pstate_strategy_fw_drr, dml2_pmo_pstate_strategy_fw_drr },
167 		.allow_state_increase = true,
168 	},
169 
170 	// All VBlank, with state increase allowed
171 	/*
172 	{
173 		.per_stream_pstate_method = { dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_vblank, dml2_pmo_pstate_strategy_vblank },
174 		.allow_state_increase = true,
175 	},
176 	*/
177 };
178 
179 static const int base_strategy_list_4_display_size = sizeof(base_strategy_list_4_display) / sizeof(struct dml2_pmo_pstate_strategy);
180 
181 
increase_odm_combine_factor(enum dml2_odm_mode * odm_mode,int odms_calculated)182 static bool increase_odm_combine_factor(enum dml2_odm_mode *odm_mode, int odms_calculated)
183 {
184 	bool result = true;
185 
186 	if (*odm_mode == dml2_odm_mode_auto) {
187 		switch (odms_calculated) {
188 		case 1:
189 			*odm_mode = dml2_odm_mode_bypass;
190 			break;
191 		case 2:
192 			*odm_mode = dml2_odm_mode_combine_2to1;
193 			break;
194 		case 3:
195 			*odm_mode = dml2_odm_mode_combine_3to1;
196 			break;
197 		case 4:
198 			*odm_mode = dml2_odm_mode_combine_4to1;
199 			break;
200 		default:
201 			result = false;
202 			break;
203 		}
204 	}
205 
206 	if (result) {
207 		if (*odm_mode == dml2_odm_mode_bypass) {
208 			*odm_mode = dml2_odm_mode_combine_2to1;
209 		} else if (*odm_mode == dml2_odm_mode_combine_2to1) {
210 			*odm_mode = dml2_odm_mode_combine_3to1;
211 		} else if (*odm_mode == dml2_odm_mode_combine_3to1) {
212 			*odm_mode = dml2_odm_mode_combine_4to1;
213 		} else {
214 			result = false;
215 		}
216 	}
217 
218 	return result;
219 }
220 
increase_mpc_combine_factor(unsigned int * mpc_combine_factor,unsigned int limit)221 static bool increase_mpc_combine_factor(unsigned int *mpc_combine_factor, unsigned int limit)
222 {
223 	if (*mpc_combine_factor < limit) {
224 		(*mpc_combine_factor)++;
225 		return true;
226 	}
227 
228 	return false;
229 }
230 
count_planes_with_stream_index(const struct dml2_display_cfg * display_cfg,unsigned int stream_index)231 static int count_planes_with_stream_index(const struct dml2_display_cfg *display_cfg, unsigned int stream_index)
232 {
233 	unsigned int i, count;
234 
235 	count = 0;
236 	for (i = 0; i < display_cfg->num_planes; i++) {
237 		if (display_cfg->plane_descriptors[i].stream_index == stream_index)
238 			count++;
239 	}
240 
241 	return count;
242 }
243 
optimize_dcc_mcache_no_odm(struct dml2_pmo_optimize_dcc_mcache_in_out * in_out,int free_pipes)244 static bool optimize_dcc_mcache_no_odm(struct dml2_pmo_optimize_dcc_mcache_in_out *in_out,
245 	int free_pipes)
246 {
247 	struct dml2_pmo_instance *pmo = in_out->instance;
248 
249 	unsigned int i;
250 	bool result = true;
251 
252 	for (i = 0; i < in_out->optimized_display_cfg->num_planes; i++) {
253 		// For pipes that failed dcc mcache check, we want to increase the pipe count.
254 		// The logic for doing this depends on how many pipes is already being used,
255 		// and whether it's mpcc or odm combine.
256 		if (!in_out->dcc_mcache_supported[i]) {
257 			// For the general case of "n displays", we can only optimize streams with an ODM combine factor of 1
258 			if (in_out->cfg_support_info->stream_support_info[in_out->optimized_display_cfg->plane_descriptors[i].stream_index].odms_used == 1) {
259 				in_out->optimized_display_cfg->plane_descriptors[i].overrides.mpcc_combine_factor =
260 					in_out->cfg_support_info->plane_support_info[i].dpps_used;
261 				// For each plane that is not passing mcache validation, just add another pipe to it, up to the limit.
262 				if (free_pipes > 0) {
263 					if (!increase_mpc_combine_factor(&in_out->optimized_display_cfg->plane_descriptors[i].overrides.mpcc_combine_factor,
264 						pmo->mpc_combine_limit)) {
265 						// We've reached max pipes allocatable to a single plane, so we fail.
266 						result = false;
267 						break;
268 					} else {
269 						// Successfully added another pipe to this failing plane.
270 						free_pipes--;
271 					}
272 				} else {
273 					// No free pipes to add.
274 					result = false;
275 					break;
276 				}
277 			} else {
278 				// If the stream of this plane needs ODM combine, no further optimization can be done.
279 				result = false;
280 				break;
281 			}
282 		}
283 	}
284 
285 	return result;
286 }
287 
pmo_dcn4_fams2_optimize_dcc_mcache(struct dml2_pmo_optimize_dcc_mcache_in_out * in_out)288 bool pmo_dcn4_fams2_optimize_dcc_mcache(struct dml2_pmo_optimize_dcc_mcache_in_out *in_out)
289 {
290 	struct dml2_pmo_instance *pmo = in_out->instance;
291 
292 	unsigned int i, used_pipes, free_pipes, planes_on_stream;
293 	bool result;
294 
295 	if (in_out->display_config != in_out->optimized_display_cfg) {
296 		memcpy(in_out->optimized_display_cfg, in_out->display_config, sizeof(struct dml2_display_cfg));
297 	}
298 
299 	//Count number of free pipes, and check if any odm combine is in use.
300 	used_pipes = 0;
301 	for (i = 0; i < in_out->optimized_display_cfg->num_planes; i++) {
302 		used_pipes += in_out->cfg_support_info->plane_support_info[i].dpps_used;
303 	}
304 	free_pipes = pmo->ip_caps->pipe_count - used_pipes;
305 
306 	// Optimization loop
307 	// The goal here is to add more pipes to any planes
308 	// which are failing mcache admissibility
309 	result = true;
310 
311 	// The optimization logic depends on whether ODM combine is enabled, and the stream count.
312 	if (in_out->optimized_display_cfg->num_streams > 1 || in_out->instance->options->disable_dyn_odm) {
313 		// If there are multiple streams, we are limited to only be able to optimize mcache failures on planes
314 		// which are not ODM combined.
315 
316 		result = optimize_dcc_mcache_no_odm(in_out, free_pipes);
317 	} else if (in_out->optimized_display_cfg->num_streams == 1) {
318 		// In single stream cases, we still optimize mcache failures when there's ODM combine with some
319 		// additional logic.
320 
321 		if (in_out->cfg_support_info->stream_support_info[0].odms_used > 1) {
322 			// If ODM combine is enabled, then the logic is to increase ODM combine factor.
323 
324 			// Optimization for streams with > 1 ODM combine factor is only supported for single display.
325 			planes_on_stream = count_planes_with_stream_index(in_out->optimized_display_cfg, 0);
326 
327 			for (i = 0; i < in_out->optimized_display_cfg->num_planes; i++) {
328 				// For pipes that failed dcc mcache check, we want to increase the pipe count.
329 				// The logic for doing this depends on how many pipes is already being used,
330 				// and whether it's mpcc or odm combine.
331 				if (!in_out->dcc_mcache_supported[i]) {
332 					// Increasing ODM combine factor on a stream requires a free pipe for each plane on the stream.
333 					if (free_pipes >= planes_on_stream) {
334 						if (!increase_odm_combine_factor(&in_out->optimized_display_cfg->stream_descriptors[i].overrides.odm_mode,
335 							in_out->cfg_support_info->plane_support_info[i].dpps_used)) {
336 							result = false;
337 						} else {
338 							break;
339 						}
340 					} else {
341 						result = false;
342 						break;
343 					}
344 				}
345 			}
346 		} else {
347 			// If ODM combine is not enabled, then we can actually use the same logic as before.
348 
349 			result = optimize_dcc_mcache_no_odm(in_out, free_pipes);
350 		}
351 	} else {
352 		result = true;
353 	}
354 
355 	return result;
356 }
357 
convert_strategy_to_drr_variant(const enum dml2_pmo_pstate_method base_strategy)358 static enum dml2_pmo_pstate_method convert_strategy_to_drr_variant(const enum dml2_pmo_pstate_method base_strategy)
359 {
360 	enum dml2_pmo_pstate_method variant_strategy = 0;
361 
362 	switch (base_strategy) {
363 	case dml2_pmo_pstate_strategy_vactive:
364 		variant_strategy = dml2_pmo_pstate_strategy_fw_vactive_drr;
365 		break;
366 	case dml2_pmo_pstate_strategy_vblank:
367 		variant_strategy = dml2_pmo_pstate_strategy_fw_vblank_drr;
368 		break;
369 	case dml2_pmo_pstate_strategy_fw_svp:
370 		variant_strategy = dml2_pmo_pstate_strategy_fw_svp_drr;
371 		break;
372 	case dml2_pmo_pstate_strategy_fw_vactive_drr:
373 	case dml2_pmo_pstate_strategy_fw_vblank_drr:
374 	case dml2_pmo_pstate_strategy_fw_svp_drr:
375 	case dml2_pmo_pstate_strategy_fw_drr:
376 	case dml2_pmo_pstate_strategy_reserved_hw:
377 	case dml2_pmo_pstate_strategy_reserved_fw:
378 	case dml2_pmo_pstate_strategy_reserved_fw_drr_clamped:
379 	case dml2_pmo_pstate_strategy_reserved_fw_drr_var:
380 	case dml2_pmo_pstate_strategy_na:
381 	default:
382 		/* no variant for this mode */
383 		variant_strategy = base_strategy;
384 	}
385 
386 	return variant_strategy;
387 }
388 
get_expanded_strategy_list(struct dml2_pmo_init_data * init_data,int stream_count)389 static struct dml2_pmo_pstate_strategy *get_expanded_strategy_list(struct dml2_pmo_init_data *init_data, int stream_count)
390 {
391 	struct dml2_pmo_pstate_strategy *expanded_strategy_list = NULL;
392 
393 	switch (stream_count) {
394 	case 1:
395 		expanded_strategy_list = init_data->pmo_dcn4.expanded_strategy_list_1_display;
396 		break;
397 	case 2:
398 		expanded_strategy_list = init_data->pmo_dcn4.expanded_strategy_list_2_display;
399 		break;
400 	case 3:
401 		expanded_strategy_list = init_data->pmo_dcn4.expanded_strategy_list_3_display;
402 		break;
403 	case 4:
404 		expanded_strategy_list = init_data->pmo_dcn4.expanded_strategy_list_4_display;
405 		break;
406 	default:
407 		break;
408 	}
409 
410 	return expanded_strategy_list;
411 }
412 
get_num_expanded_strategies(struct dml2_pmo_init_data * init_data,int stream_count)413 static unsigned int get_num_expanded_strategies(
414 	struct dml2_pmo_init_data *init_data,
415 	int stream_count)
416 {
417 	return init_data->pmo_dcn4.num_expanded_strategies_per_list[stream_count - 1];
418 }
419 
insert_strategy_into_expanded_list(const struct dml2_pmo_pstate_strategy * per_stream_pstate_strategy,int stream_count,struct dml2_pmo_init_data * init_data)420 static void insert_strategy_into_expanded_list(
421 	const struct dml2_pmo_pstate_strategy *per_stream_pstate_strategy,
422 	int stream_count,
423 	struct dml2_pmo_init_data *init_data)
424 {
425 	struct dml2_pmo_pstate_strategy *expanded_strategy_list = NULL;
426 
427 	expanded_strategy_list = get_expanded_strategy_list(init_data, stream_count);
428 
429 	if (expanded_strategy_list) {
430 		memcpy(&expanded_strategy_list[init_data->pmo_dcn4.num_expanded_strategies_per_list[stream_count - 1]], per_stream_pstate_strategy, sizeof(struct dml2_pmo_pstate_strategy));
431 
432 		init_data->pmo_dcn4.num_expanded_strategies_per_list[stream_count - 1]++;
433 	}
434 }
435 
expand_base_strategy(struct dml2_pmo_instance * pmo,const struct dml2_pmo_pstate_strategy * base_strategy,unsigned int stream_count)436 static void expand_base_strategy(struct dml2_pmo_instance *pmo,
437 	const struct dml2_pmo_pstate_strategy *base_strategy,
438 	unsigned int stream_count)
439 {
440 	bool skip_to_next_stream;
441 	bool expanded_strategy_added;
442 	bool skip_iteration;
443 	unsigned int i, j;
444 	unsigned int num_streams_per_method[PMO_DCN4_MAX_DISPLAYS] = { 0 };
445 	unsigned int stream_iteration_indices[PMO_DCN4_MAX_DISPLAYS] = { 0 };
446 	struct dml2_pmo_pstate_strategy cur_strategy_list = { 0 };
447 
448 	/* determine number of displays per method */
449 	for (i = 0; i < stream_count; i++) {
450 		/* increment the count of the earliest index with the same method */
451 		for (j = 0; j < stream_count; j++) {
452 			if (base_strategy->per_stream_pstate_method[i] == base_strategy->per_stream_pstate_method[j]) {
453 				num_streams_per_method[j] = num_streams_per_method[j] + 1;
454 				break;
455 			}
456 		}
457 	}
458 
459 	cur_strategy_list.allow_state_increase = base_strategy->allow_state_increase;
460 
461 	i = 0;
462 	/* uses a while loop instead of recursion to build permutations of base strategy */
463 	while (stream_iteration_indices[0] < stream_count) {
464 		skip_to_next_stream = false;
465 		expanded_strategy_added = false;
466 		skip_iteration = false;
467 
468 		/* determine what to do for this iteration */
469 		if (stream_iteration_indices[i] < stream_count && num_streams_per_method[stream_iteration_indices[i]] != 0) {
470 			/* decrement count and assign method */
471 			cur_strategy_list.per_stream_pstate_method[i] = base_strategy->per_stream_pstate_method[stream_iteration_indices[i]];
472 			num_streams_per_method[stream_iteration_indices[i]] -= 1;
473 
474 			if (i >= stream_count - 1) {
475 				/* insert into strategy list */
476 				insert_strategy_into_expanded_list(&cur_strategy_list, stream_count, &pmo->init_data);
477 				expanded_strategy_added = true;
478 			} else {
479 				/* skip to next stream */
480 				skip_to_next_stream = true;
481 			}
482 		} else {
483 			skip_iteration = true;
484 		}
485 
486 		/* prepare for next iteration */
487 		if (skip_to_next_stream) {
488 			i++;
489 		} else {
490 			/* restore count */
491 			if (!skip_iteration) {
492 				num_streams_per_method[stream_iteration_indices[i]] += 1;
493 			}
494 
495 			/* increment iteration count */
496 			stream_iteration_indices[i]++;
497 
498 			/* if iterations are complete, or last stream was reached */
499 			if ((stream_iteration_indices[i] >= stream_count || expanded_strategy_added) && i > 0) {
500 				/* reset per stream index, decrement i */
501 				stream_iteration_indices[i] = 0;
502 				i--;
503 
504 				/* restore previous stream's count and increment index */
505 				num_streams_per_method[stream_iteration_indices[i]] += 1;
506 				stream_iteration_indices[i]++;
507 			}
508 		}
509 	}
510 }
511 
512 
is_variant_method_valid(const struct dml2_pmo_pstate_strategy * base_strategy,const struct dml2_pmo_pstate_strategy * variant_strategy,unsigned int num_streams_per_base_method[PMO_DCN4_MAX_DISPLAYS],unsigned int num_streams_per_variant_method[PMO_DCN4_MAX_DISPLAYS],unsigned int stream_count)513 static bool is_variant_method_valid(const struct dml2_pmo_pstate_strategy *base_strategy,
514 		const struct dml2_pmo_pstate_strategy *variant_strategy,
515 		unsigned int num_streams_per_base_method[PMO_DCN4_MAX_DISPLAYS],
516 		unsigned int num_streams_per_variant_method[PMO_DCN4_MAX_DISPLAYS],
517 		unsigned int stream_count)
518 {
519 	bool valid = true;
520 	unsigned int i;
521 
522 	/* check all restrictions are met */
523 	for (i = 0; i < stream_count; i++) {
524 		/* vblank + vblank_drr variants are invalid */
525 		if (base_strategy->per_stream_pstate_method[i] == dml2_pmo_pstate_strategy_vblank &&
526 				((num_streams_per_base_method[i] > 0 && num_streams_per_variant_method[i] > 0) ||
527 				num_streams_per_variant_method[i] > 1)) {
528 			valid = false;
529 			break;
530 		}
531 	}
532 
533 	return valid;
534 }
535 
expand_variant_strategy(struct dml2_pmo_instance * pmo,const struct dml2_pmo_pstate_strategy * base_strategy,unsigned int stream_count)536 static void expand_variant_strategy(struct dml2_pmo_instance *pmo,
537 		const struct dml2_pmo_pstate_strategy *base_strategy,
538 		unsigned int stream_count)
539 {
540 	bool variant_found;
541 	unsigned int i, j;
542 	unsigned int method_index;
543 	unsigned int stream_index;
544 	unsigned int num_streams_per_method[PMO_DCN4_MAX_DISPLAYS] = { 0 };
545 	unsigned int num_streams_per_base_method[PMO_DCN4_MAX_DISPLAYS] = { 0 };
546 	unsigned int num_streams_per_variant_method[PMO_DCN4_MAX_DISPLAYS] = { 0 };
547 	enum dml2_pmo_pstate_method per_stream_variant_method[DML2_MAX_PLANES];
548 	struct dml2_pmo_pstate_strategy variant_strategy = { 0 };
549 
550 	/* determine number of displays per method */
551 	for (i = 0; i < stream_count; i++) {
552 		/* increment the count of the earliest index with the same method */
553 		for (j = 0; j < stream_count; j++) {
554 			if (base_strategy->per_stream_pstate_method[i] == base_strategy->per_stream_pstate_method[j]) {
555 				num_streams_per_method[j] = num_streams_per_method[j] + 1;
556 				break;
557 			}
558 		}
559 
560 		per_stream_variant_method[i] = convert_strategy_to_drr_variant(base_strategy->per_stream_pstate_method[i]);
561 	}
562 	memcpy(num_streams_per_base_method, num_streams_per_method, sizeof(unsigned int) * PMO_DCN4_MAX_DISPLAYS);
563 
564 	memcpy(&variant_strategy, base_strategy, sizeof(struct dml2_pmo_pstate_strategy));
565 
566 	method_index = 0;
567 	/* uses a while loop instead of recursion to build permutations of base strategy */
568 	while (num_streams_per_base_method[0] > 0 || method_index != 0) {
569 		if (method_index == stream_count) {
570 			/* construct variant strategy */
571 			variant_found = false;
572 			stream_index = 0;
573 
574 			for (i = 0; i < stream_count; i++) {
575 				for (j = 0; j < num_streams_per_base_method[i]; j++) {
576 					variant_strategy.per_stream_pstate_method[stream_index++] = base_strategy->per_stream_pstate_method[i];
577 				}
578 
579 				for (j = 0; j < num_streams_per_variant_method[i]; j++) {
580 					variant_strategy.per_stream_pstate_method[stream_index++] = per_stream_variant_method[i];
581 					if (base_strategy->per_stream_pstate_method[i] != per_stream_variant_method[i]) {
582 						variant_found = true;
583 					}
584 				}
585 			}
586 
587 			if (variant_found && is_variant_method_valid(base_strategy, &variant_strategy, num_streams_per_base_method, num_streams_per_variant_method, stream_count)) {
588 				expand_base_strategy(pmo, &variant_strategy, stream_count);
589 			}
590 
591 			/* rollback to earliest method with bases remaining */
592 			for (method_index = stream_count - 1; method_index > 0; method_index--) {
593 				if (num_streams_per_base_method[method_index]) {
594 					/* bases remaining */
595 					break;
596 				} else {
597 					/* reset counters */
598 					num_streams_per_base_method[method_index] = num_streams_per_method[method_index];
599 					num_streams_per_variant_method[method_index] = 0;
600 				}
601 			}
602 		}
603 
604 		if (num_streams_per_base_method[method_index]) {
605 			num_streams_per_base_method[method_index]--;
606 			num_streams_per_variant_method[method_index]++;
607 
608 			method_index++;
609 		} else if (method_index != 0) {
610 			method_index++;
611 		}
612 	}
613 }
614 
expand_base_strategies(struct dml2_pmo_instance * pmo,const struct dml2_pmo_pstate_strategy * base_strategies_list,const unsigned int num_base_strategies,unsigned int stream_count)615 static void expand_base_strategies(
616 	struct dml2_pmo_instance *pmo,
617 	const struct dml2_pmo_pstate_strategy *base_strategies_list,
618 	const unsigned int num_base_strategies,
619 	unsigned int stream_count)
620 {
621 	unsigned int i;
622 
623 	/* expand every explicit base strategy (except all DRR) */
624 	for (i = 0; i < num_base_strategies; i++) {
625 		expand_base_strategy(pmo, &base_strategies_list[i], stream_count);
626 		expand_variant_strategy(pmo, &base_strategies_list[i], stream_count);
627 	}
628 }
629 
pmo_dcn4_fams2_initialize(struct dml2_pmo_initialize_in_out * in_out)630 bool pmo_dcn4_fams2_initialize(struct dml2_pmo_initialize_in_out *in_out)
631 {
632 	int i = 0;
633 	struct dml2_pmo_instance *pmo = in_out->instance;
634 
635 	pmo->soc_bb = in_out->soc_bb;
636 	pmo->ip_caps = in_out->ip_caps;
637 	pmo->mpc_combine_limit = 2;
638 	pmo->odm_combine_limit = 4;
639 	pmo->mcg_clock_table_size = in_out->mcg_clock_table_size;
640 
641 	pmo->fams_params.v2.subvp.refresh_rate_limit_max = 175;
642 	pmo->fams_params.v2.subvp.refresh_rate_limit_min = 0;
643 	pmo->fams_params.v2.drr.refresh_rate_limit_max = 1000;
644 	pmo->fams_params.v2.drr.refresh_rate_limit_min = 119;
645 
646 	pmo->options = in_out->options;
647 
648 	/* generate permutations of p-state configs from base strategy list */
649 	for (i = 1; i <= PMO_DCN4_MAX_DISPLAYS; i++) {
650 		switch (i) {
651 		case 1:
652 			DML2_ASSERT(base_strategy_list_1_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES);
653 
654 			/* populate list */
655 			expand_base_strategies(pmo, base_strategy_list_1_display, base_strategy_list_1_display_size, 1);
656 			break;
657 		case 2:
658 			DML2_ASSERT(base_strategy_list_2_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES);
659 
660 			/* populate list */
661 			expand_base_strategies(pmo, base_strategy_list_2_display, base_strategy_list_2_display_size, 2);
662 			break;
663 		case 3:
664 			DML2_ASSERT(base_strategy_list_3_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES);
665 
666 			/* populate list */
667 			expand_base_strategies(pmo, base_strategy_list_3_display, base_strategy_list_3_display_size, 3);
668 			break;
669 		case 4:
670 			DML2_ASSERT(base_strategy_list_4_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES);
671 
672 			/* populate list */
673 			expand_base_strategies(pmo, base_strategy_list_4_display, base_strategy_list_4_display_size, 4);
674 			break;
675 		}
676 	}
677 
678 	return true;
679 }
680 
is_h_timing_divisible_by(const struct dml2_timing_cfg * timing,unsigned char denominator)681 static bool is_h_timing_divisible_by(const struct dml2_timing_cfg *timing, unsigned char denominator)
682 {
683 	/*
684 	 * Htotal, Hblank start/end, and Hsync start/end all must be divisible
685 	 * in order for the horizontal timing params to be considered divisible
686 	 * by 2. Hsync start is always 0.
687 	 */
688 	unsigned long h_blank_start = timing->h_total - timing->h_front_porch;
689 
690 	return (timing->h_total % denominator == 0) &&
691 			(h_blank_start % denominator == 0) &&
692 			(timing->h_blank_end % denominator == 0) &&
693 			(timing->h_sync_width % denominator == 0);
694 }
695 
is_dp_encoder(enum dml2_output_encoder_class encoder_type)696 static bool is_dp_encoder(enum dml2_output_encoder_class encoder_type)
697 {
698 	switch (encoder_type) {
699 	case dml2_dp:
700 	case dml2_edp:
701 	case dml2_dp2p0:
702 	case dml2_none:
703 		return true;
704 	case dml2_hdmi:
705 	case dml2_hdmifrl:
706 	default:
707 		return false;
708 	}
709 }
710 
pmo_dcn4_fams2_init_for_vmin(struct dml2_pmo_init_for_vmin_in_out * in_out)711 bool pmo_dcn4_fams2_init_for_vmin(struct dml2_pmo_init_for_vmin_in_out *in_out)
712 {
713 	unsigned int i;
714 	const struct dml2_display_cfg *display_config =
715 			&in_out->base_display_config->display_config;
716 	const struct dml2_core_mode_support_result *mode_support_result =
717 			&in_out->base_display_config->mode_support_result;
718 	struct dml2_optimization_stage4_state *state =
719 				&in_out->base_display_config->stage4;
720 
721 	if (in_out->instance->options->disable_dyn_odm ||
722 			(in_out->instance->options->disable_dyn_odm_for_multi_stream && display_config->num_streams > 1))
723 		return false;
724 
725 	for (i = 0; i < display_config->num_planes; i++)
726 		/*
727 		 * vmin optimization is required to be seamlessly switched off
728 		 * at any time when the new configuration is no longer
729 		 * supported. However switching from ODM combine to MPC combine
730 		 * is not always seamless. When there not enough free pipes, we
731 		 * will have to use the same secondary OPP heads as secondary
732 		 * DPP pipes in MPC combine in new state. This transition is
733 		 * expected to cause glitches. To avoid the transition, we only
734 		 * allow vmin optimization if the stream's base configuration
735 		 * doesn't require MPC combine. This condition checks if MPC
736 		 * combine is enabled. If so do not optimize the stream.
737 		 */
738 		if (mode_support_result->cfg_support_info.plane_support_info[i].dpps_used > 1 &&
739 				mode_support_result->cfg_support_info.stream_support_info[display_config->plane_descriptors[i].stream_index].odms_used == 1)
740 			state->unoptimizable_streams[display_config->plane_descriptors[i].stream_index] = true;
741 
742 	for (i = 0; i < display_config->num_streams; i++) {
743 		if (display_config->stream_descriptors[i].overrides.disable_dynamic_odm)
744 			state->unoptimizable_streams[i] = true;
745 		else if (in_out->base_display_config->stage3.stream_svp_meta[i].valid &&
746 				in_out->instance->options->disable_dyn_odm_for_stream_with_svp)
747 			state->unoptimizable_streams[i] = true;
748 		/*
749 		 * ODM Combine requires horizontal timing divisible by 2 so each
750 		 * ODM segment has the same size.
751 		 */
752 		else if (!is_h_timing_divisible_by(&display_config->stream_descriptors[i].timing, 2))
753 			state->unoptimizable_streams[i] = true;
754 		/*
755 		 * Our hardware support seamless ODM transitions for DP encoders
756 		 * only.
757 		 */
758 		else if (!is_dp_encoder(display_config->stream_descriptors[i].output.output_encoder))
759 			state->unoptimizable_streams[i] = true;
760 	}
761 
762 	state->performed = true;
763 
764 	return true;
765 }
766 
pmo_dcn4_fams2_test_for_vmin(struct dml2_pmo_test_for_vmin_in_out * in_out)767 bool pmo_dcn4_fams2_test_for_vmin(struct dml2_pmo_test_for_vmin_in_out *in_out)
768 {
769 	bool is_vmin = true;
770 
771 	if (in_out->vmin_limits->dispclk_khz > 0 &&
772 		in_out->display_config->mode_support_result.global.dispclk_khz > in_out->vmin_limits->dispclk_khz)
773 		is_vmin = false;
774 
775 	return is_vmin;
776 }
777 
find_highest_odm_load_stream_index(const struct dml2_display_cfg * display_config,const struct dml2_core_mode_support_result * mode_support_result)778 static int find_highest_odm_load_stream_index(
779 		const struct dml2_display_cfg *display_config,
780 		const struct dml2_core_mode_support_result *mode_support_result)
781 {
782 	unsigned int i;
783 	int odm_load, highest_odm_load = -1, highest_odm_load_index = -1;
784 
785 	for (i = 0; i < display_config->num_streams; i++) {
786 		odm_load = display_config->stream_descriptors[i].timing.pixel_clock_khz
787 				/ mode_support_result->cfg_support_info.stream_support_info[i].odms_used;
788 		if (odm_load > highest_odm_load) {
789 			highest_odm_load_index = i;
790 			highest_odm_load = odm_load;
791 		}
792 	}
793 
794 	return highest_odm_load_index;
795 }
796 
pmo_dcn4_fams2_optimize_for_vmin(struct dml2_pmo_optimize_for_vmin_in_out * in_out)797 bool pmo_dcn4_fams2_optimize_for_vmin(struct dml2_pmo_optimize_for_vmin_in_out *in_out)
798 {
799 	int stream_index;
800 	const struct dml2_display_cfg *display_config =
801 			&in_out->base_display_config->display_config;
802 	const struct dml2_core_mode_support_result *mode_support_result =
803 			&in_out->base_display_config->mode_support_result;
804 	unsigned int odms_used;
805 	struct dml2_stream_parameters *stream_descriptor;
806 	bool optimizable = false;
807 
808 	/*
809 	 * highest odm load stream must be optimizable to continue as dispclk is
810 	 * bounded by it.
811 	 */
812 	stream_index = find_highest_odm_load_stream_index(display_config,
813 			mode_support_result);
814 
815 	if (stream_index < 0 ||
816 			in_out->base_display_config->stage4.unoptimizable_streams[stream_index])
817 		return false;
818 
819 	odms_used = mode_support_result->cfg_support_info.stream_support_info[stream_index].odms_used;
820 	if ((int)odms_used >= in_out->instance->odm_combine_limit)
821 		return false;
822 
823 	memcpy(in_out->optimized_display_config,
824 			in_out->base_display_config,
825 			sizeof(struct display_configuation_with_meta));
826 
827 	stream_descriptor = &in_out->optimized_display_config->display_config.stream_descriptors[stream_index];
828 	while (!optimizable && increase_odm_combine_factor(
829 			&stream_descriptor->overrides.odm_mode,
830 			odms_used)) {
831 		switch (stream_descriptor->overrides.odm_mode) {
832 		case dml2_odm_mode_combine_2to1:
833 			optimizable = true;
834 			break;
835 		case dml2_odm_mode_combine_3to1:
836 			/*
837 			 * In ODM Combine 3:1 OTG_valid_pixel rate is 1/4 of
838 			 * actual pixel rate. Therefore horizontal timing must
839 			 * be divisible by 4.
840 			 */
841 			if (is_h_timing_divisible_by(&display_config->stream_descriptors[stream_index].timing, 4)) {
842 				if (mode_support_result->cfg_support_info.stream_support_info[stream_index].dsc_enable) {
843 					/*
844 					 * DSC h slice count must be divisible
845 					 * by 3.
846 					 */
847 					if (mode_support_result->cfg_support_info.stream_support_info[stream_index].num_dsc_slices % 3 == 0)
848 						optimizable = true;
849 				} else {
850 					optimizable = true;
851 				}
852 			}
853 			break;
854 		case dml2_odm_mode_combine_4to1:
855 			/*
856 			 * In ODM Combine 4:1 OTG_valid_pixel rate is 1/4 of
857 			 * actual pixel rate. Therefore horizontal timing must
858 			 * be divisible by 4.
859 			 */
860 			if (is_h_timing_divisible_by(&display_config->stream_descriptors[stream_index].timing, 4)) {
861 				if (mode_support_result->cfg_support_info.stream_support_info[stream_index].dsc_enable) {
862 					/*
863 					 * DSC h slice count must be divisible
864 					 * by 4.
865 					 */
866 					if (mode_support_result->cfg_support_info.stream_support_info[stream_index].num_dsc_slices % 4 == 0)
867 						optimizable = true;
868 				} else {
869 					optimizable = true;
870 				}
871 			}
872 			break;
873 		case dml2_odm_mode_auto:
874 		case dml2_odm_mode_bypass:
875 		case dml2_odm_mode_split_1to2:
876 		case dml2_odm_mode_mso_1to2:
877 		case dml2_odm_mode_mso_1to4:
878 		default:
879 			break;
880 		}
881 	}
882 
883 	return optimizable;
884 }
885 
set_bit_in_bitfield(unsigned int * bit_field,unsigned int bit_offset)886 static void set_bit_in_bitfield(unsigned int *bit_field, unsigned int bit_offset)
887 {
888 	*bit_field = *bit_field | (0x1 << bit_offset);
889 }
890 
is_bit_set_in_bitfield(unsigned int bit_field,unsigned int bit_offset)891 static bool is_bit_set_in_bitfield(unsigned int bit_field, unsigned int bit_offset)
892 {
893 	if (bit_field & (0x1 << bit_offset))
894 		return true;
895 
896 	return false;
897 }
898 
build_synchronized_timing_groups(struct dml2_pmo_instance * pmo,struct display_configuation_with_meta * display_config)899 static void build_synchronized_timing_groups(
900 	struct dml2_pmo_instance *pmo,
901 	struct display_configuation_with_meta *display_config)
902 {
903 	unsigned int i, j;
904 	struct dml2_timing_cfg *master_timing;
905 
906 	unsigned int stream_mapped_mask = 0;
907 	unsigned int num_timing_groups = 0;
908 	unsigned int timing_group_idx = 0;
909 	struct dml2_pmo_scratch *s = &pmo->scratch;
910 
911 	/* clear all group masks */
912 	memset(s->pmo_dcn4.synchronized_timing_group_masks, 0, sizeof(s->pmo_dcn4.synchronized_timing_group_masks));
913 	memset(s->pmo_dcn4.group_is_drr_enabled, 0, sizeof(s->pmo_dcn4.group_is_drr_enabled));
914 	memset(s->pmo_dcn4.group_is_drr_active, 0, sizeof(s->pmo_dcn4.group_is_drr_active));
915 	memset(s->pmo_dcn4.group_line_time_us, 0, sizeof(s->pmo_dcn4.group_line_time_us));
916 	s->pmo_dcn4.num_timing_groups = 0;
917 
918 	for (i = 0; i < display_config->display_config.num_streams; i++) {
919 		master_timing = &display_config->display_config.stream_descriptors[i].timing;
920 
921 		/* only need to build group of this stream is not in a group already */
922 		if (is_bit_set_in_bitfield(stream_mapped_mask, i)) {
923 			continue;
924 		}
925 		set_bit_in_bitfield(&stream_mapped_mask, i);
926 		timing_group_idx = num_timing_groups;
927 		num_timing_groups++;
928 
929 		/* trivially set default timing group to itself */
930 		set_bit_in_bitfield(&s->pmo_dcn4.synchronized_timing_group_masks[timing_group_idx], i);
931 		s->pmo_dcn4.group_line_time_us[timing_group_idx] = (double)master_timing->h_total / master_timing->pixel_clock_khz * 1000.0;
932 
933 		/* if drr is in use, timing is not sychnronizable */
934 		if (master_timing->drr_config.enabled) {
935 			s->pmo_dcn4.group_is_drr_enabled[timing_group_idx] = true;
936 			s->pmo_dcn4.group_is_drr_active[timing_group_idx] = !master_timing->drr_config.disallowed &&
937 					(master_timing->drr_config.drr_active_fixed || master_timing->drr_config.drr_active_variable);
938 			continue;
939 		}
940 
941 		/* find synchronizable timing groups */
942 		for (j = i + 1; j < display_config->display_config.num_streams; j++) {
943 			if (memcmp(master_timing,
944 					&display_config->display_config.stream_descriptors[j].timing,
945 					sizeof(struct dml2_timing_cfg)) == 0 &&
946 					display_config->display_config.stream_descriptors[i].output.output_encoder == display_config->display_config.stream_descriptors[j].output.output_encoder &&
947 					(display_config->display_config.stream_descriptors[i].output.output_encoder != dml2_hdmi || //hdmi requires formats match
948 					display_config->display_config.stream_descriptors[i].output.output_format == display_config->display_config.stream_descriptors[j].output.output_format)) {
949 				set_bit_in_bitfield(&pmo->scratch.pmo_dcn4.synchronized_timing_group_masks[timing_group_idx], j);
950 				set_bit_in_bitfield(&stream_mapped_mask, j);
951 			}
952 		}
953 	}
954 
955 	s->pmo_dcn4.num_timing_groups = num_timing_groups;
956 }
957 
all_timings_support_vactive(const struct dml2_pmo_instance * pmo,const struct display_configuation_with_meta * display_config,unsigned int mask)958 static bool all_timings_support_vactive(const struct dml2_pmo_instance *pmo,
959 		const struct display_configuation_with_meta *display_config,
960 		unsigned int mask)
961 {
962 	unsigned char i;
963 	bool valid = true;
964 
965 	// Create a remap array to enable simple iteration through only masked stream indicies
966 	for (i = 0; i < display_config->display_config.num_streams; i++) {
967 		if (is_bit_set_in_bitfield(mask, i)) {
968 			/* check if stream has enough vactive margin */
969 			valid &= is_bit_set_in_bitfield(pmo->scratch.pmo_dcn4.stream_vactive_capability_mask, i);
970 		}
971 	}
972 
973 	return valid;
974 }
975 
all_timings_support_vblank(const struct dml2_pmo_instance * pmo,const struct display_configuation_with_meta * display_config,unsigned int mask)976 static bool all_timings_support_vblank(const struct dml2_pmo_instance *pmo,
977 		const struct display_configuation_with_meta *display_config,
978 		unsigned int mask)
979 {
980 	unsigned int i;
981 
982 	bool synchronizable = true;
983 
984 	/* find first vblank stream index and compare the timing group mask */
985 	for (i = 0; i < display_config->display_config.num_streams; i++) {
986 		if (is_bit_set_in_bitfield(mask, i)) {
987 			if (mask != pmo->scratch.pmo_dcn4.synchronized_timing_group_masks[i]) {
988 				/* vblank streams are not synchronizable */
989 				synchronizable = false;
990 			}
991 			break;
992 		}
993 	}
994 
995 	return synchronizable;
996 }
997 
calc_svp_microschedule(const struct dml2_fams2_meta * fams2_meta)998 static unsigned int calc_svp_microschedule(const struct dml2_fams2_meta *fams2_meta)
999 {
1000 	return fams2_meta->contention_delay_otg_vlines +
1001 		fams2_meta->method_subvp.programming_delay_otg_vlines +
1002 		fams2_meta->method_subvp.phantom_vtotal +
1003 		fams2_meta->method_subvp.prefetch_to_mall_delay_otg_vlines +
1004 		fams2_meta->dram_clk_change_blackout_otg_vlines;
1005 }
1006 
all_timings_support_drr(const struct dml2_pmo_instance * pmo,const struct display_configuation_with_meta * display_config,unsigned int mask)1007 static bool all_timings_support_drr(const struct dml2_pmo_instance *pmo,
1008 	const struct display_configuation_with_meta *display_config,
1009 	unsigned int mask)
1010 {
1011 	unsigned char i;
1012 	for (i = 0; i < DML2_MAX_PLANES; i++) {
1013 		const struct dml2_stream_parameters *stream_descriptor;
1014 		const struct dml2_fams2_meta *stream_fams2_meta;
1015 
1016 		if (is_bit_set_in_bitfield(mask, i)) {
1017 			stream_descriptor = &display_config->display_config.stream_descriptors[i];
1018 			stream_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[i];
1019 
1020 			if (!stream_descriptor->timing.drr_config.enabled)
1021 				return false;
1022 
1023 			/* cannot support required vtotal */
1024 			if (stream_fams2_meta->method_drr.stretched_vtotal > stream_fams2_meta->max_vtotal) {
1025 				return false;
1026 			}
1027 
1028 			/* check rr is within bounds */
1029 			if (stream_fams2_meta->nom_refresh_rate_hz < pmo->fams_params.v2.drr.refresh_rate_limit_min ||
1030 				stream_fams2_meta->nom_refresh_rate_hz > pmo->fams_params.v2.drr.refresh_rate_limit_max) {
1031 				return false;
1032 			}
1033 
1034 			/* check required stretch is allowed */
1035 			if (stream_descriptor->timing.drr_config.max_instant_vtotal_delta > 0 &&
1036 					stream_fams2_meta->method_drr.stretched_vtotal - stream_fams2_meta->nom_vtotal > stream_descriptor->timing.drr_config.max_instant_vtotal_delta) {
1037 				return false;
1038 			}
1039 		}
1040 	}
1041 
1042 	return true;
1043 }
1044 
all_timings_support_svp(const struct dml2_pmo_instance * pmo,const struct display_configuation_with_meta * display_config,unsigned int mask)1045 static bool all_timings_support_svp(const struct dml2_pmo_instance *pmo,
1046 	const struct display_configuation_with_meta *display_config,
1047 	unsigned int mask)
1048 {
1049 	const struct dml2_stream_parameters *stream_descriptor;
1050 	const struct dml2_plane_parameters *plane_descriptor;
1051 	const struct dml2_fams2_meta *stream_fams2_meta;
1052 	unsigned int microschedule_vlines;
1053 	unsigned char i;
1054 
1055 	unsigned int num_planes_per_stream[DML2_MAX_PLANES] = { 0 };
1056 
1057 	/* confirm timing it is not a centered timing */
1058 	for (i = 0; i < display_config->display_config.num_planes; i++) {
1059 		plane_descriptor = &display_config->display_config.plane_descriptors[i];
1060 
1061 		if (is_bit_set_in_bitfield(mask, (unsigned char)plane_descriptor->stream_index)) {
1062 			num_planes_per_stream[plane_descriptor->stream_index]++;
1063 
1064 			/* check recout height covers entire otg vactive, and single plane */
1065 			if (num_planes_per_stream[plane_descriptor->stream_index] > 1 ||
1066 					!plane_descriptor->composition.rect_out_height_spans_vactive ||
1067 					plane_descriptor->composition.rotation_angle != dml2_rotation_0) {
1068 				return false;
1069 			}
1070 		}
1071 	}
1072 
1073 	for (i = 0; i < DML2_MAX_PLANES; i++) {
1074 		if (is_bit_set_in_bitfield(mask, i)) {
1075 			stream_descriptor = &display_config->display_config.stream_descriptors[i];
1076 			stream_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[i];
1077 
1078 			if (stream_descriptor->overrides.disable_subvp) {
1079 				return false;
1080 			}
1081 
1082 			microschedule_vlines = calc_svp_microschedule(&pmo->scratch.pmo_dcn4.stream_fams2_meta[i]);
1083 
1084 			/* block if using an interlaced timing */
1085 			if (stream_descriptor->timing.interlaced) {
1086 				return false;
1087 			}
1088 
1089 			/* 1) svp main stream's vactive must be able to fit the microschedule
1090 			*  2) refresh rate must be within the allowed bounds
1091 			*/
1092 			if (microschedule_vlines >= stream_descriptor->timing.v_active ||
1093 					(stream_fams2_meta->nom_refresh_rate_hz < pmo->fams_params.v2.subvp.refresh_rate_limit_min ||
1094 					stream_fams2_meta->nom_refresh_rate_hz > pmo->fams_params.v2.subvp.refresh_rate_limit_max)) {
1095 				return false;
1096 			}
1097 		}
1098 	}
1099 
1100 	return true;
1101 }
1102 
insert_into_candidate_list(const struct dml2_pmo_pstate_strategy * pstate_strategy,int stream_count,struct dml2_pmo_scratch * scratch)1103 static void insert_into_candidate_list(const struct dml2_pmo_pstate_strategy *pstate_strategy, int stream_count, struct dml2_pmo_scratch *scratch)
1104 {
1105 	scratch->pmo_dcn4.pstate_strategy_candidates[scratch->pmo_dcn4.num_pstate_candidates] = *pstate_strategy;
1106 	scratch->pmo_dcn4.num_pstate_candidates++;
1107 }
1108 
all_planes_match_method(const struct display_configuation_with_meta * display_cfg,int plane_mask,enum dml2_pmo_pstate_method method)1109 static bool all_planes_match_method(const struct display_configuation_with_meta *display_cfg, int plane_mask, enum dml2_pmo_pstate_method method)
1110 {
1111 	unsigned char i;
1112 	enum dml2_uclk_pstate_change_strategy matching_strategy = (enum dml2_uclk_pstate_change_strategy) dml2_pmo_pstate_strategy_na;
1113 
1114 	if (method == dml2_pmo_pstate_strategy_vactive || method == dml2_pmo_pstate_strategy_fw_vactive_drr)
1115 		matching_strategy = dml2_uclk_pstate_change_strategy_force_vactive;
1116 	else if (method == dml2_pmo_pstate_strategy_vblank || method == dml2_pmo_pstate_strategy_fw_vblank_drr)
1117 		matching_strategy = dml2_uclk_pstate_change_strategy_force_vblank;
1118 	else if (method == dml2_pmo_pstate_strategy_fw_svp)
1119 		matching_strategy = dml2_uclk_pstate_change_strategy_force_mall_svp;
1120 	else if (method == dml2_pmo_pstate_strategy_fw_drr)
1121 		matching_strategy = dml2_uclk_pstate_change_strategy_force_drr;
1122 
1123 	for (i = 0; i < DML2_MAX_PLANES; i++) {
1124 		if (is_bit_set_in_bitfield(plane_mask, i)) {
1125 			if (display_cfg->display_config.plane_descriptors[i].overrides.uclk_pstate_change_strategy != dml2_uclk_pstate_change_strategy_auto &&
1126 				display_cfg->display_config.plane_descriptors[i].overrides.uclk_pstate_change_strategy != matching_strategy)
1127 				return false;
1128 		}
1129 	}
1130 
1131 	return true;
1132 }
1133 
build_method_scheduling_params(struct dml2_fams2_per_method_common_meta * stream_method_fams2_meta,struct dml2_fams2_meta * stream_fams2_meta)1134 static void build_method_scheduling_params(
1135 	struct dml2_fams2_per_method_common_meta *stream_method_fams2_meta,
1136 	struct dml2_fams2_meta *stream_fams2_meta)
1137 {
1138 	stream_method_fams2_meta->allow_time_us =
1139 			(double)((int)stream_method_fams2_meta->allow_end_otg_vline - (int)stream_method_fams2_meta->allow_start_otg_vline) *
1140 			stream_fams2_meta->otg_vline_time_us;
1141 	if (stream_method_fams2_meta->allow_time_us >= stream_method_fams2_meta->period_us) {
1142 		/* when allow wave overlaps an entire frame, it is always schedulable (DRR can do this)*/
1143 		stream_method_fams2_meta->disallow_time_us = 0.0;
1144 	} else {
1145 		stream_method_fams2_meta->disallow_time_us =
1146 				stream_method_fams2_meta->period_us - stream_method_fams2_meta->allow_time_us;
1147 	}
1148 }
1149 
get_per_method_common_meta(struct dml2_pmo_instance * pmo,enum dml2_pmo_pstate_method stream_pstate_method,int stream_idx)1150 static struct dml2_fams2_per_method_common_meta *get_per_method_common_meta(
1151 	struct dml2_pmo_instance *pmo,
1152 	enum dml2_pmo_pstate_method stream_pstate_method,
1153 	int stream_idx)
1154 {
1155 	struct dml2_fams2_per_method_common_meta *stream_method_fams2_meta = NULL;
1156 
1157 	switch (stream_pstate_method) {
1158 	case dml2_pmo_pstate_strategy_vactive:
1159 	case dml2_pmo_pstate_strategy_fw_vactive_drr:
1160 		stream_method_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_idx].method_vactive.common;
1161 		break;
1162 	case dml2_pmo_pstate_strategy_vblank:
1163 	case dml2_pmo_pstate_strategy_fw_vblank_drr:
1164 		stream_method_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_idx].method_vblank.common;
1165 		break;
1166 	case dml2_pmo_pstate_strategy_fw_svp:
1167 	case dml2_pmo_pstate_strategy_fw_svp_drr:
1168 		stream_method_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_idx].method_subvp.common;
1169 		break;
1170 	case dml2_pmo_pstate_strategy_fw_drr:
1171 		stream_method_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_idx].method_drr.common;
1172 		break;
1173 	case dml2_pmo_pstate_strategy_reserved_hw:
1174 	case dml2_pmo_pstate_strategy_reserved_fw:
1175 	case dml2_pmo_pstate_strategy_reserved_fw_drr_clamped:
1176 	case dml2_pmo_pstate_strategy_reserved_fw_drr_var:
1177 	case dml2_pmo_pstate_strategy_na:
1178 	default:
1179 		stream_method_fams2_meta = NULL;
1180 	}
1181 
1182 	return stream_method_fams2_meta;
1183 }
1184 
is_timing_group_schedulable(struct dml2_pmo_instance * pmo,const struct display_configuation_with_meta * display_cfg,const struct dml2_pmo_pstate_strategy * pstate_strategy,const unsigned int timing_group_idx,struct dml2_fams2_per_method_common_meta * group_fams2_meta)1185 static bool is_timing_group_schedulable(
1186 		struct dml2_pmo_instance *pmo,
1187 		const struct display_configuation_with_meta *display_cfg,
1188 		const struct dml2_pmo_pstate_strategy *pstate_strategy,
1189 		const unsigned int timing_group_idx,
1190 		struct dml2_fams2_per_method_common_meta *group_fams2_meta)
1191 {
1192 	unsigned int i;
1193 	struct dml2_fams2_per_method_common_meta *stream_method_fams2_meta;
1194 
1195 	unsigned int base_stream_idx = 0;
1196 	struct dml2_pmo_scratch *s = &pmo->scratch;
1197 
1198 	/* find base stream idx */
1199 	for (base_stream_idx = 0; base_stream_idx < display_cfg->display_config.num_streams; base_stream_idx++) {
1200 		if (is_bit_set_in_bitfield(s->pmo_dcn4.synchronized_timing_group_masks[timing_group_idx], base_stream_idx)) {
1201 			/* master stream found */
1202 			break;
1203 		}
1204 	}
1205 
1206 	/* init allow start and end lines for timing group */
1207 	stream_method_fams2_meta = get_per_method_common_meta(pmo, pstate_strategy->per_stream_pstate_method[base_stream_idx], base_stream_idx);
1208 	if (!stream_method_fams2_meta)
1209 		return false;
1210 
1211 	group_fams2_meta->allow_start_otg_vline = stream_method_fams2_meta->allow_start_otg_vline;
1212 	group_fams2_meta->allow_end_otg_vline = stream_method_fams2_meta->allow_end_otg_vline;
1213 	group_fams2_meta->period_us = stream_method_fams2_meta->period_us;
1214 	for (i = base_stream_idx + 1; i < display_cfg->display_config.num_streams; i++) {
1215 		if (is_bit_set_in_bitfield(pmo->scratch.pmo_dcn4.synchronized_timing_group_masks[timing_group_idx], i)) {
1216 			stream_method_fams2_meta = get_per_method_common_meta(pmo, pstate_strategy->per_stream_pstate_method[i], i);
1217 			if (!stream_method_fams2_meta)
1218 				return false;
1219 
1220 			if (group_fams2_meta->allow_start_otg_vline < stream_method_fams2_meta->allow_start_otg_vline) {
1221 				/* set group allow start to larger otg vline */
1222 				group_fams2_meta->allow_start_otg_vline = stream_method_fams2_meta->allow_start_otg_vline;
1223 			}
1224 
1225 			if (group_fams2_meta->allow_end_otg_vline > stream_method_fams2_meta->allow_end_otg_vline) {
1226 				/* set group allow end to smaller otg vline */
1227 				group_fams2_meta->allow_end_otg_vline = stream_method_fams2_meta->allow_end_otg_vline;
1228 			}
1229 
1230 			/* check waveform still has positive width */
1231 			if (group_fams2_meta->allow_start_otg_vline >= group_fams2_meta->allow_end_otg_vline) {
1232 				/* timing group is not schedulable */
1233 				return false;
1234 			}
1235 		}
1236 	}
1237 
1238 	/* calculate the rest of the meta */
1239 	build_method_scheduling_params(group_fams2_meta, &pmo->scratch.pmo_dcn4.stream_fams2_meta[base_stream_idx]);
1240 
1241 	return group_fams2_meta->allow_time_us > 0.0 &&
1242 			group_fams2_meta->disallow_time_us < pmo->ip_caps->fams2.max_allow_delay_us;
1243 }
1244 
is_config_schedulable(struct dml2_pmo_instance * pmo,const struct display_configuation_with_meta * display_cfg,const struct dml2_pmo_pstate_strategy * pstate_strategy)1245 static bool is_config_schedulable(
1246 	struct dml2_pmo_instance *pmo,
1247 	const struct display_configuation_with_meta *display_cfg,
1248 	const struct dml2_pmo_pstate_strategy *pstate_strategy)
1249 {
1250 	unsigned int i, j;
1251 	bool schedulable;
1252 	struct dml2_pmo_scratch *s = &pmo->scratch;
1253 
1254 	double max_allow_delay_us = 0.0;
1255 
1256 	memset(s->pmo_dcn4.group_common_fams2_meta, 0, sizeof(s->pmo_dcn4.group_common_fams2_meta));
1257 	memset(s->pmo_dcn4.sorted_group_gtl_disallow_index, 0, sizeof(unsigned int) * DML2_MAX_PLANES);
1258 
1259 	/* search for a general solution to the schedule */
1260 
1261 	/* STAGE 0: Early return for special cases */
1262 	if (display_cfg->display_config.num_streams == 0) {
1263 		return true;
1264 	}
1265 
1266 	/* STAGE 1: confirm allow waves overlap for synchronizable streams */
1267 	schedulable = true;
1268 	for (i = 0; i < s->pmo_dcn4.num_timing_groups; i++) {
1269 		s->pmo_dcn4.sorted_group_gtl_disallow_index[i] = i;
1270 		s->pmo_dcn4.sorted_group_gtl_period_index[i] = i;
1271 		if (!is_timing_group_schedulable(pmo, display_cfg, pstate_strategy, i, &s->pmo_dcn4.group_common_fams2_meta[i])) {
1272 			/* synchronized timing group was not schedulable */
1273 			schedulable = false;
1274 			break;
1275 		}
1276 		max_allow_delay_us += s->pmo_dcn4.group_common_fams2_meta[i].disallow_time_us;
1277 	}
1278 
1279 	if ((schedulable && s->pmo_dcn4.num_timing_groups <= 1) || !schedulable) {
1280 		/* 1. the only timing group was schedulable, so early pass
1281 		 * 2. one of the timing groups was not schedulable, so early fail */
1282 		return schedulable;
1283 	}
1284 
1285 	/* STAGE 2: Check allow can't be masked entirely by other disallows */
1286 	schedulable = true;
1287 
1288 	/* sort disallow times from greatest to least */
1289 	for (i = 0; i < s->pmo_dcn4.num_timing_groups; i++) {
1290 		bool swapped = false;
1291 
1292 		for (j = 0; j < s->pmo_dcn4.num_timing_groups - 1; j++) {
1293 			double j_disallow_us = s->pmo_dcn4.group_common_fams2_meta[s->pmo_dcn4.sorted_group_gtl_disallow_index[j]].disallow_time_us;
1294 			double jp1_disallow_us = s->pmo_dcn4.group_common_fams2_meta[s->pmo_dcn4.sorted_group_gtl_disallow_index[j + 1]].disallow_time_us;
1295 			if (j_disallow_us < jp1_disallow_us) {
1296 				/* swap as A < B */
1297 				swap(s->pmo_dcn4.sorted_group_gtl_disallow_index[j],
1298 				     s->pmo_dcn4.sorted_group_gtl_disallow_index[j+1]);
1299 				swapped = true;
1300 			}
1301 		}
1302 
1303 		/* sorted, exit early */
1304 		if (!swapped)
1305 			break;
1306 	}
1307 
1308 	/* Check worst case disallow region occurs in the middle of allow for the
1309 	* other display, or when >2 streams continue to halve the remaining allow time.
1310 	*/
1311 	for (i = 0; i < s->pmo_dcn4.num_timing_groups; i++) {
1312 		if (s->pmo_dcn4.group_common_fams2_meta[i].disallow_time_us <= 0.0) {
1313 			/* this timing group always allows */
1314 			continue;
1315 		}
1316 
1317 		double max_allow_time_us = s->pmo_dcn4.group_common_fams2_meta[i].allow_time_us;
1318 		for (j = 0; j < s->pmo_dcn4.num_timing_groups; j++) {
1319 			unsigned int sorted_j = s->pmo_dcn4.sorted_group_gtl_disallow_index[j];
1320 			/* stream can't overlap itself */
1321 			if (i != sorted_j && s->pmo_dcn4.group_common_fams2_meta[sorted_j].disallow_time_us > 0.0) {
1322 				max_allow_time_us = math_min2(
1323 						s->pmo_dcn4.group_common_fams2_meta[sorted_j].allow_time_us,
1324 						(max_allow_time_us - s->pmo_dcn4.group_common_fams2_meta[sorted_j].disallow_time_us) / 2);
1325 
1326 				if (max_allow_time_us < 0.0) {
1327 					/* failed exit early */
1328 					break;
1329 				}
1330 			}
1331 		}
1332 
1333 		if (max_allow_time_us <= 0.0) {
1334 			/* not enough time for microschedule in the worst case */
1335 			schedulable = false;
1336 			break;
1337 		}
1338 	}
1339 
1340 	if (schedulable && max_allow_delay_us < pmo->ip_caps->fams2.max_allow_delay_us) {
1341 		return true;
1342 	}
1343 
1344 	/* STAGE 3: check larger allow can fit period of all other streams */
1345 	schedulable = true;
1346 
1347 	/* sort periods from greatest to least */
1348 	for (i = 0; i < s->pmo_dcn4.num_timing_groups; i++) {
1349 		bool swapped = false;
1350 
1351 		for (j = 0; j < s->pmo_dcn4.num_timing_groups - 1; j++) {
1352 			double j_period_us = s->pmo_dcn4.group_common_fams2_meta[s->pmo_dcn4.sorted_group_gtl_period_index[j]].period_us;
1353 			double jp1_period_us = s->pmo_dcn4.group_common_fams2_meta[s->pmo_dcn4.sorted_group_gtl_period_index[j + 1]].period_us;
1354 			if (j_period_us < jp1_period_us) {
1355 				/* swap as A < B */
1356 				swap(s->pmo_dcn4.sorted_group_gtl_period_index[j],
1357 				     s->pmo_dcn4.sorted_group_gtl_period_index[j+1]);
1358 				swapped = true;
1359 			}
1360 		}
1361 
1362 		/* sorted, exit early */
1363 		if (!swapped)
1364 			break;
1365 	}
1366 
1367 	/* check larger allow can fit period of all other streams */
1368 	for (i = 0; i < s->pmo_dcn4.num_timing_groups - 1; i++) {
1369 		unsigned int sorted_i = s->pmo_dcn4.sorted_group_gtl_period_index[i];
1370 		unsigned int sorted_ip1 = s->pmo_dcn4.sorted_group_gtl_period_index[i + 1];
1371 
1372 		if (s->pmo_dcn4.group_common_fams2_meta[sorted_i].allow_time_us < s->pmo_dcn4.group_common_fams2_meta[sorted_ip1].period_us ||
1373 				(s->pmo_dcn4.group_is_drr_enabled[sorted_ip1] && s->pmo_dcn4.group_is_drr_active[sorted_ip1])) {
1374 			schedulable = false;
1375 			break;
1376 		}
1377 	}
1378 
1379 	if (schedulable && max_allow_delay_us < pmo->ip_caps->fams2.max_allow_delay_us) {
1380 		return true;
1381 	}
1382 
1383 	/* STAGE 4: When using HW exclusive modes, check disallow alignments are within allowed threshold */
1384 	if (s->pmo_dcn4.num_timing_groups == 2 &&
1385 			!is_bit_set_in_bitfield(PMO_FW_STRATEGY_MASK, pstate_strategy->per_stream_pstate_method[0]) &&
1386 			!is_bit_set_in_bitfield(PMO_FW_STRATEGY_MASK, pstate_strategy->per_stream_pstate_method[1])) {
1387 		double period_ratio;
1388 		double max_shift_us;
1389 		double shift_per_period;
1390 
1391 		/* default period_0 > period_1 */
1392 		unsigned int lrg_idx = 0;
1393 		unsigned int sml_idx = 1;
1394 		if (s->pmo_dcn4.group_common_fams2_meta[0].period_us < s->pmo_dcn4.group_common_fams2_meta[1].period_us) {
1395 			/* period_0 < period_1 */
1396 			lrg_idx = 1;
1397 			sml_idx = 0;
1398 		}
1399 		period_ratio = s->pmo_dcn4.group_common_fams2_meta[lrg_idx].period_us / s->pmo_dcn4.group_common_fams2_meta[sml_idx].period_us;
1400 		shift_per_period = s->pmo_dcn4.group_common_fams2_meta[sml_idx].period_us * (period_ratio - math_floor(period_ratio));
1401 		max_shift_us = s->pmo_dcn4.group_common_fams2_meta[lrg_idx].disallow_time_us - s->pmo_dcn4.group_common_fams2_meta[sml_idx].allow_time_us;
1402 		max_allow_delay_us = max_shift_us / shift_per_period * s->pmo_dcn4.group_common_fams2_meta[lrg_idx].period_us;
1403 
1404 		if (shift_per_period > 0.0 &&
1405 			shift_per_period < s->pmo_dcn4.group_common_fams2_meta[lrg_idx].allow_time_us + s->pmo_dcn4.group_common_fams2_meta[sml_idx].allow_time_us &&
1406 			max_allow_delay_us < pmo->ip_caps->fams2.max_allow_delay_us) {
1407 			schedulable = true;
1408 		}
1409 	}
1410 
1411 	return schedulable;
1412 }
1413 
stream_matches_drr_policy(struct dml2_pmo_instance * pmo,const struct display_configuation_with_meta * display_cfg,const enum dml2_pmo_pstate_method stream_pstate_method,unsigned int stream_index)1414 static bool stream_matches_drr_policy(struct dml2_pmo_instance *pmo,
1415 	const struct display_configuation_with_meta *display_cfg,
1416 	const enum dml2_pmo_pstate_method stream_pstate_method,
1417 	unsigned int stream_index)
1418 {
1419 	const struct dml2_stream_parameters *stream_descriptor = &display_cfg->display_config.stream_descriptors[stream_index];
1420 	bool strategy_matches_drr_requirements = true;
1421 
1422 	/* check if strategy is compatible with stream drr capability and strategy */
1423 	if (is_bit_set_in_bitfield(PMO_NO_DRR_STRATEGY_MASK, stream_pstate_method) &&
1424 			display_cfg->display_config.num_streams > 1 &&
1425 			stream_descriptor->timing.drr_config.enabled &&
1426 			(stream_descriptor->timing.drr_config.drr_active_fixed || stream_descriptor->timing.drr_config.drr_active_variable)) {
1427 		/* DRR is active, so config may become unschedulable */
1428 		strategy_matches_drr_requirements = false;
1429 	} else if (is_bit_set_in_bitfield(PMO_NO_DRR_STRATEGY_MASK, stream_pstate_method) &&
1430 			is_bit_set_in_bitfield(PMO_FW_STRATEGY_MASK, stream_pstate_method) &&
1431 			stream_descriptor->timing.drr_config.enabled &&
1432 			stream_descriptor->timing.drr_config.drr_active_variable) {
1433 		/* DRR is variable, fw exclusive methods require DRR to be clamped */
1434 		strategy_matches_drr_requirements = false;
1435 	} else if (is_bit_set_in_bitfield(PMO_DRR_VAR_STRATEGY_MASK, stream_pstate_method) &&
1436 			pmo->options->disable_drr_var_when_var_active &&
1437 			stream_descriptor->timing.drr_config.enabled &&
1438 			stream_descriptor->timing.drr_config.drr_active_variable) {
1439 		/* DRR variable is active, but policy blocks DRR for p-state when this happens */
1440 		strategy_matches_drr_requirements = false;
1441 	} else if (is_bit_set_in_bitfield(PMO_DRR_VAR_STRATEGY_MASK, stream_pstate_method) &&
1442 			(pmo->options->disable_drr_var ||
1443 			!stream_descriptor->timing.drr_config.enabled ||
1444 			stream_descriptor->timing.drr_config.disallowed)) {
1445 		/* DRR variable strategies are disallowed due to settings or policy */
1446 		strategy_matches_drr_requirements = false;
1447 	} else if (is_bit_set_in_bitfield(PMO_DRR_CLAMPED_STRATEGY_MASK, stream_pstate_method) &&
1448 		(pmo->options->disable_drr_clamped ||
1449 			(!stream_descriptor->timing.drr_config.enabled ||
1450 			(!stream_descriptor->timing.drr_config.drr_active_fixed && !stream_descriptor->timing.drr_config.drr_active_variable)) ||
1451 			(pmo->options->disable_drr_clamped_when_var_active &&
1452 			stream_descriptor->timing.drr_config.enabled &&
1453 			stream_descriptor->timing.drr_config.drr_active_variable))) {
1454 		/* DRR fixed strategies are disallowed due to settings or policy */
1455 		strategy_matches_drr_requirements = false;
1456 	} else if (is_bit_set_in_bitfield(PMO_FW_STRATEGY_MASK, stream_pstate_method) &&
1457 			pmo->options->disable_fams2) {
1458 		/* FW modes require FAMS2 */
1459 		strategy_matches_drr_requirements = false;
1460 	}
1461 
1462 	return strategy_matches_drr_requirements;
1463 }
1464 
validate_pstate_support_strategy_cofunctionality(struct dml2_pmo_instance * pmo,const struct display_configuation_with_meta * display_cfg,const struct dml2_pmo_pstate_strategy * pstate_strategy)1465 static bool validate_pstate_support_strategy_cofunctionality(struct dml2_pmo_instance *pmo,
1466 		const struct display_configuation_with_meta *display_cfg,
1467 		const struct dml2_pmo_pstate_strategy *pstate_strategy)
1468 {
1469 	struct dml2_pmo_scratch *s = &pmo->scratch;
1470 
1471 	unsigned char stream_index = 0;
1472 
1473 	unsigned int svp_count = 0;
1474 	unsigned int svp_stream_mask = 0;
1475 	unsigned int drr_count = 0;
1476 	unsigned int drr_stream_mask = 0;
1477 	unsigned int vactive_count = 0;
1478 	unsigned int vactive_stream_mask = 0;
1479 	unsigned int vblank_count = 0;
1480 	unsigned int vblank_stream_mask = 0;
1481 
1482 	bool strategy_matches_forced_requirements = true;
1483 	bool strategy_matches_drr_requirements = true;
1484 
1485 	// Tabulate everything
1486 	for (stream_index = 0; stream_index < display_cfg->display_config.num_streams; stream_index++) {
1487 
1488 		if (!all_planes_match_method(display_cfg, s->pmo_dcn4.stream_plane_mask[stream_index],
1489 			pstate_strategy->per_stream_pstate_method[stream_index])) {
1490 			strategy_matches_forced_requirements = false;
1491 			break;
1492 		}
1493 
1494 		strategy_matches_drr_requirements &=
1495 			stream_matches_drr_policy(pmo, display_cfg, pstate_strategy->per_stream_pstate_method[stream_index], stream_index);
1496 
1497 		if (pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_svp ||
1498 			pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_svp_drr) {
1499 			svp_count++;
1500 			set_bit_in_bitfield(&svp_stream_mask, stream_index);
1501 		} else if (pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_drr) {
1502 			drr_count++;
1503 			set_bit_in_bitfield(&drr_stream_mask, stream_index);
1504 		} else if (pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_vactive ||
1505 			pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_vactive_drr) {
1506 			vactive_count++;
1507 			set_bit_in_bitfield(&vactive_stream_mask, stream_index);
1508 		} else if (pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_vblank ||
1509 			pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_vblank_drr) {
1510 			vblank_count++;
1511 			set_bit_in_bitfield(&vblank_stream_mask, stream_index);
1512 		}
1513 	}
1514 
1515 	if (!strategy_matches_forced_requirements || !strategy_matches_drr_requirements)
1516 		return false;
1517 
1518 	if (vactive_count > 0 && !all_timings_support_vactive(pmo, display_cfg, vactive_stream_mask))
1519 		return false;
1520 
1521 	if (vblank_count > 0 && (pmo->options->disable_vblank || !all_timings_support_vblank(pmo, display_cfg, vblank_stream_mask)))
1522 		return false;
1523 
1524 	if (drr_count > 0 && (pmo->options->disable_drr_var || !all_timings_support_drr(pmo, display_cfg, drr_stream_mask)))
1525 		return false;
1526 
1527 	if (svp_count > 0 && (pmo->options->disable_svp || !all_timings_support_svp(pmo, display_cfg, svp_stream_mask)))
1528 		return false;
1529 
1530 	return is_config_schedulable(pmo, display_cfg, pstate_strategy);
1531 }
1532 
get_vactive_pstate_margin(const struct display_configuation_with_meta * display_cfg,int plane_mask)1533 static int get_vactive_pstate_margin(const struct display_configuation_with_meta *display_cfg, int plane_mask)
1534 {
1535 	unsigned char i;
1536 	int min_vactive_margin_us = 0xFFFFFFF;
1537 
1538 	for (i = 0; i < DML2_MAX_PLANES; i++) {
1539 		if (is_bit_set_in_bitfield(plane_mask, i)) {
1540 			if (display_cfg->mode_support_result.cfg_support_info.plane_support_info[i].dram_change_latency_hiding_margin_in_active < min_vactive_margin_us)
1541 				min_vactive_margin_us = display_cfg->mode_support_result.cfg_support_info.plane_support_info[i].dram_change_latency_hiding_margin_in_active;
1542 		}
1543 	}
1544 
1545 	return min_vactive_margin_us;
1546 }
1547 
get_vactive_det_fill_latency_delay_us(const struct display_configuation_with_meta * display_cfg,int plane_mask)1548 static unsigned int get_vactive_det_fill_latency_delay_us(const struct display_configuation_with_meta *display_cfg, int plane_mask)
1549 {
1550 	unsigned char i;
1551 	unsigned int max_vactive_fill_us = 0;
1552 
1553 	for (i = 0; i < DML2_MAX_PLANES; i++) {
1554 		if (is_bit_set_in_bitfield(plane_mask, i)) {
1555 			if (display_cfg->mode_support_result.cfg_support_info.plane_support_info[i].dram_change_vactive_det_fill_delay_us > max_vactive_fill_us)
1556 				max_vactive_fill_us = display_cfg->mode_support_result.cfg_support_info.plane_support_info[i].dram_change_vactive_det_fill_delay_us;
1557 		}
1558 	}
1559 
1560 	return max_vactive_fill_us;
1561 }
1562 
build_fams2_meta_per_stream(struct dml2_pmo_instance * pmo,struct display_configuation_with_meta * display_config,int stream_index)1563 static void build_fams2_meta_per_stream(struct dml2_pmo_instance *pmo,
1564 	struct display_configuation_with_meta *display_config,
1565 	int stream_index)
1566 {
1567 	const struct dml2_ip_capabilities *ip_caps = pmo->ip_caps;
1568 	const struct dml2_stream_parameters *stream_descriptor = &display_config->display_config.stream_descriptors[stream_index];
1569 	const struct core_stream_support_info *stream_info = &display_config->mode_support_result.cfg_support_info.stream_support_info[stream_index];
1570 	const struct dml2_timing_cfg *timing = &stream_descriptor->timing;
1571 	struct dml2_fams2_meta *stream_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_index];
1572 
1573 	/* worst case all other streams require some programming at the same time, 0 if only 1 stream */
1574 	unsigned int contention_delay_us = (ip_caps->fams2.vertical_interrupt_ack_delay_us +
1575 			(unsigned int)math_max3(ip_caps->fams2.subvp_programming_delay_us, ip_caps->fams2.drr_programming_delay_us, ip_caps->fams2.allow_programming_delay_us)) *
1576 			(display_config->display_config.num_streams - 1);
1577 
1578 	/* common */
1579 	stream_fams2_meta->valid = true;
1580 	stream_fams2_meta->otg_vline_time_us = (double)timing->h_total / timing->pixel_clock_khz * 1000.0;
1581 	stream_fams2_meta->nom_vtotal = stream_descriptor->timing.vblank_nom + stream_descriptor->timing.v_active;
1582 	stream_fams2_meta->nom_refresh_rate_hz = timing->pixel_clock_khz * 1000.0 /
1583 			(stream_fams2_meta->nom_vtotal * timing->h_total);
1584 	stream_fams2_meta->nom_frame_time_us =
1585 			(double)stream_fams2_meta->nom_vtotal * stream_fams2_meta->otg_vline_time_us;
1586 	stream_fams2_meta->vblank_start = timing->v_blank_end + timing->v_active;
1587 
1588 	if (stream_descriptor->timing.drr_config.enabled == true) {
1589 		if (stream_descriptor->timing.drr_config.min_refresh_uhz != 0.0) {
1590 			stream_fams2_meta->max_vtotal = (unsigned int)math_floor((double)stream_descriptor->timing.pixel_clock_khz /
1591 					((double)stream_descriptor->timing.drr_config.min_refresh_uhz * stream_descriptor->timing.h_total) * 1e9);
1592 		} else {
1593 			/* assume min of 48Hz */
1594 			stream_fams2_meta->max_vtotal = (unsigned int)math_floor((double)stream_descriptor->timing.pixel_clock_khz /
1595 					(48000000.0 * stream_descriptor->timing.h_total) * 1e9);
1596 		}
1597 	} else {
1598 		stream_fams2_meta->max_vtotal = stream_fams2_meta->nom_vtotal;
1599 	}
1600 	stream_fams2_meta->min_refresh_rate_hz = timing->pixel_clock_khz * 1000.0 /
1601 			(stream_fams2_meta->max_vtotal * timing->h_total);
1602 	stream_fams2_meta->max_frame_time_us =
1603 			(double)stream_fams2_meta->max_vtotal * stream_fams2_meta->otg_vline_time_us;
1604 
1605 	stream_fams2_meta->scheduling_delay_otg_vlines =
1606 			(unsigned int)math_ceil(ip_caps->fams2.scheduling_delay_us / stream_fams2_meta->otg_vline_time_us);
1607 	stream_fams2_meta->vertical_interrupt_ack_delay_otg_vlines =
1608 			(unsigned int)math_ceil(ip_caps->fams2.vertical_interrupt_ack_delay_us / stream_fams2_meta->otg_vline_time_us);
1609 	stream_fams2_meta->contention_delay_otg_vlines =
1610 			(unsigned int)math_ceil(contention_delay_us / stream_fams2_meta->otg_vline_time_us);
1611 	/* worst case allow to target needs to account for all streams' allow events overlapping, and 1 line for error */
1612 	stream_fams2_meta->allow_to_target_delay_otg_vlines =
1613 			(unsigned int)(math_ceil((ip_caps->fams2.vertical_interrupt_ack_delay_us + contention_delay_us + ip_caps->fams2.allow_programming_delay_us) / stream_fams2_meta->otg_vline_time_us)) + 1;
1614 	stream_fams2_meta->min_allow_width_otg_vlines =
1615 			(unsigned int)math_ceil(ip_caps->fams2.min_allow_width_us / stream_fams2_meta->otg_vline_time_us);
1616 	/* this value should account for urgent latency */
1617 	stream_fams2_meta->dram_clk_change_blackout_otg_vlines =
1618 			(unsigned int)math_ceil(pmo->soc_bb->power_management_parameters.dram_clk_change_blackout_us /
1619 			stream_fams2_meta->otg_vline_time_us);
1620 
1621 	/* scheduling params should be built based on the worst case for allow_time:disallow_time */
1622 
1623 	/* vactive */
1624 	if (display_config->display_config.num_streams == 1) {
1625 		/* for single stream, guarantee at least an instant of allow */
1626 		stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = (unsigned int)math_floor(
1627 				math_max2(0.0,
1628 				timing->v_active - stream_fams2_meta->min_allow_width_otg_vlines - stream_fams2_meta->dram_clk_change_blackout_otg_vlines));
1629 	} else {
1630 		/* for multi stream, bound to a max fill time defined by IP caps */
1631 		stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines =
1632 				(unsigned int)math_floor((double)ip_caps->max_vactive_det_fill_delay_us / stream_fams2_meta->otg_vline_time_us);
1633 	}
1634 	stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_us = stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines * stream_fams2_meta->otg_vline_time_us;
1635 
1636 	if (stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_us > 0.0) {
1637 		stream_fams2_meta->method_vactive.common.allow_start_otg_vline =
1638 			timing->v_blank_end + stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines;
1639 		stream_fams2_meta->method_vactive.common.allow_end_otg_vline =
1640 			stream_fams2_meta->vblank_start -
1641 			stream_fams2_meta->dram_clk_change_blackout_otg_vlines;
1642 	} else {
1643 		stream_fams2_meta->method_vactive.common.allow_start_otg_vline = 0;
1644 		stream_fams2_meta->method_vactive.common.allow_end_otg_vline = 0;
1645 	}
1646 	stream_fams2_meta->method_vactive.common.period_us = stream_fams2_meta->nom_frame_time_us;
1647 	build_method_scheduling_params(&stream_fams2_meta->method_vactive.common, stream_fams2_meta);
1648 
1649 	/* vblank */
1650 	stream_fams2_meta->method_vblank.common.allow_start_otg_vline = stream_fams2_meta->vblank_start;
1651 	stream_fams2_meta->method_vblank.common.allow_end_otg_vline =
1652 			stream_fams2_meta->method_vblank.common.allow_start_otg_vline + 1;
1653 	stream_fams2_meta->method_vblank.common.period_us = stream_fams2_meta->nom_frame_time_us;
1654 	build_method_scheduling_params(&stream_fams2_meta->method_vblank.common, stream_fams2_meta);
1655 
1656 	/* subvp */
1657 	stream_fams2_meta->method_subvp.programming_delay_otg_vlines =
1658 			(unsigned int)math_ceil(ip_caps->fams2.subvp_programming_delay_us / stream_fams2_meta->otg_vline_time_us);
1659 	stream_fams2_meta->method_subvp.df_throttle_delay_otg_vlines =
1660 			(unsigned int)math_ceil(ip_caps->fams2.subvp_df_throttle_delay_us / stream_fams2_meta->otg_vline_time_us);
1661 	stream_fams2_meta->method_subvp.prefetch_to_mall_delay_otg_vlines =
1662 			(unsigned int)math_ceil(ip_caps->fams2.subvp_prefetch_to_mall_delay_us / stream_fams2_meta->otg_vline_time_us);
1663 	stream_fams2_meta->method_subvp.phantom_vactive =
1664 			stream_fams2_meta->allow_to_target_delay_otg_vlines +
1665 			stream_fams2_meta->min_allow_width_otg_vlines +
1666 			stream_info->phantom_min_v_active;
1667 	stream_fams2_meta->method_subvp.phantom_vfp =
1668 			stream_fams2_meta->method_subvp.df_throttle_delay_otg_vlines;
1669 	/* phantom vtotal = v_bp(vstartup) + v_sync(1) + v_fp(throttle_delay) + v_active(allow_to_target + min_allow + min_vactive)*/
1670 	stream_fams2_meta->method_subvp.phantom_vtotal =
1671 			stream_info->phantom_v_startup +
1672 			stream_fams2_meta->method_subvp.phantom_vfp +
1673 			1 +
1674 			stream_fams2_meta->method_subvp.df_throttle_delay_otg_vlines +
1675 			stream_fams2_meta->method_subvp.phantom_vactive;
1676 	stream_fams2_meta->method_subvp.common.allow_start_otg_vline =
1677 			stream_descriptor->timing.v_blank_end +
1678 			stream_fams2_meta->contention_delay_otg_vlines +
1679 			stream_fams2_meta->method_subvp.programming_delay_otg_vlines +
1680 			stream_fams2_meta->method_subvp.phantom_vtotal +
1681 			stream_fams2_meta->method_subvp.prefetch_to_mall_delay_otg_vlines +
1682 			stream_fams2_meta->allow_to_target_delay_otg_vlines;
1683 	stream_fams2_meta->method_subvp.common.allow_end_otg_vline =
1684 			stream_fams2_meta->vblank_start -
1685 			stream_fams2_meta->dram_clk_change_blackout_otg_vlines;
1686 	stream_fams2_meta->method_subvp.common.period_us = stream_fams2_meta->nom_frame_time_us;
1687 	build_method_scheduling_params(&stream_fams2_meta->method_subvp.common, stream_fams2_meta);
1688 
1689 	/* drr */
1690 	stream_fams2_meta->method_drr.programming_delay_otg_vlines =
1691 			(unsigned int)math_ceil(ip_caps->fams2.drr_programming_delay_us / stream_fams2_meta->otg_vline_time_us);
1692 	stream_fams2_meta->method_drr.common.allow_start_otg_vline =
1693 			stream_fams2_meta->vblank_start +
1694 			stream_fams2_meta->allow_to_target_delay_otg_vlines;
1695 	stream_fams2_meta->method_drr.common.period_us = stream_fams2_meta->nom_frame_time_us;
1696 	if (display_config->display_config.num_streams <= 1) {
1697 		/* only need to stretch vblank for blackout time */
1698 		stream_fams2_meta->method_drr.stretched_vtotal =
1699 				stream_fams2_meta->nom_vtotal +
1700 				stream_fams2_meta->allow_to_target_delay_otg_vlines +
1701 				stream_fams2_meta->min_allow_width_otg_vlines +
1702 				stream_fams2_meta->dram_clk_change_blackout_otg_vlines;
1703 	} else {
1704 		/* multi display needs to always be schedulable */
1705 		stream_fams2_meta->method_drr.stretched_vtotal =
1706 				stream_fams2_meta->nom_vtotal * 2 +
1707 				stream_fams2_meta->allow_to_target_delay_otg_vlines +
1708 				stream_fams2_meta->min_allow_width_otg_vlines +
1709 				stream_fams2_meta->dram_clk_change_blackout_otg_vlines;
1710 	}
1711 	stream_fams2_meta->method_drr.common.allow_end_otg_vline =
1712 			stream_fams2_meta->method_drr.stretched_vtotal -
1713 			stream_fams2_meta->dram_clk_change_blackout_otg_vlines;
1714 	build_method_scheduling_params(&stream_fams2_meta->method_drr.common, stream_fams2_meta);
1715 }
1716 
build_subvp_meta_per_stream(struct dml2_pmo_instance * pmo,struct display_configuation_with_meta * display_config,int stream_index)1717 static void build_subvp_meta_per_stream(struct dml2_pmo_instance *pmo,
1718 	struct display_configuation_with_meta *display_config,
1719 	int stream_index)
1720 {
1721 	struct dml2_implicit_svp_meta *stream_svp_meta = &pmo->scratch.pmo_dcn4.stream_svp_meta[stream_index];
1722 	struct dml2_fams2_meta *stream_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_index];
1723 
1724 	stream_svp_meta->valid = true;
1725 
1726 	/* PMO FAMS2 precaulcates these values */
1727 	stream_svp_meta->v_active = stream_fams2_meta->method_subvp.phantom_vactive;
1728 	stream_svp_meta->v_front_porch = stream_fams2_meta->method_subvp.phantom_vfp;
1729 	stream_svp_meta->v_total = stream_fams2_meta->method_subvp.phantom_vtotal;
1730 }
1731 
pmo_dcn4_fams2_init_for_pstate_support(struct dml2_pmo_init_for_pstate_support_in_out * in_out)1732 bool pmo_dcn4_fams2_init_for_pstate_support(struct dml2_pmo_init_for_pstate_support_in_out *in_out)
1733 {
1734 	struct dml2_pmo_instance *pmo = in_out->instance;
1735 	struct dml2_optimization_stage3_state *state = &in_out->base_display_config->stage3;
1736 	struct dml2_pmo_scratch *s = &pmo->scratch;
1737 
1738 	struct display_configuation_with_meta *display_config;
1739 	const struct dml2_plane_parameters *plane_descriptor;
1740 	const struct dml2_pmo_pstate_strategy *strategy_list = NULL;
1741 	unsigned int strategy_list_size = 0;
1742 	unsigned char plane_index, stream_index, i;
1743 
1744 	state->performed = true;
1745 	in_out->base_display_config->stage3.min_clk_index_for_latency = in_out->base_display_config->stage1.min_clk_index_for_latency;
1746 
1747 	display_config = in_out->base_display_config;
1748 	display_config->display_config.overrides.enable_subvp_implicit_pmo = true;
1749 
1750 	memset(s, 0, sizeof(struct dml2_pmo_scratch));
1751 
1752 	if (display_config->display_config.overrides.all_streams_blanked) {
1753 		return true;
1754 	}
1755 
1756 	pmo->scratch.pmo_dcn4.min_latency_index = in_out->base_display_config->stage1.min_clk_index_for_latency;
1757 	pmo->scratch.pmo_dcn4.max_latency_index = pmo->mcg_clock_table_size;
1758 	pmo->scratch.pmo_dcn4.cur_latency_index = in_out->base_display_config->stage1.min_clk_index_for_latency;
1759 
1760 	// First build the stream plane mask (array of bitfields indexed by stream, indicating plane mapping)
1761 	for (plane_index = 0; plane_index < display_config->display_config.num_planes; plane_index++) {
1762 		plane_descriptor = &display_config->display_config.plane_descriptors[plane_index];
1763 
1764 		set_bit_in_bitfield(&s->pmo_dcn4.stream_plane_mask[plane_descriptor->stream_index], plane_index);
1765 
1766 		state->pstate_switch_modes[plane_index] = dml2_uclk_pstate_support_method_vactive;
1767 	}
1768 
1769 	// Figure out which streams can do vactive, and also build up implicit SVP and FAMS2 meta
1770 	for (stream_index = 0; stream_index < display_config->display_config.num_streams; stream_index++) {
1771 		if (get_vactive_pstate_margin(display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) >= (int)(MIN_VACTIVE_MARGIN_PCT * pmo->soc_bb->power_management_parameters.dram_clk_change_blackout_us))
1772 			set_bit_in_bitfield(&s->pmo_dcn4.stream_vactive_capability_mask, stream_index);
1773 
1774 		/* FAMS2 meta */
1775 		build_fams2_meta_per_stream(pmo, display_config, stream_index);
1776 
1777 		/* SVP meta */
1778 		build_subvp_meta_per_stream(pmo, display_config, stream_index);
1779 	}
1780 
1781 	/* get synchronized timing groups */
1782 	build_synchronized_timing_groups(pmo, display_config);
1783 
1784 	strategy_list = get_expanded_strategy_list(&pmo->init_data, display_config->display_config.num_streams);
1785 	if (!strategy_list)
1786 		return false;
1787 
1788 	strategy_list_size = get_num_expanded_strategies(&pmo->init_data, display_config->display_config.num_streams);
1789 
1790 	if (strategy_list_size == 0)
1791 		return false;
1792 
1793 	s->pmo_dcn4.num_pstate_candidates = 0;
1794 
1795 	for (i = 0; i < strategy_list_size && s->pmo_dcn4.num_pstate_candidates < DML2_PMO_PSTATE_CANDIDATE_LIST_SIZE; i++) {
1796 		if (validate_pstate_support_strategy_cofunctionality(pmo, display_config, &strategy_list[i])) {
1797 			insert_into_candidate_list(&strategy_list[i], display_config->display_config.num_streams, s);
1798 		}
1799 	}
1800 
1801 	if (s->pmo_dcn4.num_pstate_candidates > 0) {
1802 		s->pmo_dcn4.cur_pstate_candidate = -1;
1803 		return true;
1804 	} else {
1805 		return false;
1806 	}
1807 }
1808 
reset_display_configuration(struct display_configuation_with_meta * display_config)1809 static void reset_display_configuration(struct display_configuation_with_meta *display_config)
1810 {
1811 	unsigned int plane_index;
1812 	unsigned int stream_index;
1813 	struct dml2_plane_parameters *plane;
1814 
1815 	for (stream_index = 0; stream_index < display_config->display_config.num_streams; stream_index++) {
1816 		display_config->stage3.stream_svp_meta[stream_index].valid = false;
1817 
1818 		display_config->display_config.stream_descriptors[stream_index].overrides.minimize_active_latency_hiding = false;
1819 		display_config->display_config.overrides.best_effort_min_active_latency_hiding_us = 0;
1820 	}
1821 
1822 	for (plane_index = 0; plane_index < display_config->display_config.num_planes; plane_index++) {
1823 		plane = &display_config->display_config.plane_descriptors[plane_index];
1824 
1825 		// Unset SubVP
1826 		plane->overrides.legacy_svp_config = dml2_svp_mode_override_auto;
1827 
1828 		// Remove reserve time
1829 		plane->overrides.reserved_vblank_time_ns = 0;
1830 
1831 		// Reset strategy to auto
1832 		plane->overrides.uclk_pstate_change_strategy = dml2_uclk_pstate_change_strategy_auto;
1833 
1834 		display_config->stage3.pstate_switch_modes[plane_index] = dml2_uclk_pstate_support_method_not_supported;
1835 	}
1836 }
1837 
setup_planes_for_drr_by_mask(struct display_configuation_with_meta * display_config,struct dml2_pmo_instance * pmo,int plane_mask)1838 static void setup_planes_for_drr_by_mask(struct display_configuation_with_meta *display_config,
1839 	struct dml2_pmo_instance *pmo,
1840 	int plane_mask)
1841 {
1842 	unsigned char plane_index;
1843 	struct dml2_plane_parameters *plane;
1844 
1845 	for (plane_index = 0; plane_index < display_config->display_config.num_planes; plane_index++) {
1846 		if (is_bit_set_in_bitfield(plane_mask, plane_index)) {
1847 			plane = &display_config->display_config.plane_descriptors[plane_index];
1848 
1849 			plane->overrides.uclk_pstate_change_strategy = dml2_uclk_pstate_change_strategy_force_drr;
1850 
1851 			display_config->stage3.pstate_switch_modes[plane_index] = dml2_uclk_pstate_support_method_fw_drr;
1852 
1853 		}
1854 	}
1855 }
1856 
setup_planes_for_svp_by_mask(struct display_configuation_with_meta * display_config,struct dml2_pmo_instance * pmo,int plane_mask)1857 static void setup_planes_for_svp_by_mask(struct display_configuation_with_meta *display_config,
1858 	struct dml2_pmo_instance *pmo,
1859 	int plane_mask)
1860 {
1861 	struct dml2_pmo_scratch *scratch = &pmo->scratch;
1862 
1863 	unsigned char plane_index;
1864 	int stream_index = -1;
1865 
1866 	for (plane_index = 0; plane_index < display_config->display_config.num_planes; plane_index++) {
1867 		if (is_bit_set_in_bitfield(plane_mask, plane_index)) {
1868 			stream_index = (char)display_config->display_config.plane_descriptors[plane_index].stream_index;
1869 			display_config->stage3.pstate_switch_modes[plane_index] = dml2_uclk_pstate_support_method_fw_subvp_phantom;
1870 		}
1871 	}
1872 
1873 	if (stream_index >= 0) {
1874 		memcpy(&display_config->stage3.stream_svp_meta[stream_index],
1875 			&scratch->pmo_dcn4.stream_svp_meta[stream_index],
1876 			sizeof(struct dml2_implicit_svp_meta));
1877 	}
1878 }
1879 
setup_planes_for_svp_drr_by_mask(struct display_configuation_with_meta * display_config,struct dml2_pmo_instance * pmo,int plane_mask)1880 static void setup_planes_for_svp_drr_by_mask(struct display_configuation_with_meta *display_config,
1881 	struct dml2_pmo_instance *pmo,
1882 	int plane_mask)
1883 {
1884 	struct dml2_pmo_scratch *scratch = &pmo->scratch;
1885 
1886 	unsigned char plane_index;
1887 	int stream_index = -1;
1888 
1889 	for (plane_index = 0; plane_index < display_config->display_config.num_planes; plane_index++) {
1890 		if (is_bit_set_in_bitfield(plane_mask, plane_index)) {
1891 			stream_index = (char)display_config->display_config.plane_descriptors[plane_index].stream_index;
1892 			display_config->stage3.pstate_switch_modes[plane_index] = dml2_uclk_pstate_support_method_fw_subvp_phantom_drr;
1893 		}
1894 	}
1895 
1896 	if (stream_index >= 0) {
1897 		memcpy(&display_config->stage3.stream_svp_meta[stream_index],
1898 			&scratch->pmo_dcn4.stream_svp_meta[stream_index],
1899 			sizeof(struct dml2_implicit_svp_meta));
1900 	}
1901 }
1902 
setup_planes_for_vblank_by_mask(struct display_configuation_with_meta * display_config,struct dml2_pmo_instance * pmo,int plane_mask)1903 static void setup_planes_for_vblank_by_mask(struct display_configuation_with_meta *display_config,
1904 	struct dml2_pmo_instance *pmo,
1905 	int plane_mask)
1906 {
1907 	unsigned char plane_index;
1908 	struct dml2_plane_parameters *plane;
1909 
1910 	for (plane_index = 0; plane_index < display_config->display_config.num_planes; plane_index++) {
1911 		if (is_bit_set_in_bitfield(plane_mask, plane_index)) {
1912 			plane = &display_config->display_config.plane_descriptors[plane_index];
1913 
1914 			plane->overrides.reserved_vblank_time_ns = (long)math_max2(pmo->soc_bb->power_management_parameters.dram_clk_change_blackout_us * 1000.0,
1915 					plane->overrides.reserved_vblank_time_ns);
1916 
1917 			display_config->stage3.pstate_switch_modes[plane_index] = dml2_uclk_pstate_support_method_vblank;
1918 
1919 		}
1920 	}
1921 }
1922 
setup_planes_for_vblank_drr_by_mask(struct display_configuation_with_meta * display_config,struct dml2_pmo_instance * pmo,int plane_mask)1923 static void setup_planes_for_vblank_drr_by_mask(struct display_configuation_with_meta *display_config,
1924 	struct dml2_pmo_instance *pmo,
1925 	int plane_mask)
1926 {
1927 	unsigned char plane_index;
1928 	struct dml2_plane_parameters *plane;
1929 
1930 	for (plane_index = 0; plane_index < display_config->display_config.num_planes; plane_index++) {
1931 		if (is_bit_set_in_bitfield(plane_mask, plane_index)) {
1932 			plane = &display_config->display_config.plane_descriptors[plane_index];
1933 			plane->overrides.reserved_vblank_time_ns = (long)(pmo->soc_bb->power_management_parameters.dram_clk_change_blackout_us * 1000);
1934 
1935 			display_config->stage3.pstate_switch_modes[plane_index] = dml2_uclk_pstate_support_method_fw_vblank_drr;
1936 		}
1937 	}
1938 }
1939 
setup_planes_for_vactive_by_mask(struct display_configuation_with_meta * display_config,struct dml2_pmo_instance * pmo,int plane_mask)1940 static void setup_planes_for_vactive_by_mask(struct display_configuation_with_meta *display_config,
1941 	struct dml2_pmo_instance *pmo,
1942 	int plane_mask)
1943 {
1944 	unsigned char plane_index;
1945 	unsigned int stream_index;
1946 
1947 	for (plane_index = 0; plane_index < display_config->display_config.num_planes; plane_index++) {
1948 		if (is_bit_set_in_bitfield(plane_mask, plane_index)) {
1949 			stream_index = display_config->display_config.plane_descriptors[plane_index].stream_index;
1950 
1951 			display_config->stage3.pstate_switch_modes[plane_index] = dml2_uclk_pstate_support_method_vactive;
1952 
1953 			if (!pmo->options->disable_vactive_det_fill_bw_pad) {
1954 				display_config->display_config.plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us =
1955 					(unsigned int)math_floor(pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us);
1956 			}
1957 		}
1958 	}
1959 }
1960 
setup_planes_for_vactive_drr_by_mask(struct display_configuation_with_meta * display_config,struct dml2_pmo_instance * pmo,int plane_mask)1961 static void setup_planes_for_vactive_drr_by_mask(struct display_configuation_with_meta *display_config,
1962 	struct dml2_pmo_instance *pmo,
1963 	int plane_mask)
1964 {
1965 	unsigned char plane_index;
1966 	unsigned int stream_index;
1967 
1968 	for (plane_index = 0; plane_index < display_config->display_config.num_planes; plane_index++) {
1969 		if (is_bit_set_in_bitfield(plane_mask, plane_index)) {
1970 			stream_index = display_config->display_config.plane_descriptors[plane_index].stream_index;
1971 
1972 			display_config->stage3.pstate_switch_modes[plane_index] = dml2_uclk_pstate_support_method_fw_vactive_drr;
1973 
1974 			if (!pmo->options->disable_vactive_det_fill_bw_pad) {
1975 				display_config->display_config.plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us =
1976 					(unsigned int)math_floor(pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us);
1977 			}
1978 		}
1979 	}
1980 }
1981 
setup_display_config(struct display_configuation_with_meta * display_config,struct dml2_pmo_instance * pmo,int strategy_index)1982 static bool setup_display_config(struct display_configuation_with_meta *display_config, struct dml2_pmo_instance *pmo, int strategy_index)
1983 {
1984 	struct dml2_pmo_scratch *scratch = &pmo->scratch;
1985 
1986 	bool fams2_required = false;
1987 	bool success = true;
1988 	unsigned int stream_index;
1989 
1990 	reset_display_configuration(display_config);
1991 
1992 	for (stream_index = 0; stream_index < display_config->display_config.num_streams; stream_index++) {
1993 
1994 		if (pmo->scratch.pmo_dcn4.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_na) {
1995 			success = false;
1996 			break;
1997 		} else if (scratch->pmo_dcn4.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_vactive) {
1998 			setup_planes_for_vactive_by_mask(display_config, pmo, scratch->pmo_dcn4.stream_plane_mask[stream_index]);
1999 		} else if (scratch->pmo_dcn4.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_vblank) {
2000 			setup_planes_for_vblank_by_mask(display_config, pmo, scratch->pmo_dcn4.stream_plane_mask[stream_index]);
2001 		} else if (scratch->pmo_dcn4.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_svp) {
2002 			fams2_required = true;
2003 			setup_planes_for_svp_by_mask(display_config, pmo, scratch->pmo_dcn4.stream_plane_mask[stream_index]);
2004 		} else if (scratch->pmo_dcn4.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_vactive_drr) {
2005 			fams2_required = true;
2006 			setup_planes_for_vactive_drr_by_mask(display_config, pmo, scratch->pmo_dcn4.stream_plane_mask[stream_index]);
2007 		} else if (scratch->pmo_dcn4.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_vblank_drr) {
2008 			fams2_required = true;
2009 			setup_planes_for_vblank_drr_by_mask(display_config, pmo, scratch->pmo_dcn4.stream_plane_mask[stream_index]);
2010 		} else if (scratch->pmo_dcn4.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_svp_drr) {
2011 			fams2_required = true;
2012 			setup_planes_for_svp_drr_by_mask(display_config, pmo, scratch->pmo_dcn4.stream_plane_mask[stream_index]);
2013 		} else if (scratch->pmo_dcn4.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_drr) {
2014 			fams2_required = true;
2015 			setup_planes_for_drr_by_mask(display_config, pmo, scratch->pmo_dcn4.stream_plane_mask[stream_index]);
2016 		}
2017 	}
2018 
2019 	/* copy FAMS2 meta */
2020 	if (success) {
2021 		display_config->stage3.fams2_required = fams2_required;
2022 		memcpy(&display_config->stage3.stream_fams2_meta,
2023 			&scratch->pmo_dcn4.stream_fams2_meta,
2024 			sizeof(struct dml2_fams2_meta) * DML2_MAX_PLANES);
2025 	}
2026 
2027 	return success;
2028 }
2029 
get_minimum_reserved_time_us_for_planes(struct display_configuation_with_meta * display_config,int plane_mask)2030 static int get_minimum_reserved_time_us_for_planes(struct display_configuation_with_meta *display_config, int plane_mask)
2031 {
2032 	int min_time_us = 0xFFFFFF;
2033 	unsigned char plane_index = 0;
2034 
2035 	for (plane_index = 0; plane_index < display_config->display_config.num_planes; plane_index++) {
2036 		if (is_bit_set_in_bitfield(plane_mask, plane_index)) {
2037 			if (min_time_us > (display_config->display_config.plane_descriptors[plane_index].overrides.reserved_vblank_time_ns / 1000))
2038 				min_time_us = display_config->display_config.plane_descriptors[plane_index].overrides.reserved_vblank_time_ns / 1000;
2039 		}
2040 	}
2041 	return min_time_us;
2042 }
2043 
pmo_dcn4_fams2_test_for_pstate_support(struct dml2_pmo_test_for_pstate_support_in_out * in_out)2044 bool pmo_dcn4_fams2_test_for_pstate_support(struct dml2_pmo_test_for_pstate_support_in_out *in_out)
2045 {
2046 	bool p_state_supported = true;
2047 	unsigned int stream_index;
2048 	struct dml2_pmo_scratch *s = &in_out->instance->scratch;
2049 
2050 	int MIN_VACTIVE_MARGIN_VBLANK = 0;
2051 	int MIN_VACTIVE_MARGIN_DRR = 0;
2052 	int REQUIRED_RESERVED_TIME = 0;
2053 
2054 	if (in_out->base_display_config->display_config.overrides.all_streams_blanked) {
2055 		return true;
2056 	}
2057 
2058 	MIN_VACTIVE_MARGIN_VBLANK = INT_MIN;
2059 	MIN_VACTIVE_MARGIN_DRR = INT_MIN;
2060 	REQUIRED_RESERVED_TIME = (int)in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us;
2061 
2062 	if (s->pmo_dcn4.cur_pstate_candidate < 0)
2063 		return false;
2064 
2065 	for (stream_index = 0; stream_index < in_out->base_display_config->display_config.num_streams; stream_index++) {
2066 		struct dml2_fams2_meta *stream_fams2_meta = &s->pmo_dcn4.stream_fams2_meta[stream_index];
2067 
2068 		if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_vactive ||
2069 				s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_vactive_drr) {
2070 			if (get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < (MIN_VACTIVE_MARGIN_PCT * in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us) ||
2071 					get_vactive_det_fill_latency_delay_us(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) > stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_us) {
2072 				p_state_supported = false;
2073 				break;
2074 			}
2075 		} else if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_vblank ||
2076 				s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_vblank_drr) {
2077 			if (get_minimum_reserved_time_us_for_planes(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) <
2078 				REQUIRED_RESERVED_TIME ||
2079 				get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < MIN_VACTIVE_MARGIN_VBLANK) {
2080 				p_state_supported = false;
2081 				break;
2082 			}
2083 		} else if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_svp ||
2084 				s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_svp_drr) {
2085 			if (in_out->base_display_config->stage3.stream_svp_meta[stream_index].valid == false) {
2086 				p_state_supported = false;
2087 				break;
2088 			}
2089 		} else if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_fw_drr) {
2090 			if (!all_planes_match_method(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index], dml2_pmo_pstate_strategy_fw_drr) ||
2091 				get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < MIN_VACTIVE_MARGIN_DRR) {
2092 				p_state_supported = false;
2093 				break;
2094 			}
2095 		} else if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pmo_pstate_strategy_na) {
2096 			p_state_supported = false;
2097 			break;
2098 		}
2099 	}
2100 
2101 	return p_state_supported;
2102 }
2103 
pmo_dcn4_fams2_optimize_for_pstate_support(struct dml2_pmo_optimize_for_pstate_support_in_out * in_out)2104 bool pmo_dcn4_fams2_optimize_for_pstate_support(struct dml2_pmo_optimize_for_pstate_support_in_out *in_out)
2105 {
2106 	bool success = false;
2107 	struct dml2_pmo_scratch *s = &in_out->instance->scratch;
2108 
2109 	memcpy(in_out->optimized_display_config, in_out->base_display_config, sizeof(struct display_configuation_with_meta));
2110 
2111 	if (in_out->last_candidate_failed) {
2112 		if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].allow_state_increase &&
2113 			s->pmo_dcn4.cur_latency_index < s->pmo_dcn4.max_latency_index - 1) {
2114 			s->pmo_dcn4.cur_latency_index++;
2115 
2116 			success = true;
2117 		}
2118 	}
2119 
2120 	if (!success) {
2121 		s->pmo_dcn4.cur_latency_index = s->pmo_dcn4.min_latency_index;
2122 		s->pmo_dcn4.cur_pstate_candidate++;
2123 
2124 		if (s->pmo_dcn4.cur_pstate_candidate < s->pmo_dcn4.num_pstate_candidates) {
2125 			success = true;
2126 		}
2127 	}
2128 
2129 	if (success) {
2130 		in_out->optimized_display_config->stage3.min_clk_index_for_latency = s->pmo_dcn4.cur_latency_index;
2131 		setup_display_config(in_out->optimized_display_config, in_out->instance, in_out->instance->scratch.pmo_dcn4.cur_pstate_candidate);
2132 	}
2133 
2134 	return success;
2135 }
2136 
pmo_dcn4_fams2_init_for_stutter(struct dml2_pmo_init_for_stutter_in_out * in_out)2137 bool pmo_dcn4_fams2_init_for_stutter(struct dml2_pmo_init_for_stutter_in_out *in_out)
2138 {
2139 	bool success = true;
2140 	struct dml2_pmo_instance *pmo = in_out->instance;
2141 	bool stutter_period_meets_z8_eco = true;
2142 	bool z8_stutter_optimization_too_expensive = false;
2143 	bool stutter_optimization_too_expensive = false;
2144 	double line_time_us, vblank_nom_time_us;
2145 
2146 	unsigned int i;
2147 
2148 	if (pmo->soc_bb->power_management_parameters.z8_stutter_exit_latency_us > 0 &&
2149 		pmo->soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us > 0 &&
2150 		pmo->soc_bb->power_management_parameters.z8_stutter_exit_latency_us < pmo->soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us)
2151 		return false; // Unexpected SoCBB setup
2152 
2153 	for (i = 0; i < in_out->base_display_config->display_config.num_planes; i++) {
2154 		if (in_out->base_display_config->mode_support_result.cfg_support_info.plane_support_info[i].active_latency_hiding_us <
2155 			pmo->soc_bb->power_management_parameters.z8_stutter_exit_latency_us + pmo->soc_bb->power_management_parameters.z8_min_idle_time) {
2156 			stutter_period_meets_z8_eco = false;
2157 			break;
2158 		}
2159 	}
2160 
2161 	for (i = 0; i < in_out->base_display_config->display_config.num_streams; i++) {
2162 		line_time_us = (double)in_out->base_display_config->display_config.stream_descriptors[i].timing.h_total / (in_out->base_display_config->display_config.stream_descriptors[i].timing.pixel_clock_khz * 1000) * 1000000;
2163 		vblank_nom_time_us = line_time_us * in_out->base_display_config->display_config.stream_descriptors[i].timing.vblank_nom;
2164 
2165 		if (vblank_nom_time_us < pmo->soc_bb->power_management_parameters.z8_stutter_exit_latency_us * MIN_BLANK_STUTTER_FACTOR) {
2166 			z8_stutter_optimization_too_expensive = true;
2167 			break;
2168 		}
2169 
2170 		if (vblank_nom_time_us < pmo->soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us * MIN_BLANK_STUTTER_FACTOR) {
2171 			stutter_optimization_too_expensive = true;
2172 			break;
2173 		}
2174 	}
2175 
2176 	pmo->scratch.pmo_dcn4.num_stutter_candidates = 0;
2177 	pmo->scratch.pmo_dcn4.cur_stutter_candidate = 0;
2178 
2179 	if (stutter_period_meets_z8_eco && !z8_stutter_optimization_too_expensive) {
2180 		if (pmo->soc_bb->power_management_parameters.z8_stutter_exit_latency_us > 0) {
2181 			pmo->scratch.pmo_dcn4.optimal_vblank_reserved_time_for_stutter_us[pmo->scratch.pmo_dcn4.num_stutter_candidates] = (unsigned int)pmo->soc_bb->power_management_parameters.z8_stutter_exit_latency_us;
2182 			pmo->scratch.pmo_dcn4.num_stutter_candidates++;
2183 			pmo->scratch.pmo_dcn4.z8_vblank_optimizable = true;
2184 		}
2185 	} else {
2186 		pmo->scratch.pmo_dcn4.z8_vblank_optimizable = false;
2187 	}
2188 
2189 	if (!stutter_optimization_too_expensive && pmo->soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us > 0) {
2190 		pmo->scratch.pmo_dcn4.optimal_vblank_reserved_time_for_stutter_us[pmo->scratch.pmo_dcn4.num_stutter_candidates] = (unsigned int)pmo->soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us;
2191 		pmo->scratch.pmo_dcn4.num_stutter_candidates++;
2192 	}
2193 
2194 	if (pmo->scratch.pmo_dcn4.num_stutter_candidates == 0)
2195 		success = false;
2196 
2197 	return success;
2198 }
2199 
pmo_dcn4_fams2_test_for_stutter(struct dml2_pmo_test_for_stutter_in_out * in_out)2200 bool pmo_dcn4_fams2_test_for_stutter(struct dml2_pmo_test_for_stutter_in_out *in_out)
2201 {
2202 	bool success = true;
2203 	struct dml2_pmo_instance *pmo = in_out->instance;
2204 
2205 	unsigned int i;
2206 
2207 	for (i = 0; i < in_out->base_display_config->display_config.num_planes; i++) {
2208 		if (pmo->soc_bb->power_management_parameters.z8_stutter_exit_latency_us > 0 &&
2209 			pmo->scratch.pmo_dcn4.z8_vblank_optimizable &&
2210 			in_out->base_display_config->display_config.plane_descriptors[i].overrides.reserved_vblank_time_ns < (int)pmo->soc_bb->power_management_parameters.z8_stutter_exit_latency_us * 1000) {
2211 			success = false;
2212 			break;
2213 		}
2214 		if (pmo->soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us > 0 &&
2215 			in_out->base_display_config->display_config.plane_descriptors[i].overrides.reserved_vblank_time_ns < (int)pmo->soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us * 1000) {
2216 			success = false;
2217 			break;
2218 		}
2219 	}
2220 
2221 	return success;
2222 }
2223 
pmo_dcn4_fams2_optimize_for_stutter(struct dml2_pmo_optimize_for_stutter_in_out * in_out)2224 bool pmo_dcn4_fams2_optimize_for_stutter(struct dml2_pmo_optimize_for_stutter_in_out *in_out)
2225 {
2226 	bool success = false;
2227 	struct dml2_pmo_instance *pmo = in_out->instance;
2228 	unsigned int i;
2229 
2230 	memcpy(in_out->optimized_display_config, in_out->base_display_config, sizeof(struct display_configuation_with_meta));
2231 
2232 	if (!in_out->last_candidate_failed) {
2233 		if (pmo->scratch.pmo_dcn4.cur_stutter_candidate < pmo->scratch.pmo_dcn4.num_stutter_candidates) {
2234 			for (i = 0; i < in_out->optimized_display_config->display_config.num_planes; i++) {
2235 				/* take the max of the current and the optimal reserved time */
2236 				in_out->optimized_display_config->display_config.plane_descriptors[i].overrides.reserved_vblank_time_ns =
2237 						(long)math_max2(pmo->scratch.pmo_dcn4.optimal_vblank_reserved_time_for_stutter_us[pmo->scratch.pmo_dcn4.cur_stutter_candidate] * 1000,
2238 						in_out->optimized_display_config->display_config.plane_descriptors[i].overrides.reserved_vblank_time_ns);
2239 			}
2240 
2241 			success = true;
2242 		}
2243 	}
2244 
2245 	return success;
2246 }
2247