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