1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Copyright 2023 Advanced Micro Devices, Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 * OTHER DEALINGS IN THE SOFTWARE.
22 *
23 * Authors: AMD
24 *
25 */
26
27 #include "dml2_mall_phantom.h"
28
29 #include "dml2_dc_types.h"
30 #include "dml2_internal_types.h"
31 #include "dml2_utils.h"
32 #include "dml2_dc_resource_mgmt.h"
33
34 #define MAX_ODM_FACTOR 4
35 #define MAX_MPCC_FACTOR 4
36
37 struct dc_plane_pipe_pool {
38 unsigned int pipes_assigned_to_plane[MAX_ODM_FACTOR][MAX_MPCC_FACTOR];
39 bool pipe_used[MAX_ODM_FACTOR][MAX_MPCC_FACTOR];
40 int num_pipes_assigned_to_plane_for_mpcc_combine;
41 int num_pipes_assigned_to_plane_for_odm_combine;
42 };
43
44 struct dc_pipe_mapping_scratch {
45 struct {
46 unsigned int odm_factor;
47 unsigned int odm_slice_end_x[MAX_PIPES];
48 struct pipe_ctx *next_higher_pipe_for_odm_slice[MAX_PIPES];
49 } odm_info;
50 struct {
51 unsigned int mpc_factor;
52 struct pipe_ctx *prev_odm_pipe;
53 } mpc_info;
54
55 struct dc_plane_pipe_pool pipe_pool;
56 };
57
get_plane_id(struct dml2_context * dml2,const struct dc_state * state,const struct dc_plane_state * plane,unsigned int stream_id,unsigned int plane_index,unsigned int * plane_id)58 static bool get_plane_id(struct dml2_context *dml2, const struct dc_state *state, const struct dc_plane_state *plane,
59 unsigned int stream_id, unsigned int plane_index, unsigned int *plane_id)
60 {
61 int i, j;
62 bool is_plane_duplicate = dml2->v20.scratch.plane_duplicate_exists;
63
64 if (!plane_id)
65 return false;
66
67 for (i = 0; i < state->stream_count; i++) {
68 if (state->streams[i]->stream_id == stream_id) {
69 for (j = 0; j < state->stream_status[i].plane_count; j++) {
70 if (state->stream_status[i].plane_states[j] == plane &&
71 (!is_plane_duplicate || (j == plane_index))) {
72 *plane_id = (i << 16) | j;
73 return true;
74 }
75 }
76 }
77 }
78
79 return false;
80 }
81
find_disp_cfg_idx_by_plane_id(struct dml2_dml_to_dc_pipe_mapping * mapping,unsigned int plane_id)82 static int find_disp_cfg_idx_by_plane_id(struct dml2_dml_to_dc_pipe_mapping *mapping, unsigned int plane_id)
83 {
84 int i;
85
86 for (i = 0; i < __DML2_WRAPPER_MAX_STREAMS_PLANES__; i++) {
87 if (mapping->disp_cfg_to_plane_id_valid[i] && mapping->disp_cfg_to_plane_id[i] == plane_id)
88 return i;
89 }
90
91 ASSERT(false);
92 return __DML2_WRAPPER_MAX_STREAMS_PLANES__;
93 }
94
find_disp_cfg_idx_by_stream_id(struct dml2_dml_to_dc_pipe_mapping * mapping,unsigned int stream_id)95 static int find_disp_cfg_idx_by_stream_id(struct dml2_dml_to_dc_pipe_mapping *mapping, unsigned int stream_id)
96 {
97 int i;
98
99 for (i = 0; i < __DML2_WRAPPER_MAX_STREAMS_PLANES__; i++) {
100 if (mapping->disp_cfg_to_stream_id_valid[i] && mapping->disp_cfg_to_stream_id[i] == stream_id)
101 return i;
102 }
103
104 ASSERT(false);
105 return __DML2_WRAPPER_MAX_STREAMS_PLANES__;
106 }
107
108 // The master pipe of a stream is defined as the top pipe in odm slice 0
find_master_pipe_of_stream(struct dml2_context * ctx,struct dc_state * state,unsigned int stream_id)109 static struct pipe_ctx *find_master_pipe_of_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int stream_id)
110 {
111 int i;
112
113 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
114 if (state->res_ctx.pipe_ctx[i].stream && state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id) {
115 if (!state->res_ctx.pipe_ctx[i].prev_odm_pipe && !state->res_ctx.pipe_ctx[i].top_pipe)
116 return &state->res_ctx.pipe_ctx[i];
117 }
118 }
119
120 return NULL;
121 }
122
find_master_pipe_of_plane(struct dml2_context * ctx,struct dc_state * state,unsigned int plane_id)123 static struct pipe_ctx *find_master_pipe_of_plane(struct dml2_context *ctx,
124 struct dc_state *state, unsigned int plane_id)
125 {
126 int i;
127 unsigned int plane_id_assigned_to_pipe;
128
129 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
130 if (state->res_ctx.pipe_ctx[i].plane_state && get_plane_id(ctx, state, state->res_ctx.pipe_ctx[i].plane_state,
131 state->res_ctx.pipe_ctx[i].stream->stream_id,
132 ctx->v20.scratch.dml_to_dc_pipe_mapping.dml_pipe_idx_to_plane_index[state->res_ctx.pipe_ctx[i].pipe_idx], &plane_id_assigned_to_pipe)) {
133 if (plane_id_assigned_to_pipe == plane_id)
134 return &state->res_ctx.pipe_ctx[i];
135 }
136 }
137
138 return NULL;
139 }
140
find_pipes_assigned_to_plane(struct dml2_context * ctx,struct dc_state * state,unsigned int plane_id,unsigned int * pipes)141 static unsigned int find_pipes_assigned_to_plane(struct dml2_context *ctx,
142 struct dc_state *state, unsigned int plane_id, unsigned int *pipes)
143 {
144 int i;
145 unsigned int num_found = 0;
146 unsigned int plane_id_assigned_to_pipe = UINT_MAX;
147
148 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
149 struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
150
151 if (!pipe->plane_state || !pipe->stream)
152 continue;
153
154 get_plane_id(ctx, state, pipe->plane_state, pipe->stream->stream_id,
155 ctx->v20.scratch.dml_to_dc_pipe_mapping.dml_pipe_idx_to_plane_index[pipe->pipe_idx],
156 &plane_id_assigned_to_pipe);
157 if (plane_id_assigned_to_pipe == plane_id && !pipe->prev_odm_pipe
158 && (!pipe->top_pipe || pipe->top_pipe->plane_state != pipe->plane_state)) {
159 while (pipe) {
160 struct pipe_ctx *mpc_pipe = pipe;
161
162 while (mpc_pipe) {
163 pipes[num_found++] = mpc_pipe->pipe_idx;
164 mpc_pipe = mpc_pipe->bottom_pipe;
165 if (!mpc_pipe)
166 break;
167 if (mpc_pipe->plane_state != pipe->plane_state)
168 mpc_pipe = NULL;
169 }
170 pipe = pipe->next_odm_pipe;
171 }
172 break;
173 }
174 }
175
176 return num_found;
177 }
178
validate_pipe_assignment(const struct dml2_context * ctx,const struct dc_state * state,const struct dml_display_cfg_st * disp_cfg,const struct dml2_dml_to_dc_pipe_mapping * mapping)179 static bool validate_pipe_assignment(const struct dml2_context *ctx, const struct dc_state *state, const struct dml_display_cfg_st *disp_cfg, const struct dml2_dml_to_dc_pipe_mapping *mapping)
180 {
181 (void)ctx;
182 (void)disp_cfg;
183 (void)mapping;
184 (void)state;
185 // int i, j, k;
186 //
187 // unsigned int plane_id;
188 //
189 // unsigned int disp_cfg_index;
190 //
191 // unsigned int pipes_assigned_to_plane[MAX_PIPES];
192 // unsigned int num_pipes_assigned_to_plane;
193 //
194 // struct pipe_ctx *top_pipe;
195 //
196 // for (i = 0; i < state->stream_count; i++) {
197 // for (j = 0; j < state->stream_status[i]->plane_count; j++) {
198 // if (get_plane_id(state, state->stream_status.plane_states[j], &plane_id)) {
199 // disp_cfg_index = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
200 // num_pipes_assigned_to_plane = find_pipes_assigned_to_plane(ctx, state, plane_id, pipes_assigned_to_plane);
201 //
202 // if (disp_cfg_index >= 0 && num_pipes_assigned_to_plane > 0) {
203 // // Verify the number of pipes assigned matches
204 // if (disp_cfg->hw.DPPPerSurface != num_pipes_assigned_to_plane)
205 // return false;
206 //
207 // top_pipe = find_top_pipe_in_tree(state->res_ctx.pipe_ctx[pipes_assigned_to_plane[0]]);
208 //
209 // // Verify MPC and ODM combine
210 // if (disp_cfg->hw.ODMMode == dml_odm_mode_bypass) {
211 // verify_combine_tree(top_pipe, state->streams[i]->stream_id, plane_id, state, false);
212 // } else {
213 // verify_combine_tree(top_pipe, state->streams[i]->stream_id, plane_id, state, true);
214 // }
215 //
216 // // TODO: could also do additional verification that the pipes in tree are the same as
217 // // pipes_assigned_to_plane
218 // } else {
219 // ASSERT(false);
220 // return false;
221 // }
222 // } else {
223 // ASSERT(false);
224 // return false;
225 // }
226 // }
227 // }
228 return true;
229 }
230
is_plane_using_pipe(const struct pipe_ctx * pipe)231 static bool is_plane_using_pipe(const struct pipe_ctx *pipe)
232 {
233 if (pipe->plane_state)
234 return true;
235
236 return false;
237 }
238
is_pipe_free(const struct pipe_ctx * pipe)239 static bool is_pipe_free(const struct pipe_ctx *pipe)
240 {
241 if (!pipe->plane_state && !pipe->stream)
242 return true;
243
244 return false;
245 }
246
find_preferred_pipe_candidates(const struct dc_state * existing_state,const int pipe_count,const unsigned int stream_id,unsigned int * preferred_pipe_candidates)247 static unsigned int find_preferred_pipe_candidates(const struct dc_state *existing_state,
248 const int pipe_count,
249 const unsigned int stream_id,
250 unsigned int *preferred_pipe_candidates)
251 {
252 unsigned int num_preferred_candidates = 0;
253 int i;
254
255 /* There is only one case which we consider for adding a pipe to the preferred
256 * pipe candidate array:
257 *
258 * 1. If the existing stream id of the pipe is equivalent to the stream id
259 * of the stream we are trying to achieve MPC/ODM combine for. This allows
260 * us to minimize the changes in pipe topology during the transition.
261 *
262 * However this condition comes with a caveat. We need to ignore pipes that will
263 * require a change in OPP but still have the same stream id. For example during
264 * an MPC to ODM transiton.
265 *
266 * Adding check to avoid pipe select on the head pipe by utilizing dc resource
267 * helper function resource_get_primary_dpp_pipe and comparing the pipe index.
268 */
269 if (existing_state) {
270 for (i = 0; i < pipe_count; i++) {
271 if (existing_state->res_ctx.pipe_ctx[i].stream && existing_state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id) {
272 struct pipe_ctx *head_pipe =
273 resource_is_pipe_type(&existing_state->res_ctx.pipe_ctx[i], DPP_PIPE) ?
274 resource_get_primary_dpp_pipe(&existing_state->res_ctx.pipe_ctx[i]) :
275 NULL;
276
277 // we should always respect the head pipe from selection
278 if (head_pipe && head_pipe->pipe_idx == i)
279 continue;
280 if (existing_state->res_ctx.pipe_ctx[i].plane_res.hubp &&
281 existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i &&
282 (existing_state->res_ctx.pipe_ctx[i].prev_odm_pipe ||
283 existing_state->res_ctx.pipe_ctx[i].next_odm_pipe))
284 continue;
285
286 preferred_pipe_candidates[num_preferred_candidates++] = i;
287 }
288 }
289 }
290
291 return num_preferred_candidates;
292 }
293
find_last_resort_pipe_candidates(const struct dc_state * existing_state,const int pipe_count,const unsigned int stream_id,unsigned int * last_resort_pipe_candidates)294 static unsigned int find_last_resort_pipe_candidates(const struct dc_state *existing_state,
295 const int pipe_count,
296 const unsigned int stream_id,
297 unsigned int *last_resort_pipe_candidates)
298 {
299 (void)stream_id;
300 unsigned int num_last_resort_candidates = 0;
301 int i;
302
303 /* There are two cases where we would like to add a given pipe into the last
304 * candidate array:
305 *
306 * 1. If the pipe requires a change in OPP, for example during an MPC
307 * to ODM transiton.
308 *
309 * 2. If the pipe already has an enabled OTG.
310 */
311 if (existing_state) {
312 for (i = 0; i < pipe_count; i++) {
313 struct pipe_ctx *head_pipe =
314 resource_is_pipe_type(&existing_state->res_ctx.pipe_ctx[i], DPP_PIPE) ?
315 resource_get_primary_dpp_pipe(&existing_state->res_ctx.pipe_ctx[i]) :
316 NULL;
317
318 // we should always respect the head pipe from selection
319 if (head_pipe && head_pipe->pipe_idx == i)
320 continue;
321 if ((existing_state->res_ctx.pipe_ctx[i].plane_res.hubp &&
322 existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i) ||
323 existing_state->res_ctx.pipe_ctx[i].stream_res.tg)
324 last_resort_pipe_candidates[num_last_resort_candidates++] = i;
325 }
326 }
327
328 return num_last_resort_candidates;
329 }
330
is_pipe_in_candidate_array(const unsigned int pipe_idx,const unsigned int * candidate_array,const unsigned int candidate_array_size)331 static bool is_pipe_in_candidate_array(const unsigned int pipe_idx,
332 const unsigned int *candidate_array,
333 const unsigned int candidate_array_size)
334 {
335 int i;
336
337 for (i = 0; i < candidate_array_size; i++) {
338 if (candidate_array[i] == pipe_idx)
339 return true;
340 }
341
342 return false;
343 }
344
find_more_pipes_for_stream(struct dml2_context * ctx,struct dc_state * state,unsigned int stream_id,unsigned int * assigned_pipes,unsigned int * assigned_pipe_count,int pipes_needed,const struct dc_state * existing_state)345 static bool find_more_pipes_for_stream(struct dml2_context *ctx,
346 struct dc_state *state, // The state we want to find a free mapping in
347 unsigned int stream_id, // The stream we want this pipe to drive
348 unsigned int *assigned_pipes,
349 unsigned int *assigned_pipe_count,
350 int pipes_needed,
351 const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to
352 {
353 struct pipe_ctx *pipe = NULL;
354 unsigned int preferred_pipe_candidates[MAX_PIPES] = {0};
355 unsigned int last_resort_pipe_candidates[MAX_PIPES] = {0};
356 unsigned int num_preferred_candidates = 0;
357 unsigned int num_last_resort_candidates = 0;
358 int i;
359
360 if (existing_state) {
361 num_preferred_candidates =
362 find_preferred_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, preferred_pipe_candidates);
363
364 num_last_resort_candidates =
365 find_last_resort_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, last_resort_pipe_candidates);
366 }
367
368 // First see if any of the preferred are unmapped, and choose those instead
369 for (i = 0; pipes_needed > 0 && i < num_preferred_candidates; i++) {
370 pipe = &state->res_ctx.pipe_ctx[preferred_pipe_candidates[i]];
371 if (!is_plane_using_pipe(pipe)) {
372 pipes_needed--;
373 // TODO: This doens't make sense really, pipe_idx should always be valid
374 ASSERT(preferred_pipe_candidates[i] <= 0xFF);
375 pipe->pipe_idx = (uint8_t)preferred_pipe_candidates[i];
376 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
377 }
378 }
379
380 // We like to pair pipes starting from the higher order indicies for combining
381 for (i = ctx->config.dcn_pipe_count - 1; pipes_needed > 0 && i >= 0; i--) {
382 // Ignore any pipes that are the preferred or last resort candidate
383 if (is_pipe_in_candidate_array(i, preferred_pipe_candidates, num_preferred_candidates) ||
384 is_pipe_in_candidate_array(i, last_resort_pipe_candidates, num_last_resort_candidates))
385 continue;
386
387 pipe = &state->res_ctx.pipe_ctx[i];
388 if (!is_plane_using_pipe(pipe)) {
389 pipes_needed--;
390 // TODO: This doens't make sense really, pipe_idx should always be valid
391 ASSERT(i >= 0 && i <= 0xFF);
392 pipe->pipe_idx = (uint8_t)i;
393 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
394 }
395 }
396
397 // Only use the last resort pipe candidates as a last resort
398 for (i = 0; pipes_needed > 0 && i < num_last_resort_candidates; i++) {
399 pipe = &state->res_ctx.pipe_ctx[last_resort_pipe_candidates[i]];
400 if (!is_plane_using_pipe(pipe)) {
401 pipes_needed--;
402 // TODO: This doens't make sense really, pipe_idx should always be valid
403 ASSERT(last_resort_pipe_candidates[i] <= 0xFF);
404 pipe->pipe_idx = (uint8_t)last_resort_pipe_candidates[i];
405 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
406 }
407 }
408
409 ASSERT(pipes_needed <= 0); // Validation should prevent us from building a pipe context that exceeds the number of HW resoruces available
410
411 return pipes_needed <= 0;
412 }
413
find_more_free_pipes(struct dml2_context * ctx,struct dc_state * state,unsigned int stream_id,unsigned int * assigned_pipes,unsigned int * assigned_pipe_count,int pipes_needed,const struct dc_state * existing_state)414 static bool find_more_free_pipes(struct dml2_context *ctx,
415 struct dc_state *state, // The state we want to find a free mapping in
416 unsigned int stream_id, // The stream we want this pipe to drive
417 unsigned int *assigned_pipes,
418 unsigned int *assigned_pipe_count,
419 int pipes_needed,
420 const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to
421 {
422 struct pipe_ctx *pipe = NULL;
423 unsigned int preferred_pipe_candidates[MAX_PIPES] = {0};
424 unsigned int last_resort_pipe_candidates[MAX_PIPES] = {0};
425 unsigned int num_preferred_candidates = 0;
426 unsigned int num_last_resort_candidates = 0;
427 int i;
428
429 if (existing_state) {
430 num_preferred_candidates =
431 find_preferred_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, preferred_pipe_candidates);
432
433 num_last_resort_candidates =
434 find_last_resort_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, last_resort_pipe_candidates);
435 }
436
437 // First see if any of the preferred are unmapped, and choose those instead
438 for (i = 0; pipes_needed > 0 && i < num_preferred_candidates; i++) {
439 pipe = &state->res_ctx.pipe_ctx[preferred_pipe_candidates[i]];
440 if (is_pipe_free(pipe)) {
441 pipes_needed--;
442 // TODO: This doens't make sense really, pipe_idx should always be valid
443 ASSERT(preferred_pipe_candidates[i] <= 0xFF);
444 pipe->pipe_idx = (uint8_t)preferred_pipe_candidates[i];
445 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
446 }
447 }
448
449 // We like to pair pipes starting from the higher order indicies for combining
450 for (i = ctx->config.dcn_pipe_count - 1; pipes_needed > 0 && i >= 0; i--) {
451 // Ignore any pipes that are the preferred or last resort candidate
452 if (is_pipe_in_candidate_array(i, preferred_pipe_candidates, num_preferred_candidates) ||
453 is_pipe_in_candidate_array(i, last_resort_pipe_candidates, num_last_resort_candidates))
454 continue;
455
456 pipe = &state->res_ctx.pipe_ctx[i];
457 if (is_pipe_free(pipe)) {
458 pipes_needed--;
459 // TODO: This doens't make sense really, pipe_idx should always be valid
460 ASSERT(i >= 0 && i <= 0xFF);
461 pipe->pipe_idx = (uint8_t)i;
462 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
463 }
464 }
465
466 // Only use the last resort pipe candidates as a last resort
467 for (i = 0; pipes_needed > 0 && i < num_last_resort_candidates; i++) {
468 pipe = &state->res_ctx.pipe_ctx[last_resort_pipe_candidates[i]];
469 if (is_pipe_free(pipe)) {
470 pipes_needed--;
471 // TODO: This doens't make sense really, pipe_idx should always be valid
472 ASSERT(last_resort_pipe_candidates[i] <= 0xFF);
473 pipe->pipe_idx = (uint8_t)last_resort_pipe_candidates[i];
474 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
475 }
476 }
477
478 ASSERT(pipes_needed == 0); // Validation should prevent us from building a pipe context that exceeds the number of HW resoruces available
479
480 return pipes_needed == 0;
481 }
482
sort_pipes_for_splitting(struct dc_plane_pipe_pool * pipes)483 static void sort_pipes_for_splitting(struct dc_plane_pipe_pool *pipes)
484 {
485 bool sorted, swapped;
486 unsigned int cur_index;
487 int odm_slice_index;
488
489 for (odm_slice_index = 0; odm_slice_index < pipes->num_pipes_assigned_to_plane_for_odm_combine; odm_slice_index++) {
490 // Sort each MPCC set
491 //Un-optimized bubble sort, but that's okay for array sizes <= 6
492
493 if (pipes->num_pipes_assigned_to_plane_for_mpcc_combine <= 1)
494 sorted = true;
495 else
496 sorted = false;
497
498 cur_index = 0;
499 swapped = false;
500 while (!sorted) {
501 if (pipes->pipes_assigned_to_plane[odm_slice_index][cur_index] > pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1]) {
502 swap(pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1],
503 pipes->pipes_assigned_to_plane[odm_slice_index][cur_index]);
504
505 swapped = true;
506 }
507
508 cur_index++;
509
510 if (cur_index == pipes->num_pipes_assigned_to_plane_for_mpcc_combine - 1) {
511 cur_index = 0;
512
513 if (swapped)
514 sorted = false;
515 else
516 sorted = true;
517
518 swapped = false;
519 }
520
521 }
522 }
523 }
524
525 // For example, 3840 x 2160, ODM2:1 has a slice array of [1919, 3839], meaning, slice0 spans h_pixels 0->1919, and slice1 spans 1920->3840
calculate_odm_slices(const struct dc_stream_state * stream,unsigned int odm_factor,unsigned int * odm_slice_end_x)526 static void calculate_odm_slices(const struct dc_stream_state *stream, unsigned int odm_factor, unsigned int *odm_slice_end_x)
527 {
528 unsigned int slice_size = 0;
529 int i;
530
531 if (odm_factor < 1 || odm_factor > 4) {
532 ASSERT(false);
533 return;
534 }
535
536 slice_size = stream->src.width / odm_factor;
537
538 for (i = 0; i < odm_factor; i++)
539 odm_slice_end_x[i] = (slice_size * (i + 1)) - 1;
540
541 odm_slice_end_x[odm_factor - 1] = stream->src.width - 1;
542 }
543
add_odm_slice_to_odm_tree(struct dml2_context * ctx,struct dc_state * state,struct dc_pipe_mapping_scratch * scratch,unsigned int odm_slice_index)544 static void add_odm_slice_to_odm_tree(struct dml2_context *ctx,
545 struct dc_state *state,
546 struct dc_pipe_mapping_scratch *scratch,
547 unsigned int odm_slice_index)
548 {
549 (void)ctx;
550 struct pipe_ctx *pipe = NULL;
551 int i;
552
553 // MPCC Combine + ODM Combine is not supported, so there should never be a case where the current plane
554 // has more than 1 pipe mapped to it for a given slice.
555 ASSERT(scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine == 1 || scratch->pipe_pool.num_pipes_assigned_to_plane_for_odm_combine == 1);
556
557 for (i = 0; i < scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
558 pipe = &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][i]];
559
560 if (scratch->mpc_info.prev_odm_pipe)
561 scratch->mpc_info.prev_odm_pipe->next_odm_pipe = pipe;
562
563 pipe->prev_odm_pipe = scratch->mpc_info.prev_odm_pipe;
564 pipe->next_odm_pipe = NULL;
565 }
566 scratch->mpc_info.prev_odm_pipe = pipe;
567 }
568
add_plane_to_blend_tree(struct dml2_context * ctx,struct dc_state * state,const struct dc_plane_state * plane,struct dc_plane_pipe_pool * pipe_pool,unsigned int odm_slice,struct pipe_ctx * top_pipe)569 static struct pipe_ctx *add_plane_to_blend_tree(struct dml2_context *ctx,
570 struct dc_state *state,
571 const struct dc_plane_state *plane,
572 struct dc_plane_pipe_pool *pipe_pool,
573 unsigned int odm_slice,
574 struct pipe_ctx *top_pipe)
575 {
576 (void)ctx;
577 (void)plane;
578 int i;
579
580 for (i = 0; i < pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
581 if (top_pipe)
582 top_pipe->bottom_pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]];
583
584 pipe_pool->pipe_used[odm_slice][i] = true;
585
586 state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]].top_pipe = top_pipe;
587 state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]].bottom_pipe = NULL;
588
589 top_pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]];
590 }
591
592 // After running the above loop, the top pipe actually ends up pointing to the bottom of this MPCC combine tree, so we are actually
593 // returning the bottom pipe here
594 return top_pipe;
595 }
596
find_pipes_assigned_to_stream(struct dml2_context * ctx,struct dc_state * state,unsigned int stream_id,unsigned int * pipes)597 static unsigned int find_pipes_assigned_to_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int stream_id, unsigned int *pipes)
598 {
599 int i;
600 unsigned int num_found = 0;
601
602 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
603 struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
604
605 if (pipe->stream && pipe->stream->stream_id == stream_id && !pipe->top_pipe && !pipe->prev_odm_pipe) {
606 while (pipe) {
607 pipes[num_found++] = pipe->pipe_idx;
608 pipe = pipe->next_odm_pipe;
609 }
610 break;
611 }
612 }
613
614 return num_found;
615 }
616
assign_pipes_to_stream(struct dml2_context * ctx,struct dc_state * state,const struct dc_stream_state * stream,int odm_factor,struct dc_plane_pipe_pool * pipe_pool,const struct dc_state * existing_state)617 static struct pipe_ctx *assign_pipes_to_stream(struct dml2_context *ctx, struct dc_state *state,
618 const struct dc_stream_state *stream,
619 int odm_factor,
620 struct dc_plane_pipe_pool *pipe_pool,
621 const struct dc_state *existing_state)
622 {
623 struct pipe_ctx *master_pipe;
624 unsigned int pipes_needed;
625 unsigned int pipes_assigned;
626 unsigned int pipes[MAX_PIPES] = {0};
627 unsigned int next_pipe_to_assign;
628 int odm_slice;
629
630 pipes_needed = odm_factor;
631
632 master_pipe = find_master_pipe_of_stream(ctx, state, stream->stream_id);
633 ASSERT(master_pipe);
634
635 pipes_assigned = find_pipes_assigned_to_stream(ctx, state, stream->stream_id, pipes);
636
637 find_more_free_pipes(ctx, state, stream->stream_id, pipes, &pipes_assigned, pipes_needed - pipes_assigned, existing_state);
638
639 ASSERT(pipes_assigned == pipes_needed);
640
641 next_pipe_to_assign = 0;
642 for (odm_slice = 0; odm_slice < odm_factor; odm_slice++)
643 pipe_pool->pipes_assigned_to_plane[odm_slice][0] = pipes[next_pipe_to_assign++];
644
645 pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine = 1;
646 pipe_pool->num_pipes_assigned_to_plane_for_odm_combine = odm_factor;
647
648 return master_pipe;
649 }
650
assign_pipes_to_plane(struct dml2_context * ctx,struct dc_state * state,const struct dc_stream_state * stream,const struct dc_plane_state * plane,int odm_factor,int mpc_factor,int plane_index,struct dc_plane_pipe_pool * pipe_pool,const struct dc_state * existing_state)651 static struct pipe_ctx *assign_pipes_to_plane(struct dml2_context *ctx, struct dc_state *state,
652 const struct dc_stream_state *stream,
653 const struct dc_plane_state *plane,
654 int odm_factor,
655 int mpc_factor,
656 int plane_index,
657 struct dc_plane_pipe_pool *pipe_pool,
658 const struct dc_state *existing_state)
659 {
660 struct pipe_ctx *master_pipe = NULL;
661 unsigned int plane_id;
662 unsigned int pipes_needed;
663 unsigned int pipes_assigned;
664 unsigned int pipes[MAX_PIPES] = {0};
665 unsigned int next_pipe_to_assign;
666 int odm_slice, mpc_slice;
667
668 if (!get_plane_id(ctx, state, plane, stream->stream_id, plane_index, &plane_id)) {
669 ASSERT(false);
670 return master_pipe;
671 }
672
673 pipes_needed = mpc_factor * odm_factor;
674
675 master_pipe = find_master_pipe_of_plane(ctx, state, plane_id);
676 ASSERT(master_pipe);
677
678 pipes_assigned = find_pipes_assigned_to_plane(ctx, state, plane_id, pipes);
679
680 find_more_pipes_for_stream(ctx, state, stream->stream_id, pipes, &pipes_assigned, pipes_needed - pipes_assigned, existing_state);
681
682 ASSERT(pipes_assigned >= pipes_needed);
683
684 next_pipe_to_assign = 0;
685 for (odm_slice = 0; odm_slice < odm_factor; odm_slice++)
686 for (mpc_slice = 0; mpc_slice < mpc_factor; mpc_slice++)
687 pipe_pool->pipes_assigned_to_plane[odm_slice][mpc_slice] = pipes[next_pipe_to_assign++];
688
689 pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine = mpc_factor;
690 pipe_pool->num_pipes_assigned_to_plane_for_odm_combine = odm_factor;
691
692 return master_pipe;
693 }
694
is_pipe_used(const struct dc_plane_pipe_pool * pool,unsigned int pipe_idx)695 static bool is_pipe_used(const struct dc_plane_pipe_pool *pool, unsigned int pipe_idx)
696 {
697 int i, j;
698
699 for (i = 0; i < pool->num_pipes_assigned_to_plane_for_odm_combine; i++) {
700 for (j = 0; j < pool->num_pipes_assigned_to_plane_for_mpcc_combine; j++) {
701 if (pool->pipes_assigned_to_plane[i][j] == pipe_idx && pool->pipe_used[i][j])
702 return true;
703 }
704 }
705
706 return false;
707 }
708
free_pipe(struct pipe_ctx * pipe)709 static void free_pipe(struct pipe_ctx *pipe)
710 {
711 memset(pipe, 0, sizeof(struct pipe_ctx));
712 }
713
free_unused_pipes_for_plane(struct dml2_context * ctx,struct dc_state * state,const struct dc_plane_state * plane,const struct dc_plane_pipe_pool * pool,unsigned int stream_id,int plane_index)714 static void free_unused_pipes_for_plane(struct dml2_context *ctx, struct dc_state *state,
715 const struct dc_plane_state *plane, const struct dc_plane_pipe_pool *pool, unsigned int stream_id, int plane_index)
716 {
717 int i;
718 bool is_plane_duplicate = ctx->v20.scratch.plane_duplicate_exists;
719
720 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
721 if (state->res_ctx.pipe_ctx[i].plane_state == plane &&
722 state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id &&
723 (!is_plane_duplicate ||
724 ctx->v20.scratch.dml_to_dc_pipe_mapping.dml_pipe_idx_to_plane_index[state->res_ctx.pipe_ctx[i].pipe_idx] == plane_index) &&
725 !is_pipe_used(pool, state->res_ctx.pipe_ctx[i].pipe_idx)) {
726 free_pipe(&state->res_ctx.pipe_ctx[i]);
727 }
728 }
729 }
730
remove_pipes_from_blend_trees(struct dml2_context * ctx,struct dc_state * state,struct dc_plane_pipe_pool * pipe_pool,unsigned int odm_slice)731 static void remove_pipes_from_blend_trees(struct dml2_context *ctx, struct dc_state *state, struct dc_plane_pipe_pool *pipe_pool, unsigned int odm_slice)
732 {
733 (void)ctx;
734 struct pipe_ctx *pipe;
735 int i;
736
737 for (i = 0; i < pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
738 pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][0]];
739 if (pipe->top_pipe)
740 pipe->top_pipe->bottom_pipe = pipe->bottom_pipe;
741
742 if (pipe->bottom_pipe)
743 pipe->bottom_pipe = pipe->top_pipe;
744
745 pipe_pool->pipe_used[odm_slice][i] = true;
746 }
747 }
748
map_pipes_for_stream(struct dml2_context * ctx,struct dc_state * state,const struct dc_stream_state * stream,struct dc_pipe_mapping_scratch * scratch,const struct dc_state * existing_state)749 static void map_pipes_for_stream(struct dml2_context *ctx, struct dc_state *state, const struct dc_stream_state *stream,
750 struct dc_pipe_mapping_scratch *scratch, const struct dc_state *existing_state)
751 {
752 int odm_slice_index;
753 struct pipe_ctx *master_pipe = NULL;
754
755
756 master_pipe = assign_pipes_to_stream(ctx, state, stream, scratch->odm_info.odm_factor, &scratch->pipe_pool, existing_state);
757 sort_pipes_for_splitting(&scratch->pipe_pool);
758
759 for (odm_slice_index = 0; odm_slice_index < scratch->odm_info.odm_factor; odm_slice_index++) {
760 remove_pipes_from_blend_trees(ctx, state, &scratch->pipe_pool, odm_slice_index);
761
762 add_odm_slice_to_odm_tree(ctx, state, scratch, odm_slice_index);
763
764 ctx->config.callbacks.acquire_secondary_pipe_for_mpc_odm(ctx->config.callbacks.dc, state,
765 master_pipe, &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][0]], true);
766 }
767 }
768
map_pipes_for_plane(struct dml2_context * ctx,struct dc_state * state,const struct dc_stream_state * stream,const struct dc_plane_state * plane,int plane_index,struct dc_pipe_mapping_scratch * scratch,const struct dc_state * existing_state)769 static void map_pipes_for_plane(struct dml2_context *ctx, struct dc_state *state, const struct dc_stream_state *stream, const struct dc_plane_state *plane,
770 int plane_index, struct dc_pipe_mapping_scratch *scratch, const struct dc_state *existing_state)
771 {
772 int odm_slice_index;
773 unsigned int plane_id;
774 struct pipe_ctx *master_pipe = NULL;
775 int i;
776
777 if (!get_plane_id(ctx, state, plane, stream->stream_id, plane_index, &plane_id)) {
778 ASSERT(false);
779 return;
780 }
781
782 master_pipe = assign_pipes_to_plane(ctx, state, stream, plane, scratch->odm_info.odm_factor,
783 scratch->mpc_info.mpc_factor, plane_index, &scratch->pipe_pool, existing_state);
784 sort_pipes_for_splitting(&scratch->pipe_pool);
785
786 for (odm_slice_index = 0; odm_slice_index < scratch->odm_info.odm_factor; odm_slice_index++) {
787 // Now we have a list of all pipes to be used for this plane/stream, now setup the tree.
788 scratch->odm_info.next_higher_pipe_for_odm_slice[odm_slice_index] = add_plane_to_blend_tree(ctx, state,
789 plane,
790 &scratch->pipe_pool,
791 odm_slice_index,
792 scratch->odm_info.next_higher_pipe_for_odm_slice[odm_slice_index]);
793
794 add_odm_slice_to_odm_tree(ctx, state, scratch, odm_slice_index);
795
796 for (i = 0; i < scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
797
798 ctx->config.callbacks.acquire_secondary_pipe_for_mpc_odm(ctx->config.callbacks.dc, state,
799 master_pipe, &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][i]], true);
800 }
801 }
802
803 free_unused_pipes_for_plane(ctx, state, plane, &scratch->pipe_pool, stream->stream_id, plane_index);
804 }
805
get_target_mpc_factor(struct dml2_context * ctx,struct dc_state * state,const struct dml_display_cfg_st * disp_cfg,struct dml2_dml_to_dc_pipe_mapping * mapping,const struct dc_stream_status * status,const struct dc_stream_state * stream,int plane_idx)806 static unsigned int get_target_mpc_factor(struct dml2_context *ctx,
807 struct dc_state *state,
808 const struct dml_display_cfg_st *disp_cfg,
809 struct dml2_dml_to_dc_pipe_mapping *mapping,
810 const struct dc_stream_status *status,
811 const struct dc_stream_state *stream,
812 int plane_idx)
813 {
814 unsigned int plane_id;
815 unsigned int cfg_idx;
816 unsigned int mpc_factor;
817
818 if (ctx->architecture == dml2_architecture_20) {
819 get_plane_id(ctx, state, status->plane_states[plane_idx],
820 stream->stream_id, plane_idx, &plane_id);
821 cfg_idx = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
822 mpc_factor = (unsigned int)disp_cfg->hw.DPPPerSurface[cfg_idx];
823 } else if (ctx->architecture == dml2_architecture_21) {
824 if (ctx->config.svp_pstate.callbacks.get_stream_subvp_type(state, stream) == SUBVP_PHANTOM) {
825 struct dc_stream_state *main_stream;
826 struct dc_stream_status *main_stream_status;
827
828 /* get stream id of main stream */
829 main_stream = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(state, stream);
830 if (!main_stream) {
831 ASSERT(false);
832 return 1;
833 }
834
835 main_stream_status = ctx->config.callbacks.get_stream_status(state, main_stream);
836 if (!main_stream_status) {
837 ASSERT(false);
838 return 1;
839 }
840
841 /* get plane id for associated main plane */
842 get_plane_id(ctx, state, main_stream_status->plane_states[plane_idx],
843 main_stream->stream_id, plane_idx, &plane_id);
844 } else {
845 get_plane_id(ctx, state, status->plane_states[plane_idx],
846 stream->stream_id, plane_idx, &plane_id);
847 }
848
849 cfg_idx = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
850 mpc_factor = ctx->v21.mode_programming.programming->plane_programming[cfg_idx].num_dpps_required;
851 } else {
852 mpc_factor = 1;
853 ASSERT(false);
854 }
855
856 /* For stereo timings, we need to pipe split */
857 if (dml2_is_stereo_timing(stream))
858 mpc_factor = 2;
859
860 return mpc_factor;
861 }
862
get_target_odm_factor(const struct dml2_context * ctx,struct dc_state * state,const struct dml_display_cfg_st * disp_cfg,struct dml2_dml_to_dc_pipe_mapping * mapping,const struct dc_stream_state * stream)863 static unsigned int get_target_odm_factor(
864 const struct dml2_context *ctx,
865 struct dc_state *state,
866 const struct dml_display_cfg_st *disp_cfg,
867 struct dml2_dml_to_dc_pipe_mapping *mapping,
868 const struct dc_stream_state *stream)
869 {
870 unsigned int cfg_idx;
871
872 if (ctx->architecture == dml2_architecture_20) {
873 cfg_idx = find_disp_cfg_idx_by_stream_id(
874 mapping, stream->stream_id);
875 switch (disp_cfg->hw.ODMMode[cfg_idx]) {
876 case dml_odm_mode_bypass:
877 return 1;
878 case dml_odm_mode_combine_2to1:
879 return 2;
880 case dml_odm_mode_combine_4to1:
881 return 4;
882 default:
883 break;
884 }
885 } else if (ctx->architecture == dml2_architecture_21) {
886 if (ctx->config.svp_pstate.callbacks.get_stream_subvp_type(state, stream) == SUBVP_PHANTOM) {
887 struct dc_stream_state *main_stream;
888
889 /* get stream id of main stream */
890 main_stream = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(state, stream);
891 if (!main_stream)
892 goto failed;
893
894 /* get cfg idx for associated main stream */
895 cfg_idx = find_disp_cfg_idx_by_stream_id(
896 mapping, main_stream->stream_id);
897 } else {
898 cfg_idx = find_disp_cfg_idx_by_stream_id(
899 mapping, stream->stream_id);
900 }
901
902 return ctx->v21.mode_programming.programming->stream_programming[cfg_idx].num_odms_required;
903 }
904
905 failed:
906 ASSERT(false);
907 return 1;
908 }
909
get_source_odm_factor(const struct dml2_context * ctx,struct dc_state * state,const struct dc_stream_state * stream)910 static unsigned int get_source_odm_factor(const struct dml2_context *ctx,
911 struct dc_state *state,
912 const struct dc_stream_state *stream)
913 {
914 struct pipe_ctx *otg_master = ctx->config.callbacks.get_otg_master_for_stream(&state->res_ctx, stream);
915
916 if (!otg_master)
917 return 0;
918
919 return ctx->config.callbacks.get_odm_slice_count(otg_master);
920 }
921
get_source_mpc_factor(const struct dml2_context * ctx,struct dc_state * state,const struct dc_plane_state * plane)922 static unsigned int get_source_mpc_factor(const struct dml2_context *ctx,
923 struct dc_state *state,
924 const struct dc_plane_state *plane)
925 {
926 struct pipe_ctx *dpp_pipes[MAX_PIPES] = {0};
927
928 if (ctx->config.callbacks.get_dpp_pipes_for_plane(plane, &state->res_ctx, dpp_pipes) <= 0)
929 ASSERT(false);
930
931 return ctx->config.callbacks.get_mpc_slice_count(dpp_pipes[0]);
932 }
933
934
populate_mpc_factors_for_stream(struct dml2_context * ctx,const struct dml_display_cfg_st * disp_cfg,struct dml2_dml_to_dc_pipe_mapping * mapping,struct dc_state * state,unsigned int stream_idx,struct dml2_pipe_combine_factor odm_factor,struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])935 static void populate_mpc_factors_for_stream(
936 struct dml2_context *ctx,
937 const struct dml_display_cfg_st *disp_cfg,
938 struct dml2_dml_to_dc_pipe_mapping *mapping,
939 struct dc_state *state,
940 unsigned int stream_idx,
941 struct dml2_pipe_combine_factor odm_factor,
942 struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])
943 {
944 const struct dc_stream_status *status = &state->stream_status[stream_idx];
945 int i;
946
947 for (i = 0; i < status->plane_count; i++) {
948 mpc_factors[i].source = get_source_mpc_factor(ctx, state, status->plane_states[i]);
949 mpc_factors[i].target = (odm_factor.target == 1) ?
950 get_target_mpc_factor(ctx, state, disp_cfg, mapping, status, state->streams[stream_idx], i) : 1;
951 }
952 }
953
populate_odm_factors(const struct dml2_context * ctx,const struct dml_display_cfg_st * disp_cfg,struct dml2_dml_to_dc_pipe_mapping * mapping,struct dc_state * state,struct dml2_pipe_combine_factor odm_factors[MAX_PIPES])954 static void populate_odm_factors(const struct dml2_context *ctx,
955 const struct dml_display_cfg_st *disp_cfg,
956 struct dml2_dml_to_dc_pipe_mapping *mapping,
957 struct dc_state *state,
958 struct dml2_pipe_combine_factor odm_factors[MAX_PIPES])
959 {
960 int i;
961
962 for (i = 0; i < state->stream_count; i++) {
963 odm_factors[i].source = get_source_odm_factor(ctx, state, state->streams[i]);
964 odm_factors[i].target = get_target_odm_factor(
965 ctx, state, disp_cfg, mapping, state->streams[i]);
966 }
967 }
968
unmap_dc_pipes_for_stream(struct dml2_context * ctx,struct dc_state * state,const struct dc_state * existing_state,const struct dc_stream_state * stream,const struct dc_stream_status * status,struct dml2_pipe_combine_factor odm_factor,struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])969 static bool unmap_dc_pipes_for_stream(struct dml2_context *ctx,
970 struct dc_state *state,
971 const struct dc_state *existing_state,
972 const struct dc_stream_state *stream,
973 const struct dc_stream_status *status,
974 struct dml2_pipe_combine_factor odm_factor,
975 struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])
976 {
977 int plane_idx;
978 bool result = true;
979
980 for (plane_idx = 0; plane_idx < status->plane_count; plane_idx++)
981 if (mpc_factors[plane_idx].target < mpc_factors[plane_idx].source)
982 result &= ctx->config.callbacks.update_pipes_for_plane_with_slice_count(
983 state,
984 existing_state,
985 ctx->config.callbacks.dc->res_pool,
986 status->plane_states[plane_idx],
987 mpc_factors[plane_idx].target);
988 if (odm_factor.target < odm_factor.source)
989 result &= ctx->config.callbacks.update_pipes_for_stream_with_slice_count(
990 state,
991 existing_state,
992 ctx->config.callbacks.dc->res_pool,
993 stream,
994 odm_factor.target);
995 return result;
996 }
997
map_dc_pipes_for_stream(struct dml2_context * ctx,struct dc_state * state,const struct dc_state * existing_state,const struct dc_stream_state * stream,const struct dc_stream_status * status,struct dml2_pipe_combine_factor odm_factor,struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])998 static bool map_dc_pipes_for_stream(struct dml2_context *ctx,
999 struct dc_state *state,
1000 const struct dc_state *existing_state,
1001 const struct dc_stream_state *stream,
1002 const struct dc_stream_status *status,
1003 struct dml2_pipe_combine_factor odm_factor,
1004 struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])
1005 {
1006 int plane_idx;
1007 bool result = true;
1008
1009 for (plane_idx = 0; plane_idx < status->plane_count; plane_idx++)
1010 if (mpc_factors[plane_idx].target > mpc_factors[plane_idx].source)
1011 result &= ctx->config.callbacks.update_pipes_for_plane_with_slice_count(
1012 state,
1013 existing_state,
1014 ctx->config.callbacks.dc->res_pool,
1015 status->plane_states[plane_idx],
1016 mpc_factors[plane_idx].target);
1017 if (odm_factor.target > odm_factor.source)
1018 result &= ctx->config.callbacks.update_pipes_for_stream_with_slice_count(
1019 state,
1020 existing_state,
1021 ctx->config.callbacks.dc->res_pool,
1022 stream,
1023 odm_factor.target);
1024 return result;
1025 }
1026
map_dc_pipes_with_callbacks(struct dml2_context * ctx,struct dc_state * state,const struct dml_display_cfg_st * disp_cfg,struct dml2_dml_to_dc_pipe_mapping * mapping,const struct dc_state * existing_state)1027 static bool map_dc_pipes_with_callbacks(struct dml2_context *ctx,
1028 struct dc_state *state,
1029 const struct dml_display_cfg_st *disp_cfg,
1030 struct dml2_dml_to_dc_pipe_mapping *mapping,
1031 const struct dc_state *existing_state)
1032 {
1033 int i;
1034 bool result = true;
1035
1036 populate_odm_factors(ctx, disp_cfg, mapping, state, ctx->pipe_combine_scratch.odm_factors);
1037 for (i = 0; i < state->stream_count; i++)
1038 populate_mpc_factors_for_stream(ctx, disp_cfg, mapping, state,
1039 i, ctx->pipe_combine_scratch.odm_factors[i], ctx->pipe_combine_scratch.mpc_factors[i]);
1040 for (i = 0; i < state->stream_count; i++)
1041 result &= unmap_dc_pipes_for_stream(ctx, state, existing_state, state->streams[i],
1042 &state->stream_status[i], ctx->pipe_combine_scratch.odm_factors[i], ctx->pipe_combine_scratch.mpc_factors[i]);
1043 for (i = 0; i < state->stream_count; i++)
1044 result &= map_dc_pipes_for_stream(ctx, state, existing_state, state->streams[i],
1045 &state->stream_status[i], ctx->pipe_combine_scratch.odm_factors[i], ctx->pipe_combine_scratch.mpc_factors[i]);
1046
1047 return result;
1048 }
1049
dml2_map_dc_pipes(struct dml2_context * ctx,struct dc_state * state,const struct dml_display_cfg_st * disp_cfg,struct dml2_dml_to_dc_pipe_mapping * mapping,const struct dc_state * existing_state)1050 bool dml2_map_dc_pipes(struct dml2_context *ctx, struct dc_state *state, const struct dml_display_cfg_st *disp_cfg, struct dml2_dml_to_dc_pipe_mapping *mapping, const struct dc_state *existing_state)
1051 {
1052 int stream_index, plane_index, i;
1053
1054 unsigned int stream_disp_cfg_index;
1055 unsigned int plane_disp_cfg_index;
1056 unsigned int disp_cfg_index_max;
1057
1058 unsigned int plane_id;
1059 unsigned int stream_id;
1060
1061 const unsigned int *ODMMode, *DPPPerSurface;
1062 unsigned int odm_mode_array[__DML2_WRAPPER_MAX_STREAMS_PLANES__] = {0}, dpp_per_surface_array[__DML2_WRAPPER_MAX_STREAMS_PLANES__] = {0};
1063 struct dc_pipe_mapping_scratch scratch;
1064
1065 if (ctx->config.map_dc_pipes_with_callbacks)
1066 return map_dc_pipes_with_callbacks(
1067 ctx, state, disp_cfg, mapping, existing_state);
1068
1069 if (ctx->architecture == dml2_architecture_21) {
1070 /*
1071 * Extract ODM and DPP outputs from DML2.1 and map them in an array as required for pipe mapping in dml2_map_dc_pipes.
1072 * As data cannot be directly extracted in const pointers, assign these arrays to const pointers before proceeding to
1073 * maximize the reuse of existing code. Const pointers are required because dml2.0 dml_display_cfg_st is const.
1074 *
1075 */
1076 for (i = 0; i < __DML2_WRAPPER_MAX_STREAMS_PLANES__; i++) {
1077 odm_mode_array[i] = ctx->v21.mode_programming.programming->stream_programming[i].num_odms_required;
1078 dpp_per_surface_array[i] = ctx->v21.mode_programming.programming->plane_programming[i].num_dpps_required;
1079 }
1080
1081 ODMMode = (const unsigned int *)odm_mode_array;
1082 DPPPerSurface = (const unsigned int *)dpp_per_surface_array;
1083 disp_cfg_index_max = __DML2_WRAPPER_MAX_STREAMS_PLANES__;
1084 } else {
1085 ODMMode = (unsigned int *)disp_cfg->hw.ODMMode;
1086 DPPPerSurface = disp_cfg->hw.DPPPerSurface;
1087 disp_cfg_index_max = __DML_NUM_PLANES__;
1088 }
1089
1090 for (stream_index = 0; stream_index < state->stream_count; stream_index++) {
1091 memset(&scratch, 0, sizeof(struct dc_pipe_mapping_scratch));
1092
1093 stream_id = state->streams[stream_index]->stream_id;
1094 stream_disp_cfg_index = find_disp_cfg_idx_by_stream_id(mapping, stream_id);
1095 if (stream_disp_cfg_index >= disp_cfg_index_max)
1096 continue;
1097
1098 if (ctx->architecture == dml2_architecture_20) {
1099 if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_bypass) {
1100 scratch.odm_info.odm_factor = 1;
1101 } else if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_combine_2to1) {
1102 scratch.odm_info.odm_factor = 2;
1103 } else if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_combine_4to1) {
1104 scratch.odm_info.odm_factor = 4;
1105 } else {
1106 ASSERT(false);
1107 scratch.odm_info.odm_factor = 1;
1108 }
1109 } else if (ctx->architecture == dml2_architecture_21) {
1110 /* After DML2.1 update, ODM interpretation needs to change and is no longer same as for DML2.0.
1111 * This is not an issue with new resource management logic. This block ensure backcompat
1112 * with legacy pipe management with updated DML.
1113 * */
1114 if (ODMMode[stream_disp_cfg_index] == 1) {
1115 scratch.odm_info.odm_factor = 1;
1116 } else if (ODMMode[stream_disp_cfg_index] == 2) {
1117 scratch.odm_info.odm_factor = 2;
1118 } else if (ODMMode[stream_disp_cfg_index] == 4) {
1119 scratch.odm_info.odm_factor = 4;
1120 } else {
1121 ASSERT(false);
1122 scratch.odm_info.odm_factor = 1;
1123 }
1124 }
1125 calculate_odm_slices(state->streams[stream_index], scratch.odm_info.odm_factor, scratch.odm_info.odm_slice_end_x);
1126
1127 // If there are no planes, you still want to setup ODM...
1128 if (state->stream_status[stream_index].plane_count == 0) {
1129 map_pipes_for_stream(ctx, state, state->streams[stream_index], &scratch, existing_state);
1130 }
1131
1132 for (plane_index = 0; plane_index < state->stream_status[stream_index].plane_count; plane_index++) {
1133 // Planes are ordered top to bottom.
1134 if (get_plane_id(ctx, state, state->stream_status[stream_index].plane_states[plane_index],
1135 stream_id, plane_index, &plane_id)) {
1136 plane_disp_cfg_index = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
1137
1138 // Setup mpc_info for this plane
1139 scratch.mpc_info.prev_odm_pipe = NULL;
1140 if (scratch.odm_info.odm_factor == 1 && plane_disp_cfg_index < disp_cfg_index_max) {
1141 // If ODM combine is not inuse, then the number of pipes
1142 // per plane is determined by MPC combine factor
1143 scratch.mpc_info.mpc_factor = DPPPerSurface[plane_disp_cfg_index];
1144
1145 //For stereo timings, we need to pipe split
1146 if (dml2_is_stereo_timing(state->streams[stream_index]))
1147 scratch.mpc_info.mpc_factor = 2;
1148 } else {
1149 // If ODM combine is enabled, then we use at most 1 pipe per
1150 // odm slice per plane, i.e. MPC combine is never used
1151 scratch.mpc_info.mpc_factor = 1;
1152 }
1153
1154 ASSERT(scratch.odm_info.odm_factor * scratch.mpc_info.mpc_factor > 0);
1155
1156 // Clear the pool assignment scratch (which is per plane)
1157 memset(&scratch.pipe_pool, 0, sizeof(struct dc_plane_pipe_pool));
1158
1159 map_pipes_for_plane(ctx, state, state->streams[stream_index],
1160 state->stream_status[stream_index].plane_states[plane_index], plane_index, &scratch, existing_state);
1161 } else {
1162 // Plane ID cannot be generated, therefore no DML mapping can be performed.
1163 ASSERT(false);
1164 }
1165 }
1166
1167 }
1168
1169 if (!validate_pipe_assignment(ctx, state, disp_cfg, mapping))
1170 ASSERT(false);
1171
1172 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
1173 struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
1174
1175 if (pipe->plane_state) {
1176 if (!ctx->config.callbacks.build_scaling_params(pipe)) {
1177 ASSERT(false);
1178 }
1179 }
1180
1181 if (ctx->config.callbacks.build_test_pattern_params &&
1182 pipe->stream &&
1183 pipe->prev_odm_pipe == NULL &&
1184 pipe->top_pipe == NULL)
1185 ctx->config.callbacks.build_test_pattern_params(&state->res_ctx, pipe);
1186 }
1187
1188 return true;
1189 }
1190