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 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 || (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 return -1;
92 }
93
find_disp_cfg_idx_by_stream_id(struct dml2_dml_to_dc_pipe_mapping * mapping,unsigned int stream_id)94 static int find_disp_cfg_idx_by_stream_id(struct dml2_dml_to_dc_pipe_mapping *mapping, unsigned int stream_id)
95 {
96 int i;
97
98 for (i = 0; i < __DML2_WRAPPER_MAX_STREAMS_PLANES__; i++) {
99 if (mapping->disp_cfg_to_stream_id_valid[i] && mapping->disp_cfg_to_stream_id[i] == stream_id)
100 return i;
101 }
102
103 return -1;
104 }
105
106 // 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)107 static struct pipe_ctx *find_master_pipe_of_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int stream_id)
108 {
109 int i;
110
111 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
112 if (state->res_ctx.pipe_ctx[i].stream && state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id) {
113 if (!state->res_ctx.pipe_ctx[i].prev_odm_pipe && !state->res_ctx.pipe_ctx[i].top_pipe)
114 return &state->res_ctx.pipe_ctx[i];
115 }
116 }
117
118 return NULL;
119 }
120
find_master_pipe_of_plane(struct dml2_context * ctx,struct dc_state * state,unsigned int plane_id)121 static struct pipe_ctx *find_master_pipe_of_plane(struct dml2_context *ctx,
122 struct dc_state *state, unsigned int plane_id)
123 {
124 int i;
125 unsigned int plane_id_assigned_to_pipe;
126
127 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
128 if (state->res_ctx.pipe_ctx[i].plane_state && get_plane_id(ctx, state, state->res_ctx.pipe_ctx[i].plane_state,
129 state->res_ctx.pipe_ctx[i].stream->stream_id,
130 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)) {
131 if (plane_id_assigned_to_pipe == plane_id)
132 return &state->res_ctx.pipe_ctx[i];
133 }
134 }
135
136 return NULL;
137 }
138
find_pipes_assigned_to_plane(struct dml2_context * ctx,struct dc_state * state,unsigned int plane_id,unsigned int * pipes)139 static unsigned int find_pipes_assigned_to_plane(struct dml2_context *ctx,
140 struct dc_state *state, unsigned int plane_id, unsigned int *pipes)
141 {
142 int i;
143 unsigned int num_found = 0;
144 unsigned int plane_id_assigned_to_pipe;
145
146 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
147 if (state->res_ctx.pipe_ctx[i].plane_state && get_plane_id(ctx, state, state->res_ctx.pipe_ctx[i].plane_state,
148 state->res_ctx.pipe_ctx[i].stream->stream_id,
149 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)) {
150 if (plane_id_assigned_to_pipe == plane_id)
151 pipes[num_found++] = i;
152 }
153 }
154
155 return num_found;
156 }
157
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)158 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)
159 {
160 // int i, j, k;
161 //
162 // unsigned int plane_id;
163 //
164 // unsigned int disp_cfg_index;
165 //
166 // unsigned int pipes_assigned_to_plane[MAX_PIPES];
167 // unsigned int num_pipes_assigned_to_plane;
168 //
169 // struct pipe_ctx *top_pipe;
170 //
171 // for (i = 0; i < state->stream_count; i++) {
172 // for (j = 0; j < state->stream_status[i]->plane_count; j++) {
173 // if (get_plane_id(state, state->stream_status.plane_states[j], &plane_id)) {
174 // disp_cfg_index = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
175 // num_pipes_assigned_to_plane = find_pipes_assigned_to_plane(ctx, state, plane_id, pipes_assigned_to_plane);
176 //
177 // if (disp_cfg_index >= 0 && num_pipes_assigned_to_plane > 0) {
178 // // Verify the number of pipes assigned matches
179 // if (disp_cfg->hw.DPPPerSurface != num_pipes_assigned_to_plane)
180 // return false;
181 //
182 // top_pipe = find_top_pipe_in_tree(state->res_ctx.pipe_ctx[pipes_assigned_to_plane[0]]);
183 //
184 // // Verify MPC and ODM combine
185 // if (disp_cfg->hw.ODMMode == dml_odm_mode_bypass) {
186 // verify_combine_tree(top_pipe, state->streams[i]->stream_id, plane_id, state, false);
187 // } else {
188 // verify_combine_tree(top_pipe, state->streams[i]->stream_id, plane_id, state, true);
189 // }
190 //
191 // // TODO: could also do additional verification that the pipes in tree are the same as
192 // // pipes_assigned_to_plane
193 // } else {
194 // ASSERT(false);
195 // return false;
196 // }
197 // } else {
198 // ASSERT(false);
199 // return false;
200 // }
201 // }
202 // }
203 return true;
204 }
205
is_plane_using_pipe(const struct pipe_ctx * pipe)206 static bool is_plane_using_pipe(const struct pipe_ctx *pipe)
207 {
208 if (pipe->plane_state)
209 return true;
210
211 return false;
212 }
213
is_pipe_free(const struct pipe_ctx * pipe)214 static bool is_pipe_free(const struct pipe_ctx *pipe)
215 {
216 if (!pipe->plane_state && !pipe->stream)
217 return true;
218
219 return false;
220 }
221
find_preferred_pipe_candidates(const struct dc_state * existing_state,const int pipe_count,const unsigned int stream_id,unsigned int * preferred_pipe_candidates)222 static unsigned int find_preferred_pipe_candidates(const struct dc_state *existing_state,
223 const int pipe_count,
224 const unsigned int stream_id,
225 unsigned int *preferred_pipe_candidates)
226 {
227 unsigned int num_preferred_candidates = 0;
228 int i;
229
230 /* There is only one case which we consider for adding a pipe to the preferred
231 * pipe candidate array:
232 *
233 * 1. If the existing stream id of the pipe is equivalent to the stream id
234 * of the stream we are trying to achieve MPC/ODM combine for. This allows
235 * us to minimize the changes in pipe topology during the transition.
236 *
237 * However this condition comes with a caveat. We need to ignore pipes that will
238 * require a change in OPP but still have the same stream id. For example during
239 * an MPC to ODM transiton.
240 */
241 if (existing_state) {
242 for (i = 0; i < pipe_count; i++) {
243 if (existing_state->res_ctx.pipe_ctx[i].stream && existing_state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id) {
244 if (existing_state->res_ctx.pipe_ctx[i].plane_res.hubp &&
245 existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i)
246 continue;
247
248 preferred_pipe_candidates[num_preferred_candidates++] = i;
249 }
250 }
251 }
252
253 return num_preferred_candidates;
254 }
255
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)256 static unsigned int find_last_resort_pipe_candidates(const struct dc_state *existing_state,
257 const int pipe_count,
258 const unsigned int stream_id,
259 unsigned int *last_resort_pipe_candidates)
260 {
261 unsigned int num_last_resort_candidates = 0;
262 int i;
263
264 /* There are two cases where we would like to add a given pipe into the last
265 * candidate array:
266 *
267 * 1. If the pipe requires a change in OPP, for example during an MPC
268 * to ODM transiton.
269 *
270 * 2. If the pipe already has an enabled OTG.
271 */
272 if (existing_state) {
273 for (i = 0; i < pipe_count; i++) {
274 if ((existing_state->res_ctx.pipe_ctx[i].plane_res.hubp &&
275 existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i) ||
276 existing_state->res_ctx.pipe_ctx[i].stream_res.tg)
277 last_resort_pipe_candidates[num_last_resort_candidates++] = i;
278 }
279 }
280
281 return num_last_resort_candidates;
282 }
283
is_pipe_in_candidate_array(const unsigned int pipe_idx,const unsigned int * candidate_array,const unsigned int candidate_array_size)284 static bool is_pipe_in_candidate_array(const unsigned int pipe_idx,
285 const unsigned int *candidate_array,
286 const unsigned int candidate_array_size)
287 {
288 int i;
289
290 for (i = 0; i < candidate_array_size; i++) {
291 if (candidate_array[i] == pipe_idx)
292 return true;
293 }
294
295 return false;
296 }
297
find_more_pipes_for_stream(struct dml2_context * ctx,struct dc_state * state,unsigned int stream_id,int * assigned_pipes,int * assigned_pipe_count,int pipes_needed,const struct dc_state * existing_state)298 static bool find_more_pipes_for_stream(struct dml2_context *ctx,
299 struct dc_state *state, // The state we want to find a free mapping in
300 unsigned int stream_id, // The stream we want this pipe to drive
301 int *assigned_pipes,
302 int *assigned_pipe_count,
303 int pipes_needed,
304 const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to
305 {
306 struct pipe_ctx *pipe = NULL;
307 unsigned int preferred_pipe_candidates[MAX_PIPES] = {0};
308 unsigned int last_resort_pipe_candidates[MAX_PIPES] = {0};
309 unsigned int num_preferred_candidates = 0;
310 unsigned int num_last_resort_candidates = 0;
311 int i;
312
313 if (existing_state) {
314 num_preferred_candidates =
315 find_preferred_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, preferred_pipe_candidates);
316
317 num_last_resort_candidates =
318 find_last_resort_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, last_resort_pipe_candidates);
319 }
320
321 // First see if any of the preferred are unmapped, and choose those instead
322 for (i = 0; pipes_needed > 0 && i < num_preferred_candidates; i++) {
323 pipe = &state->res_ctx.pipe_ctx[preferred_pipe_candidates[i]];
324 if (!is_plane_using_pipe(pipe)) {
325 pipes_needed--;
326 // TODO: This doens't make sense really, pipe_idx should always be valid
327 pipe->pipe_idx = preferred_pipe_candidates[i];
328 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
329 }
330 }
331
332 // We like to pair pipes starting from the higher order indicies for combining
333 for (i = ctx->config.dcn_pipe_count - 1; pipes_needed > 0 && i >= 0; i--) {
334 // Ignore any pipes that are the preferred or last resort candidate
335 if (is_pipe_in_candidate_array(i, preferred_pipe_candidates, num_preferred_candidates) ||
336 is_pipe_in_candidate_array(i, last_resort_pipe_candidates, num_last_resort_candidates))
337 continue;
338
339 pipe = &state->res_ctx.pipe_ctx[i];
340 if (!is_plane_using_pipe(pipe)) {
341 pipes_needed--;
342 // TODO: This doens't make sense really, pipe_idx should always be valid
343 pipe->pipe_idx = i;
344 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
345 }
346 }
347
348 // Only use the last resort pipe candidates as a last resort
349 for (i = 0; pipes_needed > 0 && i < num_last_resort_candidates; i++) {
350 pipe = &state->res_ctx.pipe_ctx[last_resort_pipe_candidates[i]];
351 if (!is_plane_using_pipe(pipe)) {
352 pipes_needed--;
353 // TODO: This doens't make sense really, pipe_idx should always be valid
354 pipe->pipe_idx = last_resort_pipe_candidates[i];
355 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
356 }
357 }
358
359 ASSERT(pipes_needed <= 0); // Validation should prevent us from building a pipe context that exceeds the number of HW resoruces available
360
361 return pipes_needed <= 0;
362 }
363
find_more_free_pipes(struct dml2_context * ctx,struct dc_state * state,unsigned int stream_id,int * assigned_pipes,int * assigned_pipe_count,int pipes_needed,const struct dc_state * existing_state)364 static bool find_more_free_pipes(struct dml2_context *ctx,
365 struct dc_state *state, // The state we want to find a free mapping in
366 unsigned int stream_id, // The stream we want this pipe to drive
367 int *assigned_pipes,
368 int *assigned_pipe_count,
369 int pipes_needed,
370 const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to
371 {
372 struct pipe_ctx *pipe = NULL;
373 unsigned int preferred_pipe_candidates[MAX_PIPES] = {0};
374 unsigned int last_resort_pipe_candidates[MAX_PIPES] = {0};
375 unsigned int num_preferred_candidates = 0;
376 unsigned int num_last_resort_candidates = 0;
377 int i;
378
379 if (existing_state) {
380 num_preferred_candidates =
381 find_preferred_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, preferred_pipe_candidates);
382
383 num_last_resort_candidates =
384 find_last_resort_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, last_resort_pipe_candidates);
385 }
386
387 // First see if any of the preferred are unmapped, and choose those instead
388 for (i = 0; pipes_needed > 0 && i < num_preferred_candidates; i++) {
389 pipe = &state->res_ctx.pipe_ctx[preferred_pipe_candidates[i]];
390 if (is_pipe_free(pipe)) {
391 pipes_needed--;
392 // TODO: This doens't make sense really, pipe_idx should always be valid
393 pipe->pipe_idx = preferred_pipe_candidates[i];
394 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
395 }
396 }
397
398 // We like to pair pipes starting from the higher order indicies for combining
399 for (i = ctx->config.dcn_pipe_count - 1; pipes_needed > 0 && i >= 0; i--) {
400 // Ignore any pipes that are the preferred or last resort candidate
401 if (is_pipe_in_candidate_array(i, preferred_pipe_candidates, num_preferred_candidates) ||
402 is_pipe_in_candidate_array(i, last_resort_pipe_candidates, num_last_resort_candidates))
403 continue;
404
405 pipe = &state->res_ctx.pipe_ctx[i];
406 if (is_pipe_free(pipe)) {
407 pipes_needed--;
408 // TODO: This doens't make sense really, pipe_idx should always be valid
409 pipe->pipe_idx = i;
410 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
411 }
412 }
413
414 // Only use the last resort pipe candidates as a last resort
415 for (i = 0; pipes_needed > 0 && i < num_last_resort_candidates; i++) {
416 pipe = &state->res_ctx.pipe_ctx[last_resort_pipe_candidates[i]];
417 if (is_pipe_free(pipe)) {
418 pipes_needed--;
419 // TODO: This doens't make sense really, pipe_idx should always be valid
420 pipe->pipe_idx = last_resort_pipe_candidates[i];
421 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
422 }
423 }
424
425 ASSERT(pipes_needed == 0); // Validation should prevent us from building a pipe context that exceeds the number of HW resoruces available
426
427 return pipes_needed == 0;
428 }
429
sort_pipes_for_splitting(struct dc_plane_pipe_pool * pipes)430 static void sort_pipes_for_splitting(struct dc_plane_pipe_pool *pipes)
431 {
432 bool sorted, swapped;
433 unsigned int cur_index;
434 unsigned int temp;
435 int odm_slice_index;
436
437 for (odm_slice_index = 0; odm_slice_index < pipes->num_pipes_assigned_to_plane_for_odm_combine; odm_slice_index++) {
438 // Sort each MPCC set
439 //Un-optimized bubble sort, but that's okay for array sizes <= 6
440
441 if (pipes->num_pipes_assigned_to_plane_for_mpcc_combine <= 1)
442 sorted = true;
443 else
444 sorted = false;
445
446 cur_index = 0;
447 swapped = false;
448 while (!sorted) {
449 if (pipes->pipes_assigned_to_plane[odm_slice_index][cur_index] > pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1]) {
450 temp = pipes->pipes_assigned_to_plane[odm_slice_index][cur_index];
451 pipes->pipes_assigned_to_plane[odm_slice_index][cur_index] = pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1];
452 pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1] = temp;
453
454 swapped = true;
455 }
456
457 cur_index++;
458
459 if (cur_index == pipes->num_pipes_assigned_to_plane_for_mpcc_combine - 1) {
460 cur_index = 0;
461
462 if (swapped)
463 sorted = false;
464 else
465 sorted = true;
466
467 swapped = false;
468 }
469
470 }
471 }
472 }
473
474 // 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)475 static void calculate_odm_slices(const struct dc_stream_state *stream, unsigned int odm_factor, unsigned int *odm_slice_end_x)
476 {
477 unsigned int slice_size = 0;
478 int i;
479
480 if (odm_factor < 1 || odm_factor > 4) {
481 ASSERT(false);
482 return;
483 }
484
485 slice_size = stream->src.width / odm_factor;
486
487 for (i = 0; i < odm_factor; i++)
488 odm_slice_end_x[i] = (slice_size * (i + 1)) - 1;
489
490 odm_slice_end_x[odm_factor - 1] = stream->src.width - 1;
491 }
492
is_plane_in_odm_slice(const struct dc_plane_state * plane,unsigned int slice_index,unsigned int * odm_slice_end_x,unsigned int num_slices)493 static bool is_plane_in_odm_slice(const struct dc_plane_state *plane, unsigned int slice_index, unsigned int *odm_slice_end_x, unsigned int num_slices)
494 {
495 unsigned int slice_start_x, slice_end_x;
496
497 if (slice_index == 0)
498 slice_start_x = 0;
499 else
500 slice_start_x = odm_slice_end_x[slice_index - 1] + 1;
501
502 slice_end_x = odm_slice_end_x[slice_index];
503
504 if (plane->clip_rect.x + plane->clip_rect.width < slice_start_x)
505 return false;
506
507 if (plane->clip_rect.x > slice_end_x)
508 return false;
509
510 return true;
511 }
512
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)513 static void add_odm_slice_to_odm_tree(struct dml2_context *ctx,
514 struct dc_state *state,
515 struct dc_pipe_mapping_scratch *scratch,
516 unsigned int odm_slice_index)
517 {
518 struct pipe_ctx *pipe = NULL;
519 int i;
520
521 // MPCC Combine + ODM Combine is not supported, so there should never be a case where the current plane
522 // has more than 1 pipe mapped to it for a given slice.
523 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);
524
525 for (i = 0; i < scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
526 pipe = &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][i]];
527
528 if (scratch->mpc_info.prev_odm_pipe)
529 scratch->mpc_info.prev_odm_pipe->next_odm_pipe = pipe;
530
531 pipe->prev_odm_pipe = scratch->mpc_info.prev_odm_pipe;
532 pipe->next_odm_pipe = NULL;
533 }
534 scratch->mpc_info.prev_odm_pipe = pipe;
535 }
536
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)537 static struct pipe_ctx *add_plane_to_blend_tree(struct dml2_context *ctx,
538 struct dc_state *state,
539 const struct dc_plane_state *plane,
540 struct dc_plane_pipe_pool *pipe_pool,
541 unsigned int odm_slice,
542 struct pipe_ctx *top_pipe)
543 {
544 int i;
545
546 for (i = 0; i < pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
547 if (top_pipe)
548 top_pipe->bottom_pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]];
549
550 pipe_pool->pipe_used[odm_slice][i] = true;
551
552 state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]].top_pipe = top_pipe;
553 state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]].bottom_pipe = NULL;
554
555 top_pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]];
556 }
557
558 // After running the above loop, the top pipe actually ends up pointing to the bottom of this MPCC combine tree, so we are actually
559 // returning the bottom pipe here
560 return top_pipe;
561 }
562
find_pipes_assigned_to_stream(struct dml2_context * ctx,struct dc_state * state,unsigned int stream_id,unsigned int * pipes)563 static unsigned int find_pipes_assigned_to_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int stream_id, unsigned int *pipes)
564 {
565 int i;
566 unsigned int num_found = 0;
567
568 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
569 if (state->res_ctx.pipe_ctx[i].stream && state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id) {
570 pipes[num_found++] = i;
571 }
572 }
573
574 return num_found;
575 }
576
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)577 static struct pipe_ctx *assign_pipes_to_stream(struct dml2_context *ctx, struct dc_state *state,
578 const struct dc_stream_state *stream,
579 int odm_factor,
580 struct dc_plane_pipe_pool *pipe_pool,
581 const struct dc_state *existing_state)
582 {
583 struct pipe_ctx *master_pipe;
584 unsigned int pipes_needed;
585 unsigned int pipes_assigned;
586 unsigned int pipes[MAX_PIPES] = {0};
587 unsigned int next_pipe_to_assign;
588 int odm_slice;
589
590 pipes_needed = odm_factor;
591
592 master_pipe = find_master_pipe_of_stream(ctx, state, stream->stream_id);
593 ASSERT(master_pipe);
594
595 pipes_assigned = find_pipes_assigned_to_stream(ctx, state, stream->stream_id, pipes);
596
597 find_more_free_pipes(ctx, state, stream->stream_id, pipes, &pipes_assigned, pipes_needed - pipes_assigned, existing_state);
598
599 ASSERT(pipes_assigned == pipes_needed);
600
601 next_pipe_to_assign = 0;
602 for (odm_slice = 0; odm_slice < odm_factor; odm_slice++)
603 pipe_pool->pipes_assigned_to_plane[odm_slice][0] = pipes[next_pipe_to_assign++];
604
605 pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine = 1;
606 pipe_pool->num_pipes_assigned_to_plane_for_odm_combine = odm_factor;
607
608 return master_pipe;
609 }
610
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)611 static struct pipe_ctx *assign_pipes_to_plane(struct dml2_context *ctx, struct dc_state *state,
612 const struct dc_stream_state *stream,
613 const struct dc_plane_state *plane,
614 int odm_factor,
615 int mpc_factor,
616 int plane_index,
617 struct dc_plane_pipe_pool *pipe_pool,
618 const struct dc_state *existing_state)
619 {
620 struct pipe_ctx *master_pipe = NULL;
621 unsigned int plane_id;
622 unsigned int pipes_needed;
623 unsigned int pipes_assigned;
624 unsigned int pipes[MAX_PIPES] = {0};
625 unsigned int next_pipe_to_assign;
626 int odm_slice, mpc_slice;
627
628 if (!get_plane_id(ctx, state, plane, stream->stream_id, plane_index, &plane_id)) {
629 ASSERT(false);
630 return master_pipe;
631 }
632
633 pipes_needed = mpc_factor * odm_factor;
634
635 master_pipe = find_master_pipe_of_plane(ctx, state, plane_id);
636 ASSERT(master_pipe);
637
638 pipes_assigned = find_pipes_assigned_to_plane(ctx, state, plane_id, pipes);
639
640 find_more_pipes_for_stream(ctx, state, stream->stream_id, pipes, &pipes_assigned, pipes_needed - pipes_assigned, existing_state);
641
642 ASSERT(pipes_assigned >= pipes_needed);
643
644 next_pipe_to_assign = 0;
645 for (odm_slice = 0; odm_slice < odm_factor; odm_slice++)
646 for (mpc_slice = 0; mpc_slice < mpc_factor; mpc_slice++)
647 pipe_pool->pipes_assigned_to_plane[odm_slice][mpc_slice] = pipes[next_pipe_to_assign++];
648
649 pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine = mpc_factor;
650 pipe_pool->num_pipes_assigned_to_plane_for_odm_combine = odm_factor;
651
652 return master_pipe;
653 }
654
is_pipe_used(const struct dc_plane_pipe_pool * pool,unsigned int pipe_idx)655 static bool is_pipe_used(const struct dc_plane_pipe_pool *pool, unsigned int pipe_idx)
656 {
657 int i, j;
658
659 for (i = 0; i < pool->num_pipes_assigned_to_plane_for_odm_combine; i++) {
660 for (j = 0; j < pool->num_pipes_assigned_to_plane_for_mpcc_combine; j++) {
661 if (pool->pipes_assigned_to_plane[i][j] == pipe_idx && pool->pipe_used[i][j])
662 return true;
663 }
664 }
665
666 return false;
667 }
668
free_pipe(struct pipe_ctx * pipe)669 static void free_pipe(struct pipe_ctx *pipe)
670 {
671 memset(pipe, 0, sizeof(struct pipe_ctx));
672 }
673
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)674 static void free_unused_pipes_for_plane(struct dml2_context *ctx, struct dc_state *state,
675 const struct dc_plane_state *plane, const struct dc_plane_pipe_pool *pool, unsigned int stream_id, int plane_index)
676 {
677 int i;
678 bool is_plane_duplicate = ctx->v20.scratch.plane_duplicate_exists;
679
680 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
681 if (state->res_ctx.pipe_ctx[i].plane_state == plane &&
682 state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id &&
683 (!is_plane_duplicate || (is_plane_duplicate &&
684 ctx->v20.scratch.dml_to_dc_pipe_mapping.dml_pipe_idx_to_plane_index[state->res_ctx.pipe_ctx[i].pipe_idx] == plane_index)) &&
685 !is_pipe_used(pool, state->res_ctx.pipe_ctx[i].pipe_idx)) {
686 free_pipe(&state->res_ctx.pipe_ctx[i]);
687 }
688 }
689 }
690
remove_pipes_from_blend_trees(struct dml2_context * ctx,struct dc_state * state,struct dc_plane_pipe_pool * pipe_pool,unsigned int odm_slice)691 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)
692 {
693 struct pipe_ctx *pipe;
694 int i;
695
696 for (i = 0; i < pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
697 pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][0]];
698 if (pipe->top_pipe)
699 pipe->top_pipe->bottom_pipe = pipe->bottom_pipe;
700
701 if (pipe->bottom_pipe)
702 pipe->bottom_pipe = pipe->top_pipe;
703
704 pipe_pool->pipe_used[odm_slice][i] = true;
705 }
706 }
707
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)708 static void map_pipes_for_stream(struct dml2_context *ctx, struct dc_state *state, const struct dc_stream_state *stream,
709 struct dc_pipe_mapping_scratch *scratch, const struct dc_state *existing_state)
710 {
711 int odm_slice_index;
712 struct pipe_ctx *master_pipe = NULL;
713
714
715 master_pipe = assign_pipes_to_stream(ctx, state, stream, scratch->odm_info.odm_factor, &scratch->pipe_pool, existing_state);
716 sort_pipes_for_splitting(&scratch->pipe_pool);
717
718 for (odm_slice_index = 0; odm_slice_index < scratch->odm_info.odm_factor; odm_slice_index++) {
719 remove_pipes_from_blend_trees(ctx, state, &scratch->pipe_pool, odm_slice_index);
720
721 add_odm_slice_to_odm_tree(ctx, state, scratch, odm_slice_index);
722
723 ctx->config.callbacks.acquire_secondary_pipe_for_mpc_odm(ctx->config.callbacks.dc, state,
724 master_pipe, &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][0]], true);
725 }
726 }
727
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)728 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,
729 int plane_index, struct dc_pipe_mapping_scratch *scratch, const struct dc_state *existing_state)
730 {
731 int odm_slice_index;
732 unsigned int plane_id;
733 struct pipe_ctx *master_pipe = NULL;
734 int i;
735
736 if (!get_plane_id(ctx, state, plane, stream->stream_id, plane_index, &plane_id)) {
737 ASSERT(false);
738 return;
739 }
740
741 master_pipe = assign_pipes_to_plane(ctx, state, stream, plane, scratch->odm_info.odm_factor,
742 scratch->mpc_info.mpc_factor, plane_index, &scratch->pipe_pool, existing_state);
743 sort_pipes_for_splitting(&scratch->pipe_pool);
744
745 for (odm_slice_index = 0; odm_slice_index < scratch->odm_info.odm_factor; odm_slice_index++) {
746 // We build the tree for one ODM slice at a time.
747 // Each ODM slice shares a common OPP
748 if (!is_plane_in_odm_slice(plane, odm_slice_index, scratch->odm_info.odm_slice_end_x, scratch->odm_info.odm_factor)) {
749 continue;
750 }
751
752 // Now we have a list of all pipes to be used for this plane/stream, now setup the tree.
753 scratch->odm_info.next_higher_pipe_for_odm_slice[odm_slice_index] = add_plane_to_blend_tree(ctx, state,
754 plane,
755 &scratch->pipe_pool,
756 odm_slice_index,
757 scratch->odm_info.next_higher_pipe_for_odm_slice[odm_slice_index]);
758
759 add_odm_slice_to_odm_tree(ctx, state, scratch, odm_slice_index);
760
761 for (i = 0; i < scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
762
763 ctx->config.callbacks.acquire_secondary_pipe_for_mpc_odm(ctx->config.callbacks.dc, state,
764 master_pipe, &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][i]], true);
765 }
766 }
767
768 free_unused_pipes_for_plane(ctx, state, plane, &scratch->pipe_pool, stream->stream_id, plane_index);
769 }
770
get_mpc_factor(struct dml2_context * ctx,const 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)771 static unsigned int get_mpc_factor(struct dml2_context *ctx,
772 const struct dc_state *state,
773 const struct dml_display_cfg_st *disp_cfg,
774 struct dml2_dml_to_dc_pipe_mapping *mapping,
775 const struct dc_stream_status *status,
776 const struct dc_stream_state *stream,
777 int plane_idx)
778 {
779 unsigned int plane_id;
780 unsigned int cfg_idx;
781 unsigned int mpc_factor;
782
783 get_plane_id(ctx, state, status->plane_states[plane_idx],
784 stream->stream_id, plane_idx, &plane_id);
785 cfg_idx = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
786 if (ctx->architecture == dml2_architecture_20) {
787 mpc_factor = (unsigned int)disp_cfg->hw.DPPPerSurface[cfg_idx];
788 } else {
789 mpc_factor = 1;
790 ASSERT(false);
791 }
792
793 /* For stereo timings, we need to pipe split */
794 if (dml2_is_stereo_timing(stream))
795 mpc_factor = 2;
796
797 return mpc_factor;
798 }
799
get_odm_factor(const struct dml2_context * ctx,const struct dml_display_cfg_st * disp_cfg,struct dml2_dml_to_dc_pipe_mapping * mapping,const struct dc_stream_state * stream)800 static unsigned int get_odm_factor(
801 const struct dml2_context *ctx,
802 const struct dml_display_cfg_st *disp_cfg,
803 struct dml2_dml_to_dc_pipe_mapping *mapping,
804 const struct dc_stream_state *stream)
805 {
806 unsigned int cfg_idx = find_disp_cfg_idx_by_stream_id(
807 mapping, stream->stream_id);
808
809 if (ctx->architecture == dml2_architecture_20)
810 switch (disp_cfg->hw.ODMMode[cfg_idx]) {
811 case dml_odm_mode_bypass:
812 return 1;
813 case dml_odm_mode_combine_2to1:
814 return 2;
815 case dml_odm_mode_combine_4to1:
816 return 4;
817 default:
818 break;
819 }
820 ASSERT(false);
821 return 1;
822 }
823
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,const struct dc_state * state,unsigned int stream_idx,unsigned int odm_factor,unsigned int mpc_factors[MAX_PIPES])824 static void populate_mpc_factors_for_stream(
825 struct dml2_context *ctx,
826 const struct dml_display_cfg_st *disp_cfg,
827 struct dml2_dml_to_dc_pipe_mapping *mapping,
828 const struct dc_state *state,
829 unsigned int stream_idx,
830 unsigned int odm_factor,
831 unsigned int mpc_factors[MAX_PIPES])
832 {
833 const struct dc_stream_status *status = &state->stream_status[stream_idx];
834 int i;
835
836 for (i = 0; i < status->plane_count; i++)
837 if (odm_factor == 1)
838 mpc_factors[i] = get_mpc_factor(
839 ctx, state, disp_cfg, mapping, status,
840 state->streams[stream_idx], i);
841 else
842 mpc_factors[i] = 1;
843 }
844
populate_odm_factors(const struct dml2_context * ctx,const struct dml_display_cfg_st * disp_cfg,struct dml2_dml_to_dc_pipe_mapping * mapping,const struct dc_state * state,unsigned int odm_factors[MAX_PIPES])845 static void populate_odm_factors(const struct dml2_context *ctx,
846 const struct dml_display_cfg_st *disp_cfg,
847 struct dml2_dml_to_dc_pipe_mapping *mapping,
848 const struct dc_state *state,
849 unsigned int odm_factors[MAX_PIPES])
850 {
851 int i;
852
853 for (i = 0; i < state->stream_count; i++)
854 odm_factors[i] = get_odm_factor(
855 ctx, disp_cfg, mapping, state->streams[i]);
856 }
857
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,unsigned int odm_factor,unsigned int mpc_factors[MAX_PIPES])858 static bool map_dc_pipes_for_stream(struct dml2_context *ctx,
859 struct dc_state *state,
860 const struct dc_state *existing_state,
861 const struct dc_stream_state *stream,
862 const struct dc_stream_status *status,
863 unsigned int odm_factor,
864 unsigned int mpc_factors[MAX_PIPES])
865 {
866 int plane_idx;
867 bool result = true;
868
869 if (odm_factor == 1)
870 /*
871 * ODM and MPC combines are by DML design mutually exclusive.
872 * ODM factor of 1 means MPC factors may be greater than 1.
873 * In this case, we want to set ODM factor to 1 first to free up
874 * pipe resources from previous ODM configuration before setting
875 * up MPC combine to acquire more pipe resources.
876 */
877 result &= ctx->config.callbacks.update_pipes_for_stream_with_slice_count(
878 state,
879 existing_state,
880 ctx->config.callbacks.dc->res_pool,
881 stream,
882 odm_factor);
883 for (plane_idx = 0; plane_idx < status->plane_count; plane_idx++)
884 result &= ctx->config.callbacks.update_pipes_for_plane_with_slice_count(
885 state,
886 existing_state,
887 ctx->config.callbacks.dc->res_pool,
888 status->plane_states[plane_idx],
889 mpc_factors[plane_idx]);
890 if (odm_factor > 1)
891 result &= ctx->config.callbacks.update_pipes_for_stream_with_slice_count(
892 state,
893 existing_state,
894 ctx->config.callbacks.dc->res_pool,
895 stream,
896 odm_factor);
897 return result;
898 }
899
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)900 static bool map_dc_pipes_with_callbacks(struct dml2_context *ctx,
901 struct dc_state *state,
902 const struct dml_display_cfg_st *disp_cfg,
903 struct dml2_dml_to_dc_pipe_mapping *mapping,
904 const struct dc_state *existing_state)
905 {
906 unsigned int odm_factors[MAX_PIPES];
907 unsigned int mpc_factors_for_stream[MAX_PIPES];
908 int i;
909 bool result = true;
910
911 populate_odm_factors(ctx, disp_cfg, mapping, state, odm_factors);
912 for (i = 0; i < state->stream_count; i++) {
913 populate_mpc_factors_for_stream(ctx, disp_cfg, mapping, state,
914 i, odm_factors[i], mpc_factors_for_stream);
915 result &= map_dc_pipes_for_stream(ctx, state, existing_state,
916 state->streams[i],
917 &state->stream_status[i],
918 odm_factors[i], mpc_factors_for_stream);
919 }
920 return result;
921 }
922
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)923 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)
924 {
925 int stream_index, plane_index, i;
926
927 unsigned int stream_disp_cfg_index;
928 unsigned int plane_disp_cfg_index;
929
930 unsigned int plane_id;
931 unsigned int stream_id;
932
933 const unsigned int *ODMMode, *DPPPerSurface;
934 struct dc_pipe_mapping_scratch scratch;
935
936 if (ctx->config.map_dc_pipes_with_callbacks)
937 return map_dc_pipes_with_callbacks(
938 ctx, state, disp_cfg, mapping, existing_state);
939
940 ODMMode = (unsigned int *)disp_cfg->hw.ODMMode;
941 DPPPerSurface = disp_cfg->hw.DPPPerSurface;
942
943 for (stream_index = 0; stream_index < state->stream_count; stream_index++) {
944 memset(&scratch, 0, sizeof(struct dc_pipe_mapping_scratch));
945
946 stream_id = state->streams[stream_index]->stream_id;
947 stream_disp_cfg_index = find_disp_cfg_idx_by_stream_id(mapping, stream_id);
948
949 if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_bypass) {
950 scratch.odm_info.odm_factor = 1;
951 } else if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_combine_2to1) {
952 scratch.odm_info.odm_factor = 2;
953 } else if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_combine_4to1) {
954 scratch.odm_info.odm_factor = 4;
955 } else {
956 ASSERT(false);
957 scratch.odm_info.odm_factor = 1;
958 }
959
960 calculate_odm_slices(state->streams[stream_index], scratch.odm_info.odm_factor, scratch.odm_info.odm_slice_end_x);
961
962 // If there are no planes, you still want to setup ODM...
963 if (state->stream_status[stream_index].plane_count == 0) {
964 map_pipes_for_stream(ctx, state, state->streams[stream_index], &scratch, existing_state);
965 }
966
967 for (plane_index = 0; plane_index < state->stream_status[stream_index].plane_count; plane_index++) {
968 // Planes are ordered top to bottom.
969 if (get_plane_id(ctx, state, state->stream_status[stream_index].plane_states[plane_index],
970 stream_id, plane_index, &plane_id)) {
971 plane_disp_cfg_index = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
972
973 // Setup mpc_info for this plane
974 scratch.mpc_info.prev_odm_pipe = NULL;
975 if (scratch.odm_info.odm_factor == 1) {
976 // If ODM combine is not inuse, then the number of pipes
977 // per plane is determined by MPC combine factor
978 scratch.mpc_info.mpc_factor = DPPPerSurface[plane_disp_cfg_index];
979
980 //For stereo timings, we need to pipe split
981 if (dml2_is_stereo_timing(state->streams[stream_index]))
982 scratch.mpc_info.mpc_factor = 2;
983 } else {
984 // If ODM combine is enabled, then we use at most 1 pipe per
985 // odm slice per plane, i.e. MPC combine is never used
986 scratch.mpc_info.mpc_factor = 1;
987 }
988
989 ASSERT(scratch.odm_info.odm_factor * scratch.mpc_info.mpc_factor > 0);
990
991 // Clear the pool assignment scratch (which is per plane)
992 memset(&scratch.pipe_pool, 0, sizeof(struct dc_plane_pipe_pool));
993
994 map_pipes_for_plane(ctx, state, state->streams[stream_index],
995 state->stream_status[stream_index].plane_states[plane_index], plane_index, &scratch, existing_state);
996 } else {
997 // Plane ID cannot be generated, therefore no DML mapping can be performed.
998 ASSERT(false);
999 }
1000 }
1001
1002 }
1003
1004 if (!validate_pipe_assignment(ctx, state, disp_cfg, mapping))
1005 ASSERT(false);
1006
1007 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
1008 struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
1009
1010 if (pipe->plane_state) {
1011 if (!ctx->config.callbacks.build_scaling_params(pipe)) {
1012 ASSERT(false);
1013 }
1014 }
1015 }
1016
1017 return true;
1018 }
1019