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