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