xref: /linux/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c (revision 546b928da0427b0d6c663cbb992bd7bfa9ac7971)
1 // SPDX-License-Identifier: MIT
2 //
3 // Copyright 2025 Advanced Micro Devices, Inc.
4 
5 #include "dml2_pmo_dcn5_stage_optimizers.h"
6 #include "dml2_pmo_dcn6_stage_optimizers.h"
7 #include "dml2_debug.h"
8 #include "lib_float_math.h"
9 
uclk_pstate_strategy_override_to_pstate_method(const enum dml2_uclk_pstate_change_strategy override_strategy)10 static enum dml2_pstate_method uclk_pstate_strategy_override_to_pstate_method(const enum dml2_uclk_pstate_change_strategy override_strategy)
11 {
12 	enum dml2_pstate_method method = dml2_pstate_method_na;
13 
14 	switch (override_strategy) {
15 	case dml2_uclk_pstate_change_strategy_force_vactive:
16 		method = dml2_pstate_method_vactive;
17 		break;
18 	case dml2_uclk_pstate_change_strategy_force_vblank:
19 		method = dml2_pstate_method_vblank;
20 		break;
21 	case dml2_uclk_pstate_change_strategy_force_drr:
22 		method = dml2_pstate_method_fw_drr;
23 		break;
24 	case dml2_uclk_pstate_change_strategy_force_alternate:
25 		method = dml2_pstate_method_alternate;
26 		break;
27 	case dml2_uclk_pstate_change_strategy_force_mall_svp:
28 	case dml2_uclk_pstate_change_strategy_force_mall_full_frame:
29 	case dml2_uclk_pstate_change_strategy_auto:
30 	default:
31 		method = dml2_pstate_method_na;
32 	}
33 
34 	return method;
35 }
36 
pstate_method_to_uclk_pstate_strategy_override(const enum dml2_pstate_method method)37 static enum dml2_uclk_pstate_change_strategy pstate_method_to_uclk_pstate_strategy_override(const enum dml2_pstate_method method)
38 {
39 	enum dml2_uclk_pstate_change_strategy override_strategy = dml2_uclk_pstate_change_strategy_auto;
40 
41 	switch (method) {
42 	case dml2_pstate_method_vactive:
43 	case dml2_pstate_method_fw_vactive_drr:
44 		override_strategy = dml2_uclk_pstate_change_strategy_force_vactive;
45 		break;
46 	case dml2_pstate_method_vblank:
47 	case dml2_pstate_method_fw_vblank_drr:
48 		override_strategy = dml2_uclk_pstate_change_strategy_force_vblank;
49 		break;
50 	case dml2_pstate_method_fw_drr:
51 		override_strategy = dml2_uclk_pstate_change_strategy_force_drr;
52 		break;
53 	case dml2_pstate_method_alternate:
54 		override_strategy = dml2_uclk_pstate_change_strategy_force_alternate;
55 		break;
56 	case dml2_pstate_method_fw_svp:
57 	case dml2_pstate_method_fw_svp_drr:
58 	case dml2_pstate_method_reserved_hw:
59 	case dml2_pstate_method_reserved_fw:
60 	case dml2_pstate_method_reserved_fw_drr_clamped:
61 	case dml2_pstate_method_reserved_fw_drr_var:
62 	case dml2_pstate_method_count:
63 	case dml2_pstate_method_na:
64 	default:
65 		override_strategy = dml2_uclk_pstate_change_strategy_auto;
66 	}
67 
68 	return override_strategy;
69 }
70 
all_planes_match_method(const struct dml2_display_cfg * display_cfg,int plane_mask,enum dml2_pstate_method method)71 static bool all_planes_match_method(const struct dml2_display_cfg *display_cfg, int plane_mask, enum dml2_pstate_method method)
72 {
73 	unsigned char i;
74 
75 	for (i = 0; i < DML2_MAX_PLANES; i++) {
76 		if (is_bit_set_in_bitfield(plane_mask, i)) {
77 			if (display_cfg->plane_descriptors[i].overrides.uclk_pstate_change_strategy != dml2_uclk_pstate_change_strategy_auto &&
78 				display_cfg->plane_descriptors[i].overrides.uclk_pstate_change_strategy != pstate_method_to_uclk_pstate_strategy_override(method))
79 				return false;
80 		}
81 	}
82 
83 	return true;
84 }
85 
get_per_method_common_meta(const struct dml2_pstate_meta * stream_pstate_meta,enum dml2_pstate_method stream_pstate_method,int stream_idx)86 static const struct dml2_pstate_per_method_common_meta *get_per_method_common_meta(
87 	const struct dml2_pstate_meta *stream_pstate_meta,
88 	enum dml2_pstate_method stream_pstate_method,
89 	int stream_idx)
90 {
91 	const struct dml2_pstate_per_method_common_meta *stream_method_pstate_meta = NULL;
92 
93 	switch (stream_pstate_method) {
94 	case dml2_pstate_method_vactive:
95 	case dml2_pstate_method_fw_vactive_drr:
96 		stream_method_pstate_meta = &stream_pstate_meta[stream_idx].method_vactive.common;
97 		break;
98 	case dml2_pstate_method_vblank:
99 	case dml2_pstate_method_fw_vblank_drr:
100 		stream_method_pstate_meta = &stream_pstate_meta[stream_idx].method_vblank.common;
101 		break;
102 	case dml2_pstate_method_fw_drr:
103 		stream_method_pstate_meta = &stream_pstate_meta[stream_idx].method_drr.common;
104 		break;
105 	case dml2_pstate_method_alternate:
106 		stream_method_pstate_meta = &stream_pstate_meta[stream_idx].method_alternate.common;
107 		break;
108 	case dml2_pstate_method_fw_svp:
109 	case dml2_pstate_method_fw_svp_drr:
110 	case dml2_pstate_method_reserved_hw:
111 	case dml2_pstate_method_reserved_fw:
112 	case dml2_pstate_method_reserved_fw_drr_clamped:
113 	case dml2_pstate_method_reserved_fw_drr_var:
114 	case dml2_pstate_method_count:
115 	case dml2_pstate_method_na:
116 	default:
117 		stream_method_pstate_meta = NULL;
118 	}
119 
120 	return stream_method_pstate_meta;
121 }
122 
dcn6_get_params_for_pstate_type(const struct dml2_pmo_instance * pmo,const struct dml2_optimization_worksheet * worksheet,enum dml2_pstate_type pstate_type,double * allow_delay_us,double * blackout_us,double * watermark_us)123 static void dcn6_get_params_for_pstate_type(const struct dml2_pmo_instance *pmo,
124 		const struct dml2_optimization_worksheet *worksheet,
125 		enum dml2_pstate_type pstate_type,
126 		double *allow_delay_us,
127 		double *blackout_us,
128 		double *watermark_us)
129 {
130 	switch (pstate_type) {
131 	case dml2_pstate_type_uclk:
132 		*allow_delay_us = (double)pmo->ip_caps->fams2.max_allow_delay_us;
133 		*blackout_us = pmo->utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us;
134 		*watermark_us = worksheet->validation_result.mode_support.global.watermarks.DRAMClockChangeWatermark;
135 		break;
136 	case dml2_pstate_type_fclk:
137 		*allow_delay_us = (double)pmo->ip_caps->fams2.max_allow_delay_us; /* TODO placeholder */
138 		*blackout_us = pmo->utm_soc_bb->power_management_parameters.fclk_change_blackout_us;
139 		*watermark_us = worksheet->validation_result.mode_support.global.watermarks.FCLKChangeWatermark;
140 		break;
141 	case dml2_pstate_type_ppt:
142 		*allow_delay_us = (double)pmo->ip_caps->ppt_max_allow_delay_us;
143 		*blackout_us = math_max2(
144 				pmo->utm_soc_bb->power_management_parameters.g7_ppt_blackout_us,
145 				pmo->utm_soc_bb->power_management_parameters.g7_temperature_read_blackout_us);
146 		*watermark_us = worksheet->validation_result.mode_support.global.watermarks.temp_read_or_ppt_watermark_us;
147 		break;
148 	case dml2_pstate_type_temp_read:
149 	case dml2_pstate_type_dummy_pstate:
150 		*allow_delay_us = (double)pmo->ip_caps->temp_read_max_allow_delay_us;
151 		*blackout_us = math_max2(
152 				pmo->utm_soc_bb->power_management_parameters.g7_ppt_blackout_us,
153 				pmo->utm_soc_bb->power_management_parameters.g7_temperature_read_blackout_us);
154 		*watermark_us = worksheet->validation_result.mode_support.global.watermarks.temp_read_or_ppt_watermark_us;
155 		break;
156 	case dml2_pstate_type_count:
157 	default:
158 		*allow_delay_us = 0.0;
159 		*blackout_us = 0.0;
160 		*watermark_us = 0.0;
161 		break;
162 	}
163 }
164 
dcn6_is_timing_group_schedulable(const struct dml2_pstate_meta * stream_pstate_meta,const struct dml2_display_cfg * display_cfg,const enum dml2_pstate_method * per_stream_pstate_method,const unsigned int timing_group_idx,const double max_allow_delay_us,struct dml2_pstate_per_method_common_meta * group_pstate_meta,struct dml2_pmo_synchronized_timing_groups * s)165 static bool dcn6_is_timing_group_schedulable(
166 	const struct dml2_pstate_meta *stream_pstate_meta,
167 	const struct dml2_display_cfg *display_cfg,
168 	const enum dml2_pstate_method *per_stream_pstate_method,
169 	const unsigned int timing_group_idx,
170 	const double max_allow_delay_us,
171 	struct dml2_pstate_per_method_common_meta *group_pstate_meta,
172 	struct dml2_pmo_synchronized_timing_groups *s)
173 {
174 	unsigned int i;
175 	const struct dml2_pstate_per_method_common_meta *stream_method_pstate_meta;
176 	unsigned int base_stream_idx = 0;
177 
178 	/* find base stream idx */
179 	for (base_stream_idx = 0; base_stream_idx < display_cfg->num_streams; base_stream_idx++) {
180 		if (is_bit_set_in_bitfield(s->synchronized_timing_group_masks[timing_group_idx], base_stream_idx)) {
181 			/* master stream found */
182 			break;
183 		}
184 	}
185 
186 	/* init allow start and end lines for timing group */
187 	stream_method_pstate_meta = get_per_method_common_meta(stream_pstate_meta, per_stream_pstate_method[base_stream_idx], base_stream_idx);
188 	if (!stream_method_pstate_meta)
189 		return false;
190 
191 	group_pstate_meta->allow_start_otg_vline = stream_method_pstate_meta->allow_start_otg_vline;
192 	group_pstate_meta->allow_end_otg_vline = stream_method_pstate_meta->allow_end_otg_vline;
193 	group_pstate_meta->period_us = stream_method_pstate_meta->period_us;
194 	for (i = base_stream_idx + 1; i < display_cfg->num_streams; i++) {
195 		if (is_bit_set_in_bitfield(s->synchronized_timing_group_masks[timing_group_idx], i)) {
196 			stream_method_pstate_meta = get_per_method_common_meta(stream_pstate_meta, per_stream_pstate_method[i], i);
197 			if (!stream_method_pstate_meta)
198 				continue;
199 
200 			if (group_pstate_meta->allow_start_otg_vline < stream_method_pstate_meta->allow_start_otg_vline) {
201 				/* set group allow start to larger otg vline */
202 				group_pstate_meta->allow_start_otg_vline = stream_method_pstate_meta->allow_start_otg_vline;
203 			}
204 
205 			if (group_pstate_meta->allow_end_otg_vline > stream_method_pstate_meta->allow_end_otg_vline) {
206 				/* set group allow end to smaller otg vline */
207 				group_pstate_meta->allow_end_otg_vline = stream_method_pstate_meta->allow_end_otg_vline;
208 			}
209 
210 			/* check waveform still has positive width */
211 			if (group_pstate_meta->allow_start_otg_vline >= group_pstate_meta->allow_end_otg_vline) {
212 				/* timing group is not schedulable */
213 				return false;
214 			}
215 		}
216 	}
217 
218 	/* calculate the rest of the meta */
219 	dcn5_build_method_scheduling_params(group_pstate_meta, &stream_pstate_meta[base_stream_idx]);
220 
221 	return group_pstate_meta->allow_time_us > 0.0 &&
222 		group_pstate_meta->disallow_time_us <= max_allow_delay_us;
223 }
224 
dcn6_is_pstate_schedulable(struct dml2_pstate_meta * stream_pstate_meta,const struct dml2_display_cfg * display_cfg,const enum dml2_pstate_method * per_stream_pstate_method,const double max_allow_delay_us,struct dml2_pmo_synchronized_timing_groups * synchronized_timing_groups,struct dml2_scheduling_check_locals * s)225 static bool dcn6_is_pstate_schedulable(
226 	struct dml2_pstate_meta *stream_pstate_meta,
227 	const struct dml2_display_cfg *display_cfg,
228 	const enum dml2_pstate_method *per_stream_pstate_method,
229 	const double max_allow_delay_us,
230 	struct dml2_pmo_synchronized_timing_groups *synchronized_timing_groups,
231 	struct dml2_scheduling_check_locals *s)
232 {
233 	double max_disallow_time_us = 0.0;
234 	unsigned int i, j;
235 	bool schedulable;
236 
237 	memset(s->group_common_pstate_meta, 0, sizeof(s->group_common_pstate_meta));
238 	memset(s->sorted_group_gtl_disallow_index, 0, sizeof(unsigned int) * DML2_MAX_PLANES);
239 
240 	/* search for a general solution to the schedule */
241 
242 	/* STAGE 0: Early return for special cases */
243 	if (display_cfg->num_streams == 0) {
244 		return true;
245 	}
246 
247 	/* STAGE 1: confirm allow waves overlap for synchronizable streams */
248 	schedulable = true;
249 	for (i = 0; i < synchronized_timing_groups->num_timing_groups; i++) {
250 		s->sorted_group_gtl_disallow_index[i] = i;
251 		s->sorted_group_gtl_period_index[i] = i;
252 		if (!dcn6_is_timing_group_schedulable(stream_pstate_meta,
253 					display_cfg,
254 					per_stream_pstate_method,
255 					i,
256 					max_allow_delay_us,
257 					&s->group_common_pstate_meta[i],
258 					synchronized_timing_groups)) {
259 			/* synchronized timing group was not schedulable */
260 			schedulable = false;
261 			break;
262 		}
263 		max_disallow_time_us += s->group_common_pstate_meta[i].disallow_time_us;
264 	}
265 
266 	if ((schedulable && synchronized_timing_groups->num_timing_groups <= 1) || !schedulable) {
267 		/* 1. the only timing group was schedulable, so early pass
268 		 * 2. one of the timing groups was not schedulable, so early fail */
269 		return schedulable;
270 	}
271 
272 	/* STAGE 2: Check allow can't be masked entirely by other disallows */
273 	schedulable = true;
274 
275 	/* sort disallow times from greatest to least */
276 	for (i = 0; i < synchronized_timing_groups->num_timing_groups; i++) {
277 		bool swapped = false;
278 
279 		for (j = 0; j < synchronized_timing_groups->num_timing_groups - 1; j++) {
280 			double j_disallow_us              = s->group_common_pstate_meta[s->sorted_group_gtl_disallow_index[j]].disallow_time_us;
281 			double jp1_disallow_us            = s->group_common_pstate_meta[s->sorted_group_gtl_disallow_index[j + 1]].disallow_time_us;
282 			if (j_disallow_us < jp1_disallow_us) {
283 				/* swap as A < B */
284 				swap(s->sorted_group_gtl_disallow_index[j],
285 					 s->sorted_group_gtl_disallow_index[j+1]);
286 				swapped = true;
287 			}
288 		}
289 
290 		/* sorted, exit early */
291 		if (!swapped)
292 			break;
293 	}
294 
295 	/* Check worst case disallow region occurs in the middle of allow for the
296 	* other display, or when >2 streams continue to halve the remaining allow time.
297 	*/
298 	for (i = 0; i < synchronized_timing_groups->num_timing_groups; i++) {
299 		if (s->group_common_pstate_meta[i].disallow_time_us <= 0.0) {
300 			/* this timing group always allows */
301 			continue;
302 		}
303 
304 		double max_allow_time_us = s->group_common_pstate_meta[i].allow_time_us;
305 		for (j = 0; j < synchronized_timing_groups->num_timing_groups; j++) {
306 			unsigned int sorted_j = s->sorted_group_gtl_disallow_index[j];
307 			/* stream can't overlap itself */
308 			if (i != sorted_j && s->group_common_pstate_meta[sorted_j].disallow_time_us > 0.0) {
309 				double unmasked_allow_time_us = (max_allow_time_us - s->group_common_pstate_meta[sorted_j].disallow_time_us) / 2;
310 
311 				max_allow_time_us = math_min2(
312 					s->group_common_pstate_meta[sorted_j].allow_time_us,
313 					unmasked_allow_time_us);
314 
315 				if (max_allow_time_us < 0.0) {
316 					/* failed exit early */
317 					break;
318 				}
319 			}
320 		}
321 
322 		if (max_allow_time_us <= 0.0) {
323 			/* not enough time for microschedule in the worst case */
324 			schedulable = false;
325 			break;
326 		}
327 	}
328 
329 	if (schedulable && max_disallow_time_us < max_allow_delay_us) {
330 		return true;
331 	}
332 
333 	/* STAGE 3: check larger allow can fit period of all other streams */
334 	schedulable = true;
335 
336 	/* sort periods from greatest to least */
337 	for (i = 0; i < synchronized_timing_groups->num_timing_groups; i++) {
338 		bool swapped = false;
339 
340 		for (j = 0; j < synchronized_timing_groups->num_timing_groups - 1; j++) {
341 			double j_period_us                 = s->group_common_pstate_meta[s->sorted_group_gtl_period_index[j]].period_us;
342 			double jp1_period_us               = s->group_common_pstate_meta[s->sorted_group_gtl_period_index[j + 1]].period_us;
343 			if (j_period_us < jp1_period_us) {
344 				/* swap as A < B */
345 				swap(s->sorted_group_gtl_period_index[j],
346 					 s->sorted_group_gtl_period_index[j + 1]);
347 				swapped = true;
348 			}
349 		}
350 
351 		/* sorted, exit early */
352 		if (!swapped)
353 			break;
354 	}
355 
356 	/* check larger allow can fit period of all other streams */
357 	for (i = 0; i < synchronized_timing_groups->num_timing_groups - 1; i++) {
358 		unsigned int sorted_i = s->sorted_group_gtl_period_index[i];
359 		unsigned int sorted_ip1 = s->sorted_group_gtl_period_index[i + 1];
360 
361 		if (s->group_common_pstate_meta[sorted_i].allow_time_us < s->group_common_pstate_meta[sorted_ip1].period_us ||
362 			(synchronized_timing_groups->group_is_drr_enabled[sorted_ip1] && synchronized_timing_groups->group_is_drr_active[sorted_ip1])) {
363 			schedulable = false;
364 			break;
365 		}
366 	}
367 
368 	if (schedulable && max_disallow_time_us < max_allow_delay_us) {
369 		return true;
370 	}
371 
372 	/* STAGE 4: When using HW exclusive modes, check disallow alignments are within allowed threshold */
373 	if (synchronized_timing_groups->num_timing_groups == 2 &&
374 		!is_bit_set_in_bitfield(PMO_FW_STRATEGY_MASK, per_stream_pstate_method[0]) &&
375 		!is_bit_set_in_bitfield(PMO_FW_STRATEGY_MASK, per_stream_pstate_method[1])) {
376 		double sum_allow_time_us;
377 		double shift_per_period;
378 		double period_ratio;
379 		double max_shift_us;
380 
381 		/* default period_0 > period_1 */
382 		unsigned int lrg_idx = 0;
383 		unsigned int sml_idx = 1;
384 		if (s->group_common_pstate_meta[0].period_us < s->group_common_pstate_meta[1].period_us) {
385 			/* period_0 < period_1 */
386 			lrg_idx = 1;
387 			sml_idx = 0;
388 		}
389 		period_ratio = s->group_common_pstate_meta[lrg_idx].period_us / s->group_common_pstate_meta[sml_idx].period_us;
390 		shift_per_period = s->group_common_pstate_meta[sml_idx].period_us * (period_ratio - math_floor(period_ratio));
391 		max_shift_us = s->group_common_pstate_meta[lrg_idx].disallow_time_us - s->group_common_pstate_meta[sml_idx].allow_time_us;
392 		max_disallow_time_us = max_shift_us / shift_per_period * s->group_common_pstate_meta[lrg_idx].period_us;
393 		sum_allow_time_us = s->group_common_pstate_meta[lrg_idx].allow_time_us + s->group_common_pstate_meta[sml_idx].allow_time_us;
394 
395 		if (shift_per_period > 0.0 &&
396 			shift_per_period < sum_allow_time_us &&
397 			max_disallow_time_us < max_allow_delay_us) {
398 			schedulable = true;
399 		}
400 	}
401 
402 	return schedulable;
403 }
404 
dcn6_update_worksheet_for_pstate_admissibility(struct dml2_optimization_worksheet * worksheet,struct dml2_pstate_meta * per_stream_pstate_meta,enum dml2_pstate_type pstate_type)405 static bool dcn6_update_worksheet_for_pstate_admissibility(struct dml2_optimization_worksheet *worksheet,
406 		struct dml2_pstate_meta *per_stream_pstate_meta,
407 		enum dml2_pstate_type pstate_type)
408 {
409 	const double vblank_ratio = 0.8;
410 	unsigned int plane_index, stream_index;
411 	double ideal_relative_disallow, ideal_disallow_time_us, disallow_time_us;
412 	double extra_time_required_us;
413 	double vblank_time_us;
414 	double min_unreserved_vblank_time_us;
415 	double min_det_fill_delay_us;
416 	double delta_max_det_fill_delay_us;
417 	double delta_reserved_vblank_time_us;
418 
419 	int max_vactive_det_fill_delay_us;
420 	long reserved_vblank_time_ns;
421 	bool unvalidated_changes = false;
422 
423 	const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg;
424 	/* Two possible locations to obtain the extra time required to pass admissibility
425 	 *   1. Allocate some time from the reserved vblank
426 	 *   2. Use some of the time reserved for the vactive det fill delay
427 	 * For now statically favour getting most of the required time from the reserved vblank since
428 	 * configs failing pstate admissibility tend to have a large vblank to vactive ratio.
429 	 */
430 
431 	ideal_relative_disallow = 1.0;
432 	if (display_config->num_streams > 1) {
433 		/* The disallow region should be less than (0.5) ^ (number of displays - 1) * total frame time
434 		* Calculation assumes identical displays / timings when performing the scheduling check
435 		* Make the relative disallow 5% smaller than the ideal case to add extra margin
436 		*/
437 		ideal_relative_disallow = (double)math_pow(0.5, (float)(display_config->num_streams - 1)) * 0.95;
438 	}
439 
440 	for (stream_index = 0; stream_index < display_config->num_streams; stream_index++) {
441 		disallow_time_us = per_stream_pstate_meta[stream_index].method_vactive.common.disallow_time_us;
442 		ideal_disallow_time_us = per_stream_pstate_meta[stream_index].method_vactive.common.period_us * ideal_relative_disallow;
443 
444 		extra_time_required_us = 0.0;
445 		delta_max_det_fill_delay_us = 0.0;
446 		delta_reserved_vblank_time_us = 0.0;
447 		if (disallow_time_us > ideal_disallow_time_us) {
448 			/* minimum unreserved 15% of blank, or 50us left over for prefetch */
449 			vblank_time_us = display_config->stream_descriptors[stream_index].timing.vblank_nom *
450 					per_stream_pstate_meta[stream_index].otg_vline_time_us;
451 			min_unreserved_vblank_time_us = math_min2(vblank_time_us * 0.15, 50);
452 
453 			/* minmum of blackout time (~2x VActive bandwidth) */
454 			min_det_fill_delay_us = per_stream_pstate_meta[stream_index].blackout_otg_vlines *
455 					per_stream_pstate_meta[stream_index].otg_vline_time_us;
456 
457 			extra_time_required_us = disallow_time_us - ideal_disallow_time_us;
458 
459 			/* try compressing VActive fill first */
460 			delta_max_det_fill_delay_us = math_min2(extra_time_required_us * (1.0 - vblank_ratio),
461 					math_max2(0.0,
462 					per_stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us - min_det_fill_delay_us));
463 			delta_max_det_fill_delay_us = math_floor2(delta_max_det_fill_delay_us, per_stream_pstate_meta[stream_index].otg_vline_time_us);
464 			extra_time_required_us -= delta_max_det_fill_delay_us;
465 
466 			/* try reserving VBlank */
467 			delta_reserved_vblank_time_us = math_min2(extra_time_required_us,
468 					math_max2(0.0,
469 					vblank_time_us - per_stream_pstate_meta[stream_index].method_vactive.reserved_vblank_required_us - min_unreserved_vblank_time_us));
470 			delta_reserved_vblank_time_us = math_floor2(delta_reserved_vblank_time_us, per_stream_pstate_meta[stream_index].otg_vline_time_us);
471 			extra_time_required_us -= delta_reserved_vblank_time_us;
472 
473 			if (extra_time_required_us > 0.0 &&
474 					per_stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us > math_ceil2(delta_max_det_fill_delay_us + extra_time_required_us, per_stream_pstate_meta[stream_index].otg_vline_time_us)) {
475 				/* final attempt to compress fill time */
476 				delta_max_det_fill_delay_us = delta_max_det_fill_delay_us + extra_time_required_us;
477 				delta_max_det_fill_delay_us = math_ceil2(delta_max_det_fill_delay_us, per_stream_pstate_meta[stream_index].otg_vline_time_us);
478 				extra_time_required_us = 0.0;
479 			}
480 		}
481 
482 		/* Update the worksheet */
483 		for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) {
484 			if (display_config->plane_descriptors[plane_index].stream_index != stream_index) {
485 				continue;
486 			}
487 
488 			max_vactive_det_fill_delay_us = 0;
489 			if (worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][pstate_type] > 0.0) {
490 				max_vactive_det_fill_delay_us = (int)math_floor(math_min2(
491 						(double)worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][pstate_type],
492 						per_stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us - delta_max_det_fill_delay_us));
493 			} else {
494 				max_vactive_det_fill_delay_us = (int)math_floor(
495 						per_stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us - delta_max_det_fill_delay_us);
496 			}
497 
498 			reserved_vblank_time_ns = (long)math_max2(
499 					(double)worksheet->cur.config.reserved_vblank_time_ns[plane_index],
500 					(per_stream_pstate_meta[stream_index].method_vactive.reserved_vblank_required_us + delta_reserved_vblank_time_us) * 1000.0);
501 
502 			if ((max_vactive_det_fill_delay_us > 0 && worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][pstate_type] == 0) ||
503 					max_vactive_det_fill_delay_us < worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][pstate_type] ||
504 					(reserved_vblank_time_ns > 0 && worksheet->cur.config.reserved_vblank_time_ns[plane_index] == 0) ||
505 					reserved_vblank_time_ns > worksheet->cur.config.reserved_vblank_time_ns[plane_index]) {
506 				/* only modify the worksheet if required */
507 				worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][pstate_type] = max_vactive_det_fill_delay_us;
508 				worksheet->cur.config.reserved_vblank_time_ns[plane_index] = reserved_vblank_time_ns;
509 
510 				worksheet->cur.unvalidated_change.bits.reserved_vblank_time = true;
511 				unvalidated_changes = true;
512 			}
513 			DML_LOG_DEBUG("worksheet->cur.config.reserved_vblank_time_ns[%d] = %lu\n", plane_index, worksheet->cur.config.reserved_vblank_time_ns[plane_index]);
514 			DML_LOG_DEBUG("worksheet->cur.config.max_vactive_det_fill_delay_us[%d] = %u\n", plane_index, worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][pstate_type]);
515 		}
516 	}
517 
518 	if (!worksheet->cur.config.fclk_pstate_support
519 			|| !worksheet->cur.config.ppt_temp_read_support) {
520 		worksheet->cur.config.fclk_pstate_support = true;
521 		worksheet->cur.config.ppt_temp_read_support = true;
522 		worksheet->cur.unvalidated_change.bits.uclk_pstate_method = true;
523 		unvalidated_changes = true;
524 	}
525 
526 	return unvalidated_changes;
527 }
528 
dcn6_get_vactive_latency_hiding(const struct dml2_validation_result * validation_res,int plane_mask)529 static int dcn6_get_vactive_latency_hiding(const struct dml2_validation_result *validation_res, int plane_mask)
530 {
531 	unsigned char i;
532 	int min_vactive_latency_hiding_us = 0xFFFFFFF;
533 
534 	if (!validation_res->is_mode_support_valid)
535 		return min_vactive_latency_hiding_us;
536 
537 	for (i = 0; i < DML2_MAX_PLANES; i++) {
538 		if (is_bit_set_in_bitfield(plane_mask, i)) {
539 			if (validation_res->mode_support.cfg_support_info.plane_support_info[i].active_latency_hiding_us < min_vactive_latency_hiding_us)
540 				min_vactive_latency_hiding_us = validation_res->mode_support.cfg_support_info.plane_support_info[i].active_latency_hiding_us;
541 		}
542 	}
543 
544 	return min_vactive_latency_hiding_us;
545 }
546 
dcn6_get_vactive_det_fill_delay_us(const struct dml2_validation_result * validation_res,enum dml2_pstate_type pstate_type,int plane_mask)547 static int dcn6_get_vactive_det_fill_delay_us(
548 		const struct dml2_validation_result *validation_res,
549 		enum dml2_pstate_type pstate_type,
550 		int plane_mask)
551 {
552 	unsigned int i;
553 	int max_vactive_det_fill_delay_us = 0;
554 
555 	for (i = 0; i < DML2_MAX_PLANES; i++) {
556 		if (is_bit_set_in_bitfield(plane_mask, i)) {
557 			if (validation_res->mode_support.cfg_support_info.plane_support_info[i].vactive_det_fill_delay_us[pstate_type] > max_vactive_det_fill_delay_us)
558 				max_vactive_det_fill_delay_us = validation_res->mode_support.cfg_support_info.plane_support_info[i].vactive_det_fill_delay_us[pstate_type];
559 		}
560 	}
561 
562 	return max_vactive_det_fill_delay_us;
563 }
564 
dcn6_get_required_vactive_det_fill_delay_us(const struct dml2_optimization_worksheet * worksheet,enum dml2_pstate_type pstate_type,int plane_mask)565 static int dcn6_get_required_vactive_det_fill_delay_us(
566 	const struct dml2_optimization_worksheet *worksheet,
567 	enum dml2_pstate_type pstate_type,
568 	int plane_mask)
569 {
570 	unsigned int i;
571 	int max_vactive_det_fill_delay_us = 0;
572 
573 	for (i = 0; i < DML2_MAX_PLANES; i++) {
574 		if (is_bit_set_in_bitfield(plane_mask, i)) {
575 			if (worksheet->cur.config.max_vactive_det_fill_delay_us[i][pstate_type] > max_vactive_det_fill_delay_us)
576 				max_vactive_det_fill_delay_us = worksheet->cur.config.max_vactive_det_fill_delay_us[i][pstate_type];
577 		}
578 	}
579 
580 	return max_vactive_det_fill_delay_us;
581 }
582 
dcn6_all_timings_support_vactive(struct dml2_pmo_stage_optimizer * stage,const struct dml2_display_cfg * display_config,unsigned int mask)583 static bool dcn6_all_timings_support_vactive(struct dml2_pmo_stage_optimizer *stage,
584 		const struct dml2_display_cfg *display_config,
585 		unsigned int mask)
586 {
587 	struct dml2_stage_optimizer_uclk_pstate_init_locals *s = &stage->func_locals->uclk_pstate_init;
588 	unsigned int i;
589 	bool valid = true;
590 
591 	// Create a remap array to enable simple iteration through only masked stream indicies
592 	for (i = 0; i < display_config->num_streams; i++) {
593 		if (is_bit_set_in_bitfield(mask, i)) {
594 			/* check if stream has enough vactive margin, or single display in case blank can also be used */
595 			valid &= is_bit_set_in_bitfield(s->stream_vactive_capability_mask, i) ||
596 					display_config->num_streams == 1;
597 		}
598 	}
599 
600 	return valid;
601 }
602 
validate_pstate_support_strategy_cofunctionality(struct dml2_pmo_stage_optimizer * stage,struct dml2_optimization_worksheet * worksheet,const struct dml2_display_cfg * display_cfg,const struct dml2_pmo_pstate_strategy * pstate_strategy)603 static bool validate_pstate_support_strategy_cofunctionality(struct dml2_pmo_stage_optimizer *stage,
604 		struct dml2_optimization_worksheet *worksheet,
605 		const struct dml2_display_cfg *display_cfg,
606 		const struct dml2_pmo_pstate_strategy *pstate_strategy)
607 {
608 	const struct dml2_pmo_instance *pmo = stage->pmo;
609 
610 	unsigned int stream_index = 0;
611 
612 	unsigned int drr_count = 0;
613 	unsigned int drr_stream_mask = 0;
614 	unsigned int vactive_count = 0;
615 	unsigned int vactive_stream_mask = 0;
616 	unsigned int vblank_count = 0;
617 	unsigned int vblank_stream_mask = 0;
618 	unsigned int alternate_count = 0;
619 	unsigned int alternate_stream_mask = 0;
620 
621 	bool strategy_matches_forced_requirements = true;
622 	bool strategy_matches_drr_requirements = true;
623 
624 	// Tabulate everything
625 	for (stream_index = 0; stream_index < display_cfg->num_streams; stream_index++) {
626 
627 		if (!all_planes_match_method(display_cfg, worksheet->uclk_pstate.stream_plane_mask[stream_index],
628 			pstate_strategy->per_stream_pstate_method[stream_index])) {
629 			strategy_matches_forced_requirements = false;
630 			break;
631 		}
632 
633 		strategy_matches_drr_requirements &=
634 			dcn5_stream_matches_drr_policy(stage, display_cfg, pstate_strategy->per_stream_pstate_method[stream_index], stream_index);
635 
636 		if (pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_drr) {
637 			drr_count++;
638 			set_bit_in_bitfield(&drr_stream_mask, stream_index);
639 		} else if (pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pstate_method_vactive ||
640 			pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vactive_drr) {
641 			vactive_count++;
642 			set_bit_in_bitfield(&vactive_stream_mask, stream_index);
643 		} else if (pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pstate_method_vblank ||
644 			pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vblank_drr) {
645 			vblank_count++;
646 			set_bit_in_bitfield(&vblank_stream_mask, stream_index);
647 		} else if (pstate_strategy->per_stream_pstate_method[stream_index] == dml2_pstate_method_alternate) {
648 			alternate_count++;
649 			set_bit_in_bitfield(&alternate_stream_mask, stream_index);
650 		}
651 	}
652 
653 	if (!strategy_matches_forced_requirements || !strategy_matches_drr_requirements)
654 		return false;
655 
656 	if (vactive_count > 0 && !dcn6_all_timings_support_vactive(stage, display_cfg, vactive_stream_mask))
657 		return false;
658 
659 	if (vblank_count > 0 && (pmo->options->disable_vblank || !dcn5_all_timings_support_vblank(stage, display_cfg, vblank_stream_mask)))
660 		return false;
661 
662 	if (drr_count > 0 && (pmo->options->disable_drr_var || !dcn5_all_timings_support_drr(stage, worksheet, display_cfg, drr_stream_mask)))
663 		return false;
664 
665 	if (alternate_count > 0 && pmo->options->disable_alternate_memory_training)
666 		return false;
667 
668 	return dcn6_is_pstate_schedulable(
669 		worksheet->uclk_pstate.stream_pstate_meta,
670 		display_cfg,
671 		pstate_strategy->per_stream_pstate_method,
672 		stage->func_locals->uclk_pstate_init.allow_delay_us,
673 		&stage->func_locals->uclk_pstate_init.synchronized_timing_groups,
674 		&stage->func_locals->uclk_pstate_init.scheduling_check_locals);
675 }
676 
dcn6_build_pstate_meta_per_stream(const struct dml2_display_cfg * display_cfg,const struct dml2_ip_capabilities * ip_caps,const struct dml2_optimization_worksheet * worksheet,enum dml2_pstate_type pstate_type,double watermark_us,double blackout_us,double max_allow_delay_us,int stream_index,unsigned int stream_plane_mask,struct dml2_pstate_meta * stream_pstate_meta)677 static void dcn6_build_pstate_meta_per_stream(const struct dml2_display_cfg *display_cfg,
678 	const struct dml2_ip_capabilities *ip_caps,
679 	const struct dml2_optimization_worksheet *worksheet,
680 	enum dml2_pstate_type pstate_type,
681 	double watermark_us,
682 	double blackout_us,
683 	double max_allow_delay_us,
684 	int stream_index,
685 	unsigned int stream_plane_mask,
686 	/* output */
687 	struct dml2_pstate_meta *stream_pstate_meta)
688 {
689 	const struct dml2_stream_parameters *stream_descriptor = &display_cfg->stream_descriptors[stream_index];
690 	const struct dml2_timing_cfg *timing                   = &stream_descriptor->timing;
691 
692 	int max_det_fill_delay_otg_vlines;
693 	int min_reserved_blank_otg_vlines;
694 
695 	/* worst case all other streams require some programming at the same time, 0 if only 1 stream */
696 	double contention_delay_us = ((double)ip_caps->fams2.vertical_interrupt_ack_delay_us +
697 		math_max2(ip_caps->fams2.drr_programming_delay_us, ip_caps->fams2.allow_programming_delay_us)) *
698 		(display_cfg->num_streams - 1);
699 
700 	/* common */
701 	stream_pstate_meta->valid               = true;
702 	stream_pstate_meta->nom_vtotal          = stream_descriptor->timing.vblank_nom + stream_descriptor->timing.v_active;
703 	stream_pstate_meta->otg_vline_time_us   = (double)timing->h_total / timing->pixel_clock_khz * 1000.0;
704 	stream_pstate_meta->vblank_start        = timing->v_blank_end + timing->v_active;
705 	stream_pstate_meta->nom_refresh_rate_hz = timing->pixel_clock_khz * 1000.0 /
706 		(stream_pstate_meta->nom_vtotal * timing->h_total);
707 	stream_pstate_meta->nom_frame_time_us   =
708 		(double)stream_pstate_meta->nom_vtotal * stream_pstate_meta->otg_vline_time_us;
709 
710 	if (stream_descriptor->timing.drr_config.enabled == true) {
711 		if (stream_descriptor->timing.drr_config.min_refresh_uhz != 0.0) {
712 			stream_pstate_meta->max_vtotal = (int)math_floor((double)stream_descriptor->timing.pixel_clock_khz /
713 				((double)stream_descriptor->timing.drr_config.min_refresh_uhz * stream_descriptor->timing.h_total) * 1e9);
714 		} else {
715 			/* assume min of 48Hz */
716 			stream_pstate_meta->max_vtotal = (int)math_floor((double)stream_descriptor->timing.pixel_clock_khz /
717 				(48000000.0 * stream_descriptor->timing.h_total) * 1e9);
718 		}
719 	} else {
720 		stream_pstate_meta->max_vtotal = stream_pstate_meta->nom_vtotal;
721 	}
722 	stream_pstate_meta->min_refresh_rate_hz = timing->pixel_clock_khz * 1000.0 /
723 		(stream_pstate_meta->max_vtotal * timing->h_total);
724 	stream_pstate_meta->max_frame_time_us   =
725 		(double)stream_pstate_meta->max_vtotal * stream_pstate_meta->otg_vline_time_us;
726 
727 	stream_pstate_meta->scheduling_delay_otg_vlines =
728 		(int)math_ceil(ip_caps->fams2.scheduling_delay_us / stream_pstate_meta->otg_vline_time_us);
729 	stream_pstate_meta->vertical_interrupt_ack_delay_otg_vlines =
730 		(int)math_ceil(ip_caps->fams2.vertical_interrupt_ack_delay_us / stream_pstate_meta->otg_vline_time_us);
731 	stream_pstate_meta->contention_delay_otg_vlines =
732 		(int)math_ceil(contention_delay_us / stream_pstate_meta->otg_vline_time_us);
733 	/* worst case allow to target needs to account for all streams' allow events overlapping, and 1 line for error */
734 	stream_pstate_meta->allow_to_target_delay_otg_vlines =
735 		(int)(math_ceil((ip_caps->fams2.vertical_interrupt_ack_delay_us + contention_delay_us + ip_caps->fams2.allow_programming_delay_us) / stream_pstate_meta->otg_vline_time_us)) + 1;
736 	stream_pstate_meta->min_allow_width_otg_vlines =
737 		(int)math_ceil(ip_caps->fams2.min_allow_width_us / stream_pstate_meta->otg_vline_time_us);
738 	stream_pstate_meta->blackout_otg_vlines = (int)math_ceil(blackout_us /	stream_pstate_meta->otg_vline_time_us);
739 	stream_pstate_meta->max_allow_delay_otg_vlines = (int)math_floor((double)max_allow_delay_us / stream_pstate_meta->otg_vline_time_us);
740 	if (stream_pstate_meta->max_allow_delay_otg_vlines < 0)
741 		stream_pstate_meta->max_allow_delay_otg_vlines = 0;
742 	max_det_fill_delay_otg_vlines = (int)math_floor(
743 			(double)dcn6_get_required_vactive_det_fill_delay_us(worksheet, pstate_type, stream_plane_mask) /
744 			stream_pstate_meta->otg_vline_time_us);
745 	min_reserved_blank_otg_vlines = (int)math_ceil(
746 			(double)dcn5_get_minimum_reserved_time_us_for_planes(worksheet, stream_plane_mask) /
747 			stream_pstate_meta->otg_vline_time_us);
748 	stream_pstate_meta->nom_vblank_time_us = stream_descriptor->timing.vblank_nom * stream_pstate_meta->otg_vline_time_us;
749 
750 	/* scheduling params should be built based on the worst case for allow_time:disallow_time */
751 
752 	/* vactive */
753 	stream_pstate_meta->method_vactive.vactive_latency_hiding_us =
754 			(double)dcn6_get_vactive_latency_hiding(&worksheet->validation_result, stream_plane_mask);
755 	if (stream_pstate_meta->method_vactive.vactive_latency_hiding_us < watermark_us) {
756 		/* achieve single pulse of allow by utilizing blank */
757 		stream_pstate_meta->method_vactive.reserved_vblank_required_us =
758 				blackout_us -
759 				stream_pstate_meta->method_vactive.vactive_latency_hiding_us;
760 		stream_pstate_meta->method_vactive.reserved_blank_required_vlines = (int)math_max3(
761 				0.0,
762 				math_ceil(stream_pstate_meta->method_vactive.reserved_vblank_required_us /
763 				stream_pstate_meta->otg_vline_time_us),
764 				(double)min_reserved_blank_otg_vlines);
765 	} else {
766 		/* account for already reserved vblank */
767 		stream_pstate_meta->method_vactive.reserved_vblank_required_us =
768 				min_reserved_blank_otg_vlines *
769 				stream_pstate_meta->otg_vline_time_us;
770 		stream_pstate_meta->method_vactive.reserved_blank_required_vlines = min_reserved_blank_otg_vlines;
771 	}
772 
773 	if (display_cfg->num_streams == 1) {
774 		/* for single stream, guarantee at least an instant of allow */
775 		stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = (int)math_floor(
776 				math_max2(0.0,
777 				timing->v_active - math_max2(1.0, stream_pstate_meta->min_allow_width_otg_vlines) -
778 				(stream_pstate_meta->blackout_otg_vlines -
779 				stream_pstate_meta->method_vactive.reserved_blank_required_vlines)));
780 	} else {
781 		/* for multi stream, bound to a max fill time defined by the parameter */
782 		stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines =
783 				(int)math_floor((double)ip_caps->max_vactive_det_fill_delay_us / stream_pstate_meta->otg_vline_time_us);
784 	}
785 
786 	if (max_det_fill_delay_otg_vlines > 0) {
787 		/* consider existing DET fill time enforcement */
788 		if (stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines > 0) {
789 			stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = (int)math_min2(
790 					(double)stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines,
791 					(double)max_det_fill_delay_otg_vlines);
792 		} else {
793 			stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = max_det_fill_delay_otg_vlines;
794 		}
795 	}
796 
797 	/* consider max allow delay enforcement */
798 	if (stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines > 0) {
799 		stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = (int)math_min2(
800 				(double)stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines,
801 				math_max2(0.0,
802 				stream_pstate_meta->max_allow_delay_otg_vlines +
803 				stream_pstate_meta->method_vactive.reserved_blank_required_vlines -
804 				(int)stream_descriptor->timing.vblank_nom -
805 				stream_pstate_meta->blackout_otg_vlines));
806 	} else {
807 		stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = (int)math_max2(0.0,
808 				stream_pstate_meta->max_allow_delay_otg_vlines +
809 				stream_pstate_meta->method_vactive.reserved_blank_required_vlines -
810 				(int)stream_descriptor->timing.vblank_nom -
811 				stream_pstate_meta->blackout_otg_vlines);
812 	}
813 	stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_us =
814 			stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines *
815 			stream_pstate_meta->otg_vline_time_us;
816 
817 	if (stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_us > 0.0) {
818 		stream_pstate_meta->method_vactive.common.allow_start_otg_vline =
819 				timing->v_blank_end + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines;
820 		stream_pstate_meta->method_vactive.common.allow_end_otg_vline =
821 				stream_pstate_meta->vblank_start -
822 				stream_pstate_meta->blackout_otg_vlines +
823 				stream_pstate_meta->method_vactive.reserved_blank_required_vlines;
824 	} else {
825 		stream_pstate_meta->method_vactive.common.allow_start_otg_vline = 0;
826 		stream_pstate_meta->method_vactive.common.allow_end_otg_vline = 0;
827 	}
828 	stream_pstate_meta->method_vactive.common.period_us = stream_pstate_meta->nom_frame_time_us;
829 
830 	/* vblank */
831 	stream_pstate_meta->method_vblank.common.allow_start_otg_vline = stream_pstate_meta->vblank_start;
832 	stream_pstate_meta->method_vblank.common.period_us             = stream_pstate_meta->nom_frame_time_us;
833 	stream_pstate_meta->method_vblank.common.allow_end_otg_vline   =
834 		stream_pstate_meta->method_vblank.common.allow_start_otg_vline + 1;
835 
836 	if (pstate_type == dml2_pstate_type_uclk) {
837 		/* alternate */
838 		stream_pstate_meta->method_alternate.programming_delay_otg_vlines =
839 			(int)math_ceil(ip_caps->fams2.subvp_programming_delay_us / stream_pstate_meta->otg_vline_time_us);
840 		stream_pstate_meta->method_alternate.pmfw_throttle_delay_otg_vlines =
841 			(int)math_ceil(ip_caps->fams2.subvp_df_throttle_delay_us / stream_pstate_meta->otg_vline_time_us);
842 		stream_pstate_meta->method_alternate.common.period_us = stream_pstate_meta->nom_frame_time_us;
843 		stream_pstate_meta->method_alternate.common.allow_start_otg_vline = 0;
844 		stream_pstate_meta->method_alternate.common.allow_end_otg_vline = stream_pstate_meta->nom_vtotal;
845 
846 		/* drr */
847 		stream_pstate_meta->method_drr.common.period_us = stream_pstate_meta->nom_frame_time_us;
848 		stream_pstate_meta->method_drr.programming_delay_otg_vlines =
849 			(int)math_ceil(ip_caps->fams2.drr_programming_delay_us / stream_pstate_meta->otg_vline_time_us);
850 		stream_pstate_meta->method_drr.common.allow_start_otg_vline =
851 			stream_pstate_meta->vblank_start +
852 			stream_pstate_meta->allow_to_target_delay_otg_vlines;
853 
854 		if (display_cfg->num_streams <= 1) {
855 			/* only need to stretch vblank for blackout time */
856 			stream_pstate_meta->method_drr.stretched_vtotal =
857 				stream_pstate_meta->nom_vtotal +
858 				stream_pstate_meta->allow_to_target_delay_otg_vlines +
859 				stream_pstate_meta->min_allow_width_otg_vlines +
860 				stream_pstate_meta->blackout_otg_vlines;
861 		} else {
862 			/* multi display needs to always be schedulable */
863 			stream_pstate_meta->method_drr.stretched_vtotal =
864 				stream_pstate_meta->nom_vtotal * 2 +
865 				stream_pstate_meta->allow_to_target_delay_otg_vlines +
866 				stream_pstate_meta->min_allow_width_otg_vlines +
867 				stream_pstate_meta->blackout_otg_vlines;
868 		}
869 		stream_pstate_meta->method_drr.common.allow_end_otg_vline =
870 			stream_pstate_meta->method_drr.stretched_vtotal -
871 			stream_pstate_meta->blackout_otg_vlines;
872 
873 		dcn5_build_method_scheduling_params(&stream_pstate_meta->method_drr.common, stream_pstate_meta);
874 		dcn5_build_method_scheduling_params(&stream_pstate_meta->method_alternate.common, stream_pstate_meta);
875 	}
876 
877 	dcn5_build_method_scheduling_params(&stream_pstate_meta->method_vactive.common, stream_pstate_meta);
878 	dcn5_build_method_scheduling_params(&stream_pstate_meta->method_vblank.common, stream_pstate_meta);
879 }
880 
881 
dml2_pmo_dcn6_stage_optimizer_uclk_pstate_init(struct dml2_pmo_stage_optimizer * stage,struct dml2_optimization_worksheet * worksheet)882 static void dml2_pmo_dcn6_stage_optimizer_uclk_pstate_init(
883 		struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet)
884 {
885 	const struct dml2_pmo_instance *pmo = stage->pmo;
886 	struct dml2_stage_optimizer_uclk_pstate_init_locals *s = &stage->func_locals->uclk_pstate_init;
887 
888 	const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg;
889 	const struct dml2_plane_parameters *plane_descriptor;
890 	const struct dml2_pmo_pstate_strategy *strategy_list = NULL;
891 	struct dml2_pmo_pstate_strategy override_base_strategy = { 0 };
892 	unsigned int strategy_list_size = 0;
893 	unsigned int plane_index, i;
894 	unsigned int stream_index;
895 	bool build_override_strategy = true;
896 
897 	DML_LOG_COMP_IF_ENTER();
898 	memset(s, 0, sizeof(struct dml2_stage_optimizer_uclk_pstate_init_locals));
899 
900 	if (display_config->overrides.all_streams_blanked) {
901 		goto exit;
902 	}
903 
904 	// First build the stream plane mask (array of bitfields indexed by stream, indicating plane mapping)
905 	for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) {
906 		plane_descriptor = &display_config->plane_descriptors[plane_index];
907 
908 		set_bit_in_bitfield(&worksheet->uclk_pstate.stream_plane_mask[plane_descriptor->stream_index], plane_index);
909 
910 		build_override_strategy &= plane_descriptor->overrides.uclk_pstate_change_strategy != dml2_uclk_pstate_change_strategy_auto;
911 		override_base_strategy.per_stream_pstate_method[plane_descriptor->stream_index] =
912 				uclk_pstate_strategy_override_to_pstate_method(plane_descriptor->overrides.uclk_pstate_change_strategy);
913 
914 		/* Save initial reserved vblank time as pstate optimize may overwrite this value. But
915 		 * if validation or permissibility fails then we must restore to the original value.
916 		 */
917 		worksheet->uclk_pstate.init_reserved_vblank_time_ns[plane_index] = worksheet->cur.config.reserved_vblank_time_ns[plane_index];
918 	}
919 
920 	dcn6_get_params_for_pstate_type(pmo, worksheet, dml2_pstate_type_uclk, &s->allow_delay_us, &s->blackout_us, &s->watermark_us);
921 
922 	// Figure out which streams can do vactive, and also build up implicit FAMS2 meta
923 	for (stream_index = 0; stream_index < display_config->num_streams; stream_index++) {
924 		unsigned int stream_plane_mask = worksheet->uclk_pstate.stream_plane_mask[stream_index];
925 		struct dml2_validation_result *validation_result = &worksheet->validation_result;
926 
927 		if (dcn5_get_vactive_pstate_margin(validation_result, stream_plane_mask) > 0)
928 			set_bit_in_bitfield(&s->stream_vactive_capability_mask, stream_index);
929 
930 		/* pstate meta */
931 		dcn6_build_pstate_meta_per_stream(worksheet->orig_dispcfg,
932 			pmo->ip_caps,
933 			worksheet,
934 			dml2_pstate_type_uclk,
935 			s->watermark_us,
936 			s->blackout_us,
937 			s->allow_delay_us,
938 			stream_index,
939 			stream_plane_mask,
940 			&worksheet->uclk_pstate.stream_pstate_meta[stream_index]);
941 	}
942 
943 	/* get synchronized timing groups */
944 	dcn5_build_synchronized_timing_groups(&stage->func_locals->uclk_pstate_init.synchronized_timing_groups, display_config);
945 
946 	if (build_override_strategy) {
947 		/* build expanded override strategy list (no permutations) */
948 		override_base_strategy.allow_state_increase = true;
949 		s->num_expanded_override_strategies = 0;
950 		dcn5_insert_strategy_into_expanded_list(&override_base_strategy,
951 				display_config->num_streams,
952 				s->expanded_override_strategy_list,
953 				&s->num_expanded_override_strategies);
954 		dcn5_expand_variant_strategy(&override_base_strategy,
955 				display_config->num_streams,
956 				false,
957 				s->expanded_override_strategy_list,
958 				&s->num_expanded_override_strategies);
959 
960 		/* use override strategy list */
961 		strategy_list = s->expanded_override_strategy_list;
962 		strategy_list_size = s->num_expanded_override_strategies;
963 	} else {
964 		/* use predefined strategy list */
965 		strategy_list = dcn5_get_expanded_strategy_list(stage, display_config->num_streams);
966 		strategy_list_size = dcn5_get_num_expanded_strategies(stage, display_config->num_streams);
967 	}
968 
969 	worksheet->uclk_pstate.num_pstate_candidates = 0;
970 
971 	if (!strategy_list || strategy_list_size == 0)
972 		goto exit;
973 
974 	for (i = 0; i < strategy_list_size && worksheet->uclk_pstate.num_pstate_candidates < DML2_PMO_PSTATE_CANDIDATE_LIST_SIZE; i++) {
975 		if (validate_pstate_support_strategy_cofunctionality(stage, worksheet, display_config, &strategy_list[i])) {
976 			dcn5_insert_into_candidate_list(&strategy_list[i], display_config->num_streams, worksheet);
977 		}
978 	}
979 
980 	if (worksheet->uclk_pstate.num_pstate_candidates > 0) {
981 		worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.num_pstate_candidates-1].allow_state_increase = true;
982 		worksheet->uclk_pstate.cur_pstate_candidate = -1;
983 		goto exit;
984 	} else {
985 		goto exit;
986 	}
987 exit:
988 	DML_LOG_COMP_IF_EXIT();
989 }
990 
setup_planes_for_alternate_by_mask(struct dml2_pmo_stage_optimizer * stage,struct dml2_optimization_worksheet * worksheet,int plane_mask)991 static void setup_planes_for_alternate_by_mask(struct dml2_pmo_stage_optimizer *stage,
992 		struct dml2_optimization_worksheet *worksheet,
993 		int plane_mask)
994 {
995 	(void)stage;
996 	unsigned int plane_index;
997 	const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg;
998 
999 	for (plane_index = 0; plane_index < display_config->num_planes; plane_index++)
1000 		if (is_bit_set_in_bitfield(plane_mask, plane_index))
1001 			worksheet->cur.config.uclk_pstate_switch_modes[plane_index] = dml2_pstate_method_alternate;
1002 }
1003 
dcn6_setup_planes_for_vactive_by_mask(struct dml2_pmo_stage_optimizer * stage,struct dml2_optimization_worksheet * worksheet,int plane_mask)1004 static void dcn6_setup_planes_for_vactive_by_mask(struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet, int plane_mask)
1005 {
1006 	unsigned int plane_index;
1007 	unsigned int stream_index;
1008 	const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg;
1009 	const struct dml2_pmo_instance *pmo = stage->pmo;
1010 
1011 	for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) {
1012 		if (is_bit_set_in_bitfield(plane_mask, plane_index)) {
1013 			stream_index = display_config->plane_descriptors[plane_index].stream_index;
1014 
1015 			worksheet->cur.config.uclk_pstate_switch_modes[plane_index] = dml2_pstate_method_vactive;
1016 
1017 			if (!pmo->options->disable_vactive_det_fill_bw_pad) {
1018 				if (worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][dml2_pstate_type_uclk] > 0) {
1019 					worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][dml2_pstate_type_uclk] = (int)math_min2(
1020 							math_floor(worksheet->uclk_pstate.stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us),
1021 							worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][dml2_pstate_type_uclk]);
1022 				} else {
1023 					worksheet->cur.config.max_vactive_det_fill_delay_us[plane_index][dml2_pstate_type_uclk] = (int)math_floor(
1024 							worksheet->uclk_pstate.stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us);
1025 				}
1026 			}
1027 
1028 			worksheet->cur.config.reserved_vblank_time_ns[plane_index] = (long)math_max2(
1029 					worksheet->uclk_pstate.stream_pstate_meta[stream_index].method_vactive.reserved_vblank_required_us * 1000,
1030 					worksheet->cur.config.reserved_vblank_time_ns[plane_index]);
1031 		}
1032 	}
1033 }
1034 
setup_optimized_worksheet_for_uclk_pstate(struct dml2_pmo_stage_optimizer * stage,struct dml2_optimization_worksheet * worksheet)1035 static bool setup_optimized_worksheet_for_uclk_pstate(struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet)
1036 {
1037 	bool fams2_required = false;
1038 	bool legacy_pstate_info_for_dmu = false;
1039 	bool success = true;
1040 	unsigned int stream_index, plane_index;
1041 	int strategy_index = worksheet->uclk_pstate.cur_pstate_candidate;
1042 	const struct dml2_plane_parameters *plane_descriptor;
1043 
1044 	for (plane_index = 0; plane_index < worksheet->orig_dispcfg->num_planes; plane_index++) {
1045 		plane_descriptor = &worksheet->orig_dispcfg->plane_descriptors[plane_index];
1046 		set_bit_in_bitfield(&worksheet->uclk_pstate.stream_plane_mask[plane_descriptor->stream_index], plane_index);
1047 	}
1048 
1049 	for (stream_index = 0; stream_index < worksheet->orig_dispcfg->num_streams; stream_index++) {
1050 
1051 		if (worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pstate_method_na) {
1052 			success = false;
1053 			break;
1054 		} else if (worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pstate_method_vactive) {
1055 			legacy_pstate_info_for_dmu = true;
1056 			dcn6_setup_planes_for_vactive_by_mask(stage, worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]);
1057 		} else if (worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pstate_method_vblank) {
1058 			legacy_pstate_info_for_dmu = true;
1059 			dcn5_setup_planes_for_vblank_by_mask(stage, worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]);
1060 		} else if (worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vactive_drr) {
1061 			fams2_required = true;
1062 			dcn5_setup_planes_for_vactive_drr_by_mask(stage, worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]);
1063 		} else if (worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vblank_drr) {
1064 			fams2_required = true;
1065 			dcn5_setup_planes_for_vblank_drr_by_mask(stage, worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]);
1066 		} else if (worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_drr) {
1067 			fams2_required = true;
1068 			dcn5_setup_planes_for_drr_by_mask(stage, worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]);
1069 		} else if (worksheet->uclk_pstate.pstate_strategy_candidates[strategy_index].per_stream_pstate_method[stream_index] == dml2_pstate_method_alternate) {
1070 			fams2_required = true;
1071 			setup_planes_for_alternate_by_mask(stage, worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]);
1072 		}
1073 	}
1074 
1075 	/* Indicate if FAMS2 required */
1076 	if (success) {
1077 		worksheet->cur.config.fams2_required = fams2_required;
1078 		worksheet->cur.config.legacy_pstate_info_for_dmu = legacy_pstate_info_for_dmu;
1079 		// Copy FAMS2 meta unconditionally - we need for vactive as well
1080 		memcpy(&worksheet->cur.config.stream_pstate_meta,
1081 				&worksheet->uclk_pstate.stream_pstate_meta,
1082 				sizeof(struct dml2_pstate_meta) * DML2_MAX_PLANES);
1083 		worksheet->cur.config.uclk_pstate_support = true;
1084 	}
1085 
1086 	return success;
1087 }
1088 
dml2_pmo_dcn6_stage_optimizer_uclk_pstate_optimize_next(struct dml2_pmo_stage_optimizer * stage,struct dml2_optimization_worksheet * worksheet)1089 static bool dml2_pmo_dcn6_stage_optimizer_uclk_pstate_optimize_next(
1090 		struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet)
1091 {
1092 	bool should_continue = true;
1093 
1094 	DML_LOG_COMP_IF_ENTER();
1095 	/* Nothing to optimize if there are no candidates, so return false */
1096 	if (worksheet->uclk_pstate.num_pstate_candidates == 0) {
1097 		should_continue = false;
1098 		goto exit;
1099 	}
1100 
1101 	/* Optimization is completed if we find a candidate that passed validation and also passes permissibility.
1102 	 * There are scenarios where permissibility can pass even if validation fails, so we need to check the
1103 	 * validation result here as well.
1104 	 */
1105 	if (worksheet->validation_result.is_mode_support_valid
1106 			&& stage->test_permissibility(stage, worksheet) == DML2_STATUS_OK) {
1107 		should_continue = false;
1108 		goto exit;
1109 	}
1110 
1111 	/* If we've reached the end of the p-state candidate list, return false since
1112 	 * there's no more potential optimization options */
1113 	if (worksheet->uclk_pstate.cur_pstate_candidate == worksheet->uclk_pstate.num_pstate_candidates - 1) {
1114 		should_continue = false;
1115 		goto exit;
1116 	}
1117 
1118 	/* Reset current settings since the previous optimization attempt did not pass */
1119 	dcn5_reset_worksheet_for_uclk_pstate(worksheet);
1120 	worksheet->uclk_pstate.cur_pstate_candidate++;
1121 	worksheet->cur.unvalidated_change.bits.uclk_pstate_method =
1122 			setup_optimized_worksheet_for_uclk_pstate(stage, worksheet);
1123 	DML_ASSERT_MSG(worksheet->cur.unvalidated_change.bits.uclk_pstate_method, "optimize_next must apply changes"
1124 		" when returning true!\n");
1125 
1126 exit:
1127 	DML_LOG_DEBUG("%s exit with should_continue = %s\n", __func__, should_continue ? "true" : "false");
1128 	DML_LOG_COMP_IF_EXIT();
1129 	return should_continue;
1130 }
1131 
dcn6_alternate_permissible(const struct dml2_validation_result * validation_res,const struct dml2_pmo_instance * pmo,const struct dml2_display_cfg * display_cfg,int stream_idx)1132 static bool dcn6_alternate_permissible(
1133 		const struct dml2_validation_result *validation_res,
1134 		const struct dml2_pmo_instance *pmo,
1135 		const struct dml2_display_cfg *display_cfg,
1136 		int stream_idx)
1137 {
1138 	(void)pmo;
1139 	unsigned int svp0_dst_lines = validation_res->mode_support.cfg_support_info.stream_support_info[stream_idx].alternate_svp0_dst_lines;
1140 	unsigned int svp1_dst_lines = validation_res->mode_support.cfg_support_info.stream_support_info[stream_idx].alternate_svp1_dst_lines;
1141 	unsigned int max_dst_y_pre = validation_res->mode_support.cfg_support_info.stream_support_info[stream_idx].max_dst_y_prefetch;
1142 	unsigned int max_dst_y_after_scaler = validation_res->mode_support.cfg_support_info.stream_support_info[stream_idx].max_dst_y_after_scaler;
1143 	const unsigned int max_hw_cursor_size = 135; // Actual is 128, but set to 135 for margin
1144 
1145 	// svp0 + svp1 < vtotal - vstartup is required to support alt-chan
1146 	// TBD if we need to increase constraint to vtotal - vstartup - cursor_height -> required if last cursor deadline is beyond vblank end
1147 	if (svp0_dst_lines + svp1_dst_lines >= display_cfg->stream_descriptors[stream_idx].timing.v_total - max_dst_y_pre - max_dst_y_after_scaler - max_hw_cursor_size)
1148 		return false;
1149 
1150 	return true;
1151 }
1152 
dml2_pmo_dcn6_stage_optimizer_uclk_pstate_test_permissibility(struct dml2_pmo_stage_optimizer * stage,const struct dml2_optimization_worksheet * worksheet)1153 static enum dml2_status dml2_pmo_dcn6_stage_optimizer_uclk_pstate_test_permissibility(
1154 	struct dml2_pmo_stage_optimizer *stage, const struct dml2_optimization_worksheet *worksheet)
1155 {
1156 	enum dml2_status status = DML2_STATUS_OK;
1157 	unsigned int stream_index;
1158 	const struct dml2_pmo_instance *pmo = stage->pmo;
1159 
1160 	int REQUIRED_RESERVED_TIME = 0;
1161 
1162 	DML_LOG_COMP_IF_ENTER();
1163 
1164 	/* Permissibility passes if all streams are blanked - p-state support is guaranteed for this case*/
1165 	if (worksheet->orig_dispcfg->overrides.all_streams_blanked) {
1166 		status = DML2_STATUS_OK;
1167 		goto exit;
1168 	}
1169 
1170 	/* If there are no pstate candidates then pstate support is false and permissibility fails */
1171 	if (worksheet->uclk_pstate.num_pstate_candidates == 0) {
1172 		status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE;
1173 		goto exit;
1174 	}
1175 
1176 	if (worksheet->uclk_pstate.cur_pstate_candidate < 0) {
1177 		status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE;
1178 		goto exit;
1179 	}
1180 
1181 	if (!worksheet->validation_result.mode_support.global.uclk_pstate_supported) {
1182 		status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE;
1183 		goto exit;
1184 	}
1185 
1186 	REQUIRED_RESERVED_TIME = (int)pmo->utm_soc_bb->power_management_parameters.dram_clk_change_blackout_us;
1187 
1188 	for (stream_index = 0; stream_index < worksheet->orig_dispcfg->num_streams; stream_index++) {
1189 		const struct dml2_pstate_meta *stream_pstate_meta = &worksheet->cur.config.stream_pstate_meta[stream_index];
1190 
1191 		if (worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_vactive ||
1192 				worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vactive_drr) {
1193 			if (worksheet->orig_dispcfg->num_streams == 1) {
1194 				/* Peak VActive + VBlank (single stream only) */
1195 				if (dcn6_get_vactive_latency_hiding(&worksheet->validation_result, worksheet->uclk_pstate.stream_plane_mask[stream_index]) +
1196 						dcn5_get_minimum_reserved_time_us_for_planes(worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]) < REQUIRED_RESERVED_TIME) {
1197 					status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE;
1198 					break;
1199 				}
1200 			} else if (dcn5_get_vactive_pstate_margin(&worksheet->validation_result, worksheet->uclk_pstate.stream_plane_mask[stream_index]) < 0.0 ||
1201 				dcn6_get_vactive_det_fill_delay_us(&worksheet->validation_result, dml2_pstate_type_uclk, worksheet->uclk_pstate.stream_plane_mask[stream_index]) > math_ceil(stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_us))  {
1202 				status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE;
1203 				break;
1204 			}
1205 		} else if (worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_vblank ||
1206 				worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vblank_drr) {
1207 			if (dcn5_get_minimum_reserved_time_us_for_planes(worksheet, worksheet->uclk_pstate.stream_plane_mask[stream_index]) < REQUIRED_RESERVED_TIME) {
1208 				status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE;
1209 				break;
1210 			}
1211 		} else if (worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_drr) {
1212 			if (!all_planes_match_method(worksheet->orig_dispcfg, worksheet->uclk_pstate.stream_plane_mask[stream_index], dml2_pstate_method_fw_drr)) {
1213 				status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE;
1214 				break;
1215 			}
1216 		} else if (worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_alternate) {
1217 			if (!all_planes_match_method(worksheet->orig_dispcfg, worksheet->uclk_pstate.stream_plane_mask[stream_index], dml2_pstate_method_alternate) ||
1218 					!dcn6_alternate_permissible(&worksheet->validation_result, pmo, worksheet->orig_dispcfg, stream_index)) {
1219 				status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE;
1220 				break;
1221 			}
1222 		} else if (worksheet->uclk_pstate.pstate_strategy_candidates[worksheet->uclk_pstate.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_na) {
1223 			status = DML2_STATUS_OPTIMIZE_FAIL_UCLK_PSTATE;
1224 			break;
1225 		}
1226 	}
1227 exit:
1228 	DML_LOG_DEBUG("%s exit with status = %s\n", __func__, dml2_status_str(status));
1229 	DML_LOG_COMP_IF_EXIT();
1230 	return status;
1231 }
1232 
dml2_pmo_dcn6_stage_optimizer_uclk_pstate_create(struct dml2_pmo_instance * pmo,struct dml2_pmo_stage_optimizer * stage)1233 void dml2_pmo_dcn6_stage_optimizer_uclk_pstate_create(struct dml2_pmo_instance *pmo,
1234 		struct dml2_pmo_stage_optimizer *stage)
1235 {
1236 	stage->pmo = pmo;
1237 	stage->func_locals = &pmo->scratch.pmo_dcn5.func_locals;
1238 	stage->init = dml2_pmo_dcn6_stage_optimizer_uclk_pstate_init;
1239 	stage->optimize_next = dml2_pmo_dcn6_stage_optimizer_uclk_pstate_optimize_next;
1240 	stage->test_permissibility =
1241 			dml2_pmo_dcn6_stage_optimizer_uclk_pstate_test_permissibility;
1242 }
1243 
1244 /*
1245 * Counts the number of elements inside input array within the given span length.
1246 * Formally, what is the size of the largest subset of the array where the largest and smallest element
1247 * differ no more than the span.
1248 */
count_elements_in_span(const int * array,unsigned int array_size,unsigned int span)1249 static unsigned int count_elements_in_span(const int *array, unsigned int array_size, unsigned int span)
1250 {
1251 	unsigned int i;
1252 	unsigned int span_start_value;
1253 	unsigned int span_start_index;
1254 	unsigned int greatest_element_count;
1255 
1256 	if (array_size == 0)
1257 		return 1;
1258 
1259 	if (span == 0)
1260 		return array_size > 0 ? 1 : 0;
1261 
1262 	span_start_value = 0;
1263 	span_start_index = 0;
1264 	greatest_element_count = 0;
1265 
1266 	while (span_start_index < array_size) {
1267 		for (i = span_start_index; i < array_size; i++) {
1268 			if (array[i] - span_start_value <= span) {
1269 				if (i - span_start_index + 1 > greatest_element_count) {
1270 					greatest_element_count = i - span_start_index + 1;
1271 				}
1272 			} else
1273 				break;
1274 		}
1275 
1276 		span_start_index++;
1277 
1278 		if (span_start_index < array_size) {
1279 			span_start_value = array[span_start_index - 1] + 1;
1280 		}
1281 	}
1282 
1283 	return greatest_element_count;
1284 }
1285 
calculate_h_split_for_scaling_transform(int full_vp_width,int h_active,int num_pipes,enum dml2_scaling_transform scaling_transform,int * pipe_vp_x_start,int * pipe_vp_x_end)1286 static bool calculate_h_split_for_scaling_transform(int full_vp_width, int h_active, int num_pipes,
1287 	enum dml2_scaling_transform scaling_transform, int *pipe_vp_x_start, int *pipe_vp_x_end)
1288 {
1289 	(void)h_active;
1290 	int i, slice_width;
1291 	const char MAX_SCL_VP_OVERLAP = 3;
1292 	bool success = false;
1293 
1294 	switch (scaling_transform) {
1295 	case dml2_scaling_transform_centered:
1296 	case dml2_scaling_transform_aspect_ratio:
1297 	case dml2_scaling_transform_fullscreen:
1298 		slice_width = full_vp_width / num_pipes;
1299 		for (i = 0; i < num_pipes; i++) {
1300 			pipe_vp_x_start[i] = i * slice_width;
1301 			pipe_vp_x_end[i] = (i + 1) * slice_width - 1;
1302 
1303 			if (pipe_vp_x_start[i] < MAX_SCL_VP_OVERLAP)
1304 				pipe_vp_x_start[i] = 0;
1305 			else
1306 				pipe_vp_x_start[i] -= MAX_SCL_VP_OVERLAP;
1307 
1308 			if (pipe_vp_x_end[i] > full_vp_width - MAX_SCL_VP_OVERLAP - 1)
1309 				pipe_vp_x_end[i] = full_vp_width - 1;
1310 			else
1311 				pipe_vp_x_end[i] += MAX_SCL_VP_OVERLAP;
1312 		}
1313 		break;
1314 	case dml2_scaling_transform_explicit:
1315 	default:
1316 		success = false;
1317 		break;
1318 	}
1319 
1320 	return success;
1321 }
1322 
1323 /*
1324 * Takes an input set of mcache boundaries and finds the appropriate setting of cache programming.
1325 * Returns true if a valid set of programming can be made, and false otherwise. "Valid" means
1326 * that the horizontal viewport does not span more than 2 cache slices.
1327 *
1328 * It optionally also can apply a constant shift to all the cache boundaries.
1329 */
calculate_first_second_splitting(const int * mcache_boundaries,int num_boundaries,int shift,int pipe_h_vp_start,int pipe_h_vp_end,int * first_offset,int * second_offset)1330 static bool calculate_first_second_splitting(const int *mcache_boundaries, int num_boundaries, int shift,
1331 	int pipe_h_vp_start, int pipe_h_vp_end, int *first_offset, int *second_offset)
1332 {
1333 	const int MAX_VP = 0xFFFFFF;
1334 	int left_cache_id;
1335 	int right_cache_id;
1336 	int range_start;
1337 	int range_end;
1338 	bool success = false;
1339 
1340 	if (num_boundaries <= 1) {
1341 		if (first_offset && second_offset) {
1342 			*first_offset = 0;
1343 			*second_offset = -1;
1344 		}
1345 		success = true;
1346 		return success;
1347 	} else {
1348 		range_start = 0;
1349 		for (left_cache_id = 0; left_cache_id < num_boundaries; left_cache_id++) {
1350 			range_end = mcache_boundaries[left_cache_id] - shift - 1;
1351 
1352 			if (range_start <= pipe_h_vp_start && pipe_h_vp_start <= range_end)
1353 				break;
1354 
1355 			range_start = range_end + 1;
1356 		}
1357 
1358 		range_end = MAX_VP;
1359 		for (right_cache_id = num_boundaries - 1; right_cache_id >= -1; right_cache_id--) {
1360 			if (right_cache_id >= 0)
1361 				range_start = mcache_boundaries[right_cache_id] - shift;
1362 			else
1363 				range_start = 0;
1364 
1365 			if (range_start <= pipe_h_vp_end && pipe_h_vp_end <= range_end) {
1366 				break;
1367 			}
1368 			range_end = range_start - 1;
1369 		}
1370 		right_cache_id = (right_cache_id + 1) % num_boundaries;
1371 
1372 		if (right_cache_id == left_cache_id) {
1373 			if (first_offset && second_offset) {
1374 				*first_offset = left_cache_id;
1375 				*second_offset = -1;
1376 			}
1377 			success = true;
1378 		} else if (right_cache_id == (left_cache_id + 1) % num_boundaries) {
1379 			if (first_offset && second_offset) {
1380 				*first_offset = left_cache_id;
1381 				*second_offset = right_cache_id;
1382 			}
1383 			success = true;
1384 		}
1385 	}
1386 
1387 	return success;
1388 }
1389 
1390 /*
1391 * For a given set of pipe start/end x positions, checks to see it can support the input mcache splitting.
1392 * It also attempts to "optimize" by finding a shift if the default 0 shift does not work.
1393 */
find_shift_for_valid_cache_id_assignment(const int * mcache_boundaries,unsigned int num_boundaries,int * pipe_vp_startx,int * pipe_vp_endx,unsigned int pipe_count,int shift_granularity,int * shift)1394 static bool find_shift_for_valid_cache_id_assignment(const int *mcache_boundaries, unsigned int num_boundaries,
1395 	int *pipe_vp_startx, int *pipe_vp_endx, unsigned int pipe_count, int shift_granularity, int *shift)
1396 {
1397 	int max_shift = 0xFFFF;
1398 	unsigned int pipe_index;
1399 	unsigned int i, slice_width;
1400 	bool success = false;
1401 
1402 	for (i = 0; i < num_boundaries; i++) {
1403 		if (i == 0)
1404 			slice_width = mcache_boundaries[i];
1405 		else
1406 			slice_width = mcache_boundaries[i] - mcache_boundaries[i - 1];
1407 
1408 		if (max_shift > (int)slice_width) {
1409 			max_shift = slice_width;
1410 		}
1411 	}
1412 
1413 	for (*shift = 0; *shift <= max_shift; *shift += shift_granularity) {
1414 		success = true;
1415 		for (pipe_index = 0; pipe_index < pipe_count; pipe_index++) {
1416 			if (!calculate_first_second_splitting(mcache_boundaries, num_boundaries, *shift,
1417 				pipe_vp_startx[pipe_index], pipe_vp_endx[pipe_index], NULL, NULL)) {
1418 				success = false;
1419 				break;
1420 			}
1421 		}
1422 		if (success)
1423 			break;
1424 	}
1425 
1426 	return success;
1427 }
1428 
1429 
1430 
dml2_pmo_dcn6_stage_optimizer_mcache_decide_shifts(struct dml2_pmo_stage_optimizer * stage,struct dml2_optimization_worksheet * worksheet)1431 static void dml2_pmo_dcn6_stage_optimizer_mcache_decide_shifts(struct dml2_pmo_stage_optimizer *stage,
1432 	struct dml2_optimization_worksheet *worksheet)
1433 {
1434 	(void)stage;
1435 	const int MAX_PIXEL_OVERLAP = 6;
1436 	int max_per_pipe_vp_p0 = 0;
1437 	int max_per_pipe_vp_p1 = 0;
1438 	int temp, p0shift, p1shift;
1439 	unsigned int plane_index = 0;
1440 	unsigned int i;
1441 	unsigned int odm_combine_factor;
1442 	unsigned int mpc_combine_factor;
1443 	unsigned int num_dpps;
1444 	unsigned int num_boundaries;
1445 	enum dml2_scaling_transform scaling_transform;
1446 	const struct dml2_plane_parameters *plane;
1447 	const struct dml2_stream_parameters *stream;
1448 	const struct dml2_mcache_surface_allocation *base_allocations = worksheet->validation_result.mcache_allocations;
1449 	struct dml2_mcache_surface_allocation *new_allocations = worksheet->cur.config.mcache_allocations;
1450 	bool p0pass = false;
1451 	bool p1pass = false;
1452 
1453 	for (plane_index = 0; plane_index < worksheet->orig_dispcfg->num_planes; plane_index++) {
1454 		if (!worksheet->orig_dispcfg->plane_descriptors[plane_index].surface.dcc.enable)
1455 			continue;
1456 
1457 		plane = &worksheet->orig_dispcfg->plane_descriptors[plane_index];
1458 		stream = &worksheet->orig_dispcfg->stream_descriptors[plane->stream_index];
1459 
1460 		odm_combine_factor = worksheet->cur.config.odm_combine_overrides[plane->stream_index] > 0 ?
1461 			worksheet->cur.config.odm_combine_overrides[plane->stream_index] :
1462 			worksheet->validation_result.mode_support.cfg_support_info.stream_support_info[plane->stream_index].odms_used;
1463 		if (odm_combine_factor == 1) {
1464 			mpc_combine_factor = worksheet->cur.config.mpc_combine_overrides[plane_index] > 0 ?
1465 				worksheet->cur.config.mpc_combine_overrides[plane_index] :
1466 				(unsigned int)worksheet->validation_result.mode_support.cfg_support_info.plane_support_info[plane_index].dpps_used;
1467 			num_dpps = mpc_combine_factor;
1468 		} else {
1469 			mpc_combine_factor = 1;
1470 			num_dpps = odm_combine_factor;
1471 		}
1472 
1473 		if (odm_combine_factor > 1) {
1474 			max_per_pipe_vp_p0 = plane->surface.plane0.width;
1475 			temp = (unsigned int)math_ceil(
1476 				plane->composition.scaler_info.plane0.h_ratio * stream->timing.h_active
1477 				/ odm_combine_factor);
1478 			if (temp < max_per_pipe_vp_p0)
1479 				max_per_pipe_vp_p0 = temp;
1480 
1481 			max_per_pipe_vp_p1 = plane->surface.plane1.width;
1482 			temp = (unsigned int)math_ceil(
1483 				plane->composition.scaler_info.plane1.h_ratio * stream->timing.h_active
1484 				/ odm_combine_factor);
1485 			if (temp < max_per_pipe_vp_p1)
1486 				max_per_pipe_vp_p1 = temp;
1487 		} else {
1488 			max_per_pipe_vp_p0 = plane->surface.plane0.width / mpc_combine_factor;
1489 			max_per_pipe_vp_p1 = plane->surface.plane1.width / mpc_combine_factor;
1490 		}
1491 		max_per_pipe_vp_p0 += 2 * MAX_PIXEL_OVERLAP;
1492 		max_per_pipe_vp_p1 += MAX_PIXEL_OVERLAP;
1493 		p0shift = 0;
1494 		p1shift = 0;
1495 		// The last element in the unshifted boundary array will always be the first pixel outside the
1496 		// plane, which means theres no mcache associated with it, so -1
1497 		num_boundaries =
1498 			base_allocations[plane_index].num_mcaches_plane0 == 0 ?
1499 			0 : base_allocations[plane_index].num_mcaches_plane0 - 1;
1500 		if ((count_elements_in_span(base_allocations[plane_index].mcache_x_offsets_plane0, num_boundaries,
1501 			max_per_pipe_vp_p0) <= 1) && (num_boundaries <= num_dpps)) {
1502 			p0pass = true;
1503 		}
1504 		num_boundaries =
1505 			base_allocations[plane_index].num_mcaches_plane1 == 0 ?
1506 			0 : base_allocations[plane_index].num_mcaches_plane1 - 1;
1507 		if ((count_elements_in_span(base_allocations[plane_index].mcache_x_offsets_plane1, num_boundaries,
1508 			max_per_pipe_vp_p1) <= 1) && (num_boundaries <= num_dpps)) {
1509 			p1pass = true;
1510 		}
1511 		if (!p0pass || !p1pass) {
1512 			if (odm_combine_factor > 1) {
1513 				num_dpps = odm_combine_factor;
1514 				scaling_transform = plane->composition.scaling_transform;
1515 			} else {
1516 				num_dpps = mpc_combine_factor;
1517 				scaling_transform = dml2_scaling_transform_fullscreen;
1518 			}
1519 			if (!p0pass) {
1520 				if (plane->composition.viewport.stationary) {
1521 					calculate_h_split_for_scaling_transform(plane->surface.plane0.width,
1522 						stream->timing.h_active, num_dpps, scaling_transform,
1523 						&worksheet->mcache.plane0.pipe_vp_startx[plane_index],
1524 						&worksheet->mcache.plane0.pipe_vp_endx[plane_index]);
1525 					p0pass = find_shift_for_valid_cache_id_assignment(
1526 						base_allocations[plane_index].mcache_x_offsets_plane0,
1527 						base_allocations[plane_index].num_mcaches_plane0,
1528 						&worksheet->mcache.plane0.pipe_vp_startx[plane_index],
1529 						&worksheet->mcache.plane0.pipe_vp_endx[plane_index], num_dpps,
1530 						base_allocations[plane_index].shift_granularity.p0, &p0shift);
1531 				}
1532 			}
1533 			if (!p1pass) {
1534 				if (plane->composition.viewport.stationary) {
1535 					calculate_h_split_for_scaling_transform(plane->surface.plane1.width,
1536 						stream->timing.h_active, num_dpps, scaling_transform,
1537 						&worksheet->mcache.plane0.pipe_vp_startx[plane_index],
1538 						&worksheet->mcache.plane0.pipe_vp_endx[plane_index]);
1539 					p1pass = find_shift_for_valid_cache_id_assignment(
1540 						base_allocations[plane_index].mcache_x_offsets_plane1,
1541 						base_allocations[plane_index].num_mcaches_plane1,
1542 						&worksheet->mcache.plane1.pipe_vp_startx[plane_index],
1543 						&worksheet->mcache.plane1.pipe_vp_endx[plane_index], num_dpps,
1544 						base_allocations[plane_index].shift_granularity.p1, &p1shift);
1545 				}
1546 			}
1547 		}
1548 		if (p0pass && p1pass) {
1549 			for (i = 0; i < base_allocations[plane_index].num_mcaches_plane0; i++)
1550 				new_allocations[plane_index].mcache_x_offsets_plane0[i] =
1551 				base_allocations[plane_index].mcache_x_offsets_plane0[i] - p0shift;
1552 			for (i = 0; i < base_allocations[plane_index].num_mcaches_plane1; i++)
1553 				new_allocations[plane_index].mcache_x_offsets_plane1[i] =
1554 				base_allocations[plane_index].mcache_x_offsets_plane1[i] - p1shift;
1555 		}
1556 		worksheet->mcache.per_plane_status[plane_index] = p0pass && p1pass;
1557 	}
1558 }
1559 
dml2_pmo_dcn6_stage_optimizer_mcache_test_permissibility(struct dml2_pmo_stage_optimizer * stage,const struct dml2_optimization_worksheet * worksheet)1560 static enum dml2_status dml2_pmo_dcn6_stage_optimizer_mcache_test_permissibility(
1561 	struct dml2_pmo_stage_optimizer *stage, const struct dml2_optimization_worksheet *worksheet)
1562 {
1563 	if (!dml2_pmo_dcn5_stage_optimizer_mcache_test_total_mcache_limit(stage, worksheet))
1564 		return DML2_STATUS_OPTIMIZE_FAIL_MCACHE;
1565 
1566 	if (!dml2_pmo_dcn5_stage_optimizer_mcache_test_mcache_status(stage, worksheet))
1567 		return DML2_STATUS_OPTIMIZE_FAIL_MCACHE;
1568 
1569 	return DML2_STATUS_OK;
1570 }
1571 
dml2_pmo_dcn6_stage_optimizer_mcache_optimize_next(struct dml2_pmo_stage_optimizer * stage,struct dml2_optimization_worksheet * worksheet)1572 static bool dml2_pmo_dcn6_stage_optimizer_mcache_optimize_next(
1573 	struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet)
1574 {
1575 	if (!worksheet->validation_result.is_mode_support_valid
1576 		|| !worksheet->validation_result.is_mcache_allocation_valid)
1577 		/* validation has failed, stop optimizing further */
1578 		return false;
1579 
1580 	if (stage->test_permissibility(stage, worksheet) == DML2_STATUS_OK)
1581 		/* optimization is permissible, no need to optimize further */
1582 		return false;
1583 
1584 	if (worksheet->mcache.is_default_pipe_usage_attempted) {
1585 		if (!dml2_pmo_dcn5_stage_optimizer_mcache_increment_pipe_usage(stage, worksheet))
1586 			return false;
1587 	} else {
1588 		dml2_pmo_dcn5_stage_optimizer_mcache_apply_default_pipe_usage(stage, worksheet);
1589 		worksheet->mcache.is_default_pipe_usage_attempted = true;
1590 	}
1591 
1592 	dml2_pmo_dcn6_stage_optimizer_mcache_decide_shifts(stage, worksheet);
1593 	worksheet->cur.unvalidated_change.bits.mcache_allocation = true;
1594 
1595 	return true;
1596 }
1597 
dml2_pmo_dcn6_stage_optimizer_mcache_create(struct dml2_pmo_instance * pmo,struct dml2_pmo_stage_optimizer * stage)1598 void dml2_pmo_dcn6_stage_optimizer_mcache_create(struct dml2_pmo_instance *pmo,
1599 	struct dml2_pmo_stage_optimizer *stage)
1600 {
1601 	stage->pmo = pmo;
1602 	stage->func_locals = &pmo->scratch.pmo_dcn5.func_locals;
1603 	stage->init = dml2_pmo_dcn5_stage_optimizer_mcache_init;
1604 	stage->optimize_next = dml2_pmo_dcn6_stage_optimizer_mcache_optimize_next;
1605 	stage->test_permissibility =
1606 		dml2_pmo_dcn6_stage_optimizer_mcache_test_permissibility;
1607 }
1608 
dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_init(struct dml2_pmo_stage_optimizer * stage,struct dml2_optimization_worksheet * worksheet)1609 static void dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_init(
1610 		struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet)
1611 {
1612 	const struct dml2_utm_soc_bb *utm_soc_bb = stage->pmo->utm_soc_bb;
1613 
1614 	DML_LOG_COMP_IF_ENTER();
1615 	worksheet->dcfclk_vmin.max_available_bandwidth_kbps =
1616 			utm_soc_bb->vmin_limit.dcfclk_khz * utm_soc_bb->return_bus_width_bytes;
1617 	DML_LOG_COMP_IF_EXIT();
1618 }
1619 
dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_optimize_next(struct dml2_pmo_stage_optimizer * stage,struct dml2_optimization_worksheet * worksheet)1620 static bool dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_optimize_next(
1621 		struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet)
1622 {
1623 	bool should_continue = true;
1624 	const struct dml2_utm_soc_bb *utm_soc_bb = stage->pmo->utm_soc_bb;
1625 	const struct dml2_sop_table *sop_table = &utm_soc_bb->sop_table;
1626 
1627 	DML_LOG_COMP_IF_ENTER();
1628 	if (utm_soc_bb->vmin_limit.dcfclk_khz == 0) {
1629 		/* vmin limit for dcfclk is not configured in soc bb */
1630 		should_continue = false;
1631 		goto exit;
1632 	}
1633 
1634 	if (worksheet->cur.config.enable_vmin_dcfclk) {
1635 		/* vmin dcfclk is already enabled, nothing else to try */
1636 		should_continue = false;
1637 		goto exit;
1638 	}
1639 
1640 	if (sop_table->sop_optimal_dcfclks_khz[worksheet->cur.config.min_sop_index] <= utm_soc_bb->vmin_limit.dcfclk_khz) {
1641 		/* current sop optimal dcfclk is already less than vmin dcfclk */
1642 		should_continue = false;
1643 		goto exit;
1644 	}
1645 	if (worksheet->validation_result.mode_support.bandwidth_upper_bound.dcn5.urgent_bandwidth_kbps
1646 	> worksheet->dcfclk_vmin.max_available_bandwidth_kbps) {
1647 		/*
1648 		 * required urgent bandwidth exceeds max bandwidth available, reducing dcfclk will only increase
1649 		 * bandwidth requirements even more. It is guaranteed to fail bandwidth validation. No need to attempt.
1650 		 */
1651 		should_continue = false;
1652 		goto exit;
1653 	}
1654 
1655 	worksheet->cur.unvalidated_change.bits.dcfclk_override = true;
1656 	worksheet->cur.config.enable_vmin_dcfclk = true;
1657 exit:
1658 	DML_LOG_DEBUG("%s exit with should_continue = %s\n", __func__, should_continue ? "true" : "false");
1659 	DML_LOG_COMP_IF_EXIT();
1660 	return should_continue;
1661 }
1662 
dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_test_permissibility(struct dml2_pmo_stage_optimizer * stage,const struct dml2_optimization_worksheet * worksheet)1663 static enum dml2_status dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_test_permissibility(
1664 		struct dml2_pmo_stage_optimizer *stage, const struct dml2_optimization_worksheet *worksheet)
1665 {
1666 	(void)stage;
1667 	enum dml2_status status = worksheet->cur.config.enable_vmin_dcfclk ?
1668 			DML2_STATUS_OK : DML2_STATUS_OPTIMIZE_FAIL_VMIN_DCFCLK;
1669 
1670 	DML_LOG_COMP_IF_ENTER();
1671 	DML_LOG_DEBUG("%s exit with status = %s\n", __func__, dml2_status_str(status));
1672 	DML_LOG_COMP_IF_EXIT();
1673 	return status;
1674 }
1675 
dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_create(struct dml2_pmo_instance * pmo,struct dml2_pmo_stage_optimizer * stage)1676 void dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_create(struct dml2_pmo_instance *pmo,
1677 		struct dml2_pmo_stage_optimizer *stage)
1678 {
1679 	stage->pmo = pmo;
1680 	stage->func_locals = &pmo->scratch.pmo_dcn5.func_locals;
1681 	stage->init = dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_init;
1682 	stage->optimize_next = dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_optimize_next;
1683 	stage->test_permissibility =
1684 			dml2_pmo_dcn6_stage_optimizer_vmin_dcfclk_test_permissibility;
1685 }
1686 
dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_init(struct dml2_pmo_stage_optimizer * stage,struct dml2_optimization_worksheet * worksheet)1687 static void dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_init(
1688 		struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet)
1689 {
1690 	DML_LOG_COMP_IF_ENTER();
1691 	worksheet->fclk_ppt_temp_read_pstate.is_attempted = false;
1692 	memset(&stage->func_locals->fclk_ppt_temp_read_pstate_optimize, 0,
1693 			sizeof(struct dml2_stage_optimizer_fclk_ppt_temp_read_pstate_optimize_locals));
1694 	DML_LOG_COMP_IF_EXIT();
1695 }
1696 
dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_optimize_next(struct dml2_pmo_stage_optimizer * stage,struct dml2_optimization_worksheet * worksheet)1697 static bool dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_optimize_next(
1698 		struct dml2_pmo_stage_optimizer *stage, struct dml2_optimization_worksheet *worksheet)
1699 {
1700 	const struct dml2_pmo_instance *pmo = stage->pmo;
1701 	const enum dml2_pstate_type pstate_type_list[3] = {
1702 		dml2_pstate_type_fclk,
1703 		dml2_pstate_type_ppt,
1704 		dml2_pstate_type_temp_read,
1705 	};
1706 	struct dml2_stage_optimizer_fclk_ppt_temp_read_pstate_optimize_locals *l
1707 			= &stage->func_locals->fclk_ppt_temp_read_pstate_optimize;
1708 	const struct dml2_display_cfg *display_config = worksheet->orig_dispcfg;
1709 	unsigned int stream_index, plane_index, plane_mask;
1710 	unsigned int i;
1711 	bool modified = false;
1712 
1713 	DML_LOG_COMP_IF_ENTER();
1714 
1715 	if (worksheet->fclk_ppt_temp_read_pstate.is_attempted) {
1716 		DML_LOG_COMP_IF_EXIT();
1717 		return false;
1718 	}
1719 
1720 	worksheet->fclk_ppt_temp_read_pstate.is_attempted = true;
1721 
1722 	if (display_config->overrides.all_streams_blanked)
1723 		goto exit;
1724 
1725 	for (i = 0; i < 3; i++) {
1726 		dcn6_get_params_for_pstate_type(pmo, worksheet,
1727 				pstate_type_list[i],
1728 				&l->pstate_allow_delay_us,
1729 				&l->pstate_blackout_us,
1730 				&l->pstate_watermark_us);
1731 
1732 		if (l->pstate_blackout_us <= 0.0)
1733 			continue;
1734 
1735 		for (stream_index = 0; stream_index < display_config->num_streams; stream_index++) {
1736 			plane_mask = 0;
1737 			for (plane_index = 0; plane_index < display_config->num_planes; plane_index++) {
1738 				if (display_config->plane_descriptors[plane_index].stream_index == stream_index)
1739 					set_bit_in_bitfield(&plane_mask, plane_index);
1740 			}
1741 
1742 			l->per_stream_pstate_method[stream_index] = dml2_pstate_method_vactive;
1743 
1744 			dcn6_build_pstate_meta_per_stream(display_config,
1745 					pmo->ip_caps,
1746 					worksheet,
1747 					pstate_type_list[i],
1748 					l->pstate_watermark_us,
1749 					l->pstate_blackout_us,
1750 					l->pstate_allow_delay_us,
1751 					stream_index,
1752 					plane_mask,
1753 					&l->per_stream_pstate_meta[stream_index]);
1754 		}
1755 
1756 		/* always update the worksheet with latest requirements */
1757 		if (dcn6_update_worksheet_for_pstate_admissibility(worksheet,
1758 				l->per_stream_pstate_meta, pstate_type_list[i])) {
1759 			modified = true;
1760 			DML_LOG_VERBOSE("fclk_ppt_temp_read pstate not admissible with current worksheet, adjusting the reserved vblank time\n");
1761 		}
1762 	}
1763 
1764 exit:
1765 	DML_LOG_DEBUG("%s exit with should_continue = %s\n", __func__, modified ? "true" : "false");
1766 	DML_LOG_COMP_IF_EXIT();
1767 	return modified;
1768 }
1769 
dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_test_permissibility(struct dml2_pmo_stage_optimizer * stage,const struct dml2_optimization_worksheet * worksheet)1770 static enum dml2_status dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_test_permissibility(
1771 		struct dml2_pmo_stage_optimizer *stage, const struct dml2_optimization_worksheet *worksheet)
1772 {
1773 	(void)stage;
1774 	(void)worksheet;
1775 	DML_LOG_COMP_IF_ENTER();
1776 	DML_LOG_COMP_IF_EXIT();
1777 	return DML2_STATUS_OK;
1778 }
1779 
dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_create(struct dml2_pmo_instance * pmo,struct dml2_pmo_stage_optimizer * stage)1780 void dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_create(struct dml2_pmo_instance *pmo,
1781 		struct dml2_pmo_stage_optimizer *stage)
1782 {
1783 	stage->pmo = pmo;
1784 	stage->func_locals = &pmo->scratch.pmo_dcn5.func_locals;
1785 	stage->init = dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_init;
1786 	stage->optimize_next = dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_optimize_next;
1787 	stage->test_permissibility =
1788 			dml2_pmo_dcn6_stage_optimizer_fclk_ppt_temp_read_pstate_test_permissibility;
1789 }
1790