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

source code of linux/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c