1 /* SPDX-License-Identifier: MIT */
3 * Copyright 2023 Advanced Micro Devices, Inc.
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:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
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.
27 #include "dml2_mall_phantom.h"
29 #include "dml2_dc_types.h"
30 #include "dml2_internal_types.h"
31 #include "dml2_utils.h"
32 #include "dml2_dc_resource_mgmt.h"
34 #define MAX_ODM_FACTOR 4
35 #define MAX_MPCC_FACTOR 4
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;
44 struct dc_pipe_mapping_scratch {
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];
51 unsigned int mpc_factor;
52 struct pipe_ctx *prev_odm_pipe;
55 struct dc_plane_pipe_pool pipe_pool;
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)
62 bool is_plane_duplicate = dml2->v20.scratch.plane_duplicate_exists;
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;
82 static int find_disp_cfg_idx_by_plane_id(struct dml2_dml_to_dc_pipe_mapping *mapping, unsigned int plane_id)
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)
92 return __DML2_WRAPPER_MAX_STREAMS_PLANES__;
95 static int find_disp_cfg_idx_by_stream_id(struct dml2_dml_to_dc_pipe_mapping *mapping, unsigned int stream_id)
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)
105 return __DML2_WRAPPER_MAX_STREAMS_PLANES__;
108 // The master pipe of a stream is defined as the top pipe in odm slice 0
109 static struct pipe_ctx *find_master_pipe_of_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int stream_id)
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];
123 static struct pipe_ctx *find_master_pipe_of_plane(struct dml2_context *ctx,
124 struct dc_state *state, unsigned int plane_id)
127 unsigned int plane_id_assigned_to_pipe;
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];
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)
145 unsigned int num_found = 0;
146 unsigned int plane_id_assigned_to_pipe = -1;
148 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
149 struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
151 if (!pipe->plane_state || !pipe->stream)
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)) {
160 struct pipe_ctx *mpc_pipe = pipe;
163 pipes[num_found++] = mpc_pipe->pipe_idx;
164 mpc_pipe = mpc_pipe->bottom_pipe;
167 if (mpc_pipe->plane_state != pipe->plane_state)
170 pipe = pipe->next_odm_pipe;
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)
183 // unsigned int plane_id;
185 // unsigned int disp_cfg_index;
187 // unsigned int pipes_assigned_to_plane[MAX_PIPES];
188 // unsigned int num_pipes_assigned_to_plane;
190 // struct pipe_ctx *top_pipe;
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);
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)
203 // top_pipe = find_top_pipe_in_tree(state->res_ctx.pipe_ctx[pipes_assigned_to_plane[0]]);
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);
209 // verify_combine_tree(top_pipe, state->streams[i]->stream_id, plane_id, state, true);
212 // // TODO: could also do additional verification that the pipes in tree are the same as
213 // // pipes_assigned_to_plane
227 static bool is_plane_using_pipe(const struct pipe_ctx *pipe)
229 if (pipe->plane_state)
235 static bool is_pipe_free(const struct pipe_ctx *pipe)
237 if (!pipe->plane_state && !pipe->stream)
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)
248 unsigned int num_preferred_candidates = 0;
251 /* There is only one case which we consider for adding a pipe to the preferred
252 * pipe candidate array:
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.
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.
262 if (existing_state) {
263 for (i = 0; i < pipe_count; i++) {
264 if (existing_state->res_ctx.pipe_ctx[i].stream && existing_state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id) {
265 if (existing_state->res_ctx.pipe_ctx[i].plane_res.hubp &&
266 existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i)
269 preferred_pipe_candidates[num_preferred_candidates++] = i;
274 return num_preferred_candidates;
277 static unsigned int find_last_resort_pipe_candidates(const struct dc_state *existing_state,
278 const int pipe_count,
279 const unsigned int stream_id,
280 unsigned int *last_resort_pipe_candidates)
282 unsigned int num_last_resort_candidates = 0;
285 /* There are two cases where we would like to add a given pipe into the last
288 * 1. If the pipe requires a change in OPP, for example during an MPC
291 * 2. If the pipe already has an enabled OTG.
293 if (existing_state) {
294 for (i = 0; i < pipe_count; i++) {
295 if ((existing_state->res_ctx.pipe_ctx[i].plane_res.hubp &&
296 existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i) ||
297 existing_state->res_ctx.pipe_ctx[i].stream_res.tg)
298 last_resort_pipe_candidates[num_last_resort_candidates++] = i;
302 return num_last_resort_candidates;
305 static bool is_pipe_in_candidate_array(const unsigned int pipe_idx,
306 const unsigned int *candidate_array,
307 const unsigned int candidate_array_size)
311 for (i = 0; i < candidate_array_size; i++) {
312 if (candidate_array[i] == pipe_idx)
319 static bool find_more_pipes_for_stream(struct dml2_context *ctx,
320 struct dc_state *state, // The state we want to find a free mapping in
321 unsigned int stream_id, // The stream we want this pipe to drive
323 int *assigned_pipe_count,
325 const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to
327 struct pipe_ctx *pipe = NULL;
328 unsigned int preferred_pipe_candidates[MAX_PIPES] = {0};
329 unsigned int last_resort_pipe_candidates[MAX_PIPES] = {0};
330 unsigned int num_preferred_candidates = 0;
331 unsigned int num_last_resort_candidates = 0;
334 if (existing_state) {
335 num_preferred_candidates =
336 find_preferred_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, preferred_pipe_candidates);
338 num_last_resort_candidates =
339 find_last_resort_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, last_resort_pipe_candidates);
342 // First see if any of the preferred are unmapped, and choose those instead
343 for (i = 0; pipes_needed > 0 && i < num_preferred_candidates; i++) {
344 pipe = &state->res_ctx.pipe_ctx[preferred_pipe_candidates[i]];
345 if (!is_plane_using_pipe(pipe)) {
347 // TODO: This doens't make sense really, pipe_idx should always be valid
348 pipe->pipe_idx = preferred_pipe_candidates[i];
349 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
353 // We like to pair pipes starting from the higher order indicies for combining
354 for (i = ctx->config.dcn_pipe_count - 1; pipes_needed > 0 && i >= 0; i--) {
355 // Ignore any pipes that are the preferred or last resort candidate
356 if (is_pipe_in_candidate_array(i, preferred_pipe_candidates, num_preferred_candidates) ||
357 is_pipe_in_candidate_array(i, last_resort_pipe_candidates, num_last_resort_candidates))
360 pipe = &state->res_ctx.pipe_ctx[i];
361 if (!is_plane_using_pipe(pipe)) {
363 // TODO: This doens't make sense really, pipe_idx should always be valid
365 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
369 // Only use the last resort pipe candidates as a last resort
370 for (i = 0; pipes_needed > 0 && i < num_last_resort_candidates; i++) {
371 pipe = &state->res_ctx.pipe_ctx[last_resort_pipe_candidates[i]];
372 if (!is_plane_using_pipe(pipe)) {
374 // TODO: This doens't make sense really, pipe_idx should always be valid
375 pipe->pipe_idx = last_resort_pipe_candidates[i];
376 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
380 ASSERT(pipes_needed <= 0); // Validation should prevent us from building a pipe context that exceeds the number of HW resoruces available
382 return pipes_needed <= 0;
385 static bool find_more_free_pipes(struct dml2_context *ctx,
386 struct dc_state *state, // The state we want to find a free mapping in
387 unsigned int stream_id, // The stream we want this pipe to drive
389 int *assigned_pipe_count,
391 const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to
393 struct pipe_ctx *pipe = NULL;
394 unsigned int preferred_pipe_candidates[MAX_PIPES] = {0};
395 unsigned int last_resort_pipe_candidates[MAX_PIPES] = {0};
396 unsigned int num_preferred_candidates = 0;
397 unsigned int num_last_resort_candidates = 0;
400 if (existing_state) {
401 num_preferred_candidates =
402 find_preferred_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, preferred_pipe_candidates);
404 num_last_resort_candidates =
405 find_last_resort_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, last_resort_pipe_candidates);
408 // First see if any of the preferred are unmapped, and choose those instead
409 for (i = 0; pipes_needed > 0 && i < num_preferred_candidates; i++) {
410 pipe = &state->res_ctx.pipe_ctx[preferred_pipe_candidates[i]];
411 if (is_pipe_free(pipe)) {
413 // TODO: This doens't make sense really, pipe_idx should always be valid
414 pipe->pipe_idx = preferred_pipe_candidates[i];
415 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
419 // We like to pair pipes starting from the higher order indicies for combining
420 for (i = ctx->config.dcn_pipe_count - 1; pipes_needed > 0 && i >= 0; i--) {
421 // Ignore any pipes that are the preferred or last resort candidate
422 if (is_pipe_in_candidate_array(i, preferred_pipe_candidates, num_preferred_candidates) ||
423 is_pipe_in_candidate_array(i, last_resort_pipe_candidates, num_last_resort_candidates))
426 pipe = &state->res_ctx.pipe_ctx[i];
427 if (is_pipe_free(pipe)) {
429 // TODO: This doens't make sense really, pipe_idx should always be valid
431 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
435 // Only use the last resort pipe candidates as a last resort
436 for (i = 0; pipes_needed > 0 && i < num_last_resort_candidates; i++) {
437 pipe = &state->res_ctx.pipe_ctx[last_resort_pipe_candidates[i]];
438 if (is_pipe_free(pipe)) {
440 // TODO: This doens't make sense really, pipe_idx should always be valid
441 pipe->pipe_idx = last_resort_pipe_candidates[i];
442 assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
446 ASSERT(pipes_needed == 0); // Validation should prevent us from building a pipe context that exceeds the number of HW resoruces available
448 return pipes_needed == 0;
451 static void sort_pipes_for_splitting(struct dc_plane_pipe_pool *pipes)
453 bool sorted, swapped;
454 unsigned int cur_index;
458 for (odm_slice_index = 0; odm_slice_index < pipes->num_pipes_assigned_to_plane_for_odm_combine; odm_slice_index++) {
459 // Sort each MPCC set
460 //Un-optimized bubble sort, but that's okay for array sizes <= 6
462 if (pipes->num_pipes_assigned_to_plane_for_mpcc_combine <= 1)
470 if (pipes->pipes_assigned_to_plane[odm_slice_index][cur_index] > pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1]) {
471 temp = pipes->pipes_assigned_to_plane[odm_slice_index][cur_index];
472 pipes->pipes_assigned_to_plane[odm_slice_index][cur_index] = pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1];
473 pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1] = temp;
480 if (cur_index == pipes->num_pipes_assigned_to_plane_for_mpcc_combine - 1) {
495 // 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
496 static void calculate_odm_slices(const struct dc_stream_state *stream, unsigned int odm_factor, unsigned int *odm_slice_end_x)
498 unsigned int slice_size = 0;
501 if (odm_factor < 1 || odm_factor > 4) {
506 slice_size = stream->src.width / odm_factor;
508 for (i = 0; i < odm_factor; i++)
509 odm_slice_end_x[i] = (slice_size * (i + 1)) - 1;
511 odm_slice_end_x[odm_factor - 1] = stream->src.width - 1;
514 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)
516 unsigned int slice_start_x, slice_end_x;
518 if (slice_index == 0)
521 slice_start_x = odm_slice_end_x[slice_index - 1] + 1;
523 slice_end_x = odm_slice_end_x[slice_index];
525 if (plane->clip_rect.x + plane->clip_rect.width < slice_start_x)
528 if (plane->clip_rect.x > slice_end_x)
534 static void add_odm_slice_to_odm_tree(struct dml2_context *ctx,
535 struct dc_state *state,
536 struct dc_pipe_mapping_scratch *scratch,
537 unsigned int odm_slice_index)
539 struct pipe_ctx *pipe = NULL;
542 // MPCC Combine + ODM Combine is not supported, so there should never be a case where the current plane
543 // has more than 1 pipe mapped to it for a given slice.
544 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 for (i = 0; i < scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
547 pipe = &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][i]];
549 if (scratch->mpc_info.prev_odm_pipe)
550 scratch->mpc_info.prev_odm_pipe->next_odm_pipe = pipe;
552 pipe->prev_odm_pipe = scratch->mpc_info.prev_odm_pipe;
553 pipe->next_odm_pipe = NULL;
555 scratch->mpc_info.prev_odm_pipe = pipe;
558 static struct pipe_ctx *add_plane_to_blend_tree(struct dml2_context *ctx,
559 struct dc_state *state,
560 const struct dc_plane_state *plane,
561 struct dc_plane_pipe_pool *pipe_pool,
562 unsigned int odm_slice,
563 struct pipe_ctx *top_pipe)
567 for (i = 0; i < pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
569 top_pipe->bottom_pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]];
571 pipe_pool->pipe_used[odm_slice][i] = true;
573 state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]].top_pipe = top_pipe;
574 state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]].bottom_pipe = NULL;
576 top_pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]];
579 // After running the above loop, the top pipe actually ends up pointing to the bottom of this MPCC combine tree, so we are actually
580 // returning the bottom pipe here
584 static unsigned int find_pipes_assigned_to_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int stream_id, unsigned int *pipes)
587 unsigned int num_found = 0;
589 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
590 struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
592 if (pipe->stream && pipe->stream->stream_id == stream_id && !pipe->top_pipe && !pipe->prev_odm_pipe) {
594 pipes[num_found++] = pipe->pipe_idx;
595 pipe = pipe->next_odm_pipe;
604 static struct pipe_ctx *assign_pipes_to_stream(struct dml2_context *ctx, struct dc_state *state,
605 const struct dc_stream_state *stream,
607 struct dc_plane_pipe_pool *pipe_pool,
608 const struct dc_state *existing_state)
610 struct pipe_ctx *master_pipe;
611 unsigned int pipes_needed;
612 unsigned int pipes_assigned;
613 unsigned int pipes[MAX_PIPES] = {0};
614 unsigned int next_pipe_to_assign;
617 pipes_needed = odm_factor;
619 master_pipe = find_master_pipe_of_stream(ctx, state, stream->stream_id);
622 pipes_assigned = find_pipes_assigned_to_stream(ctx, state, stream->stream_id, pipes);
624 find_more_free_pipes(ctx, state, stream->stream_id, pipes, &pipes_assigned, pipes_needed - pipes_assigned, existing_state);
626 ASSERT(pipes_assigned == pipes_needed);
628 next_pipe_to_assign = 0;
629 for (odm_slice = 0; odm_slice < odm_factor; odm_slice++)
630 pipe_pool->pipes_assigned_to_plane[odm_slice][0] = pipes[next_pipe_to_assign++];
632 pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine = 1;
633 pipe_pool->num_pipes_assigned_to_plane_for_odm_combine = odm_factor;
638 static struct pipe_ctx *assign_pipes_to_plane(struct dml2_context *ctx, struct dc_state *state,
639 const struct dc_stream_state *stream,
640 const struct dc_plane_state *plane,
644 struct dc_plane_pipe_pool *pipe_pool,
645 const struct dc_state *existing_state)
647 struct pipe_ctx *master_pipe = NULL;
648 unsigned int plane_id;
649 unsigned int pipes_needed;
650 unsigned int pipes_assigned;
651 unsigned int pipes[MAX_PIPES] = {0};
652 unsigned int next_pipe_to_assign;
653 int odm_slice, mpc_slice;
655 if (!get_plane_id(ctx, state, plane, stream->stream_id, plane_index, &plane_id)) {
660 pipes_needed = mpc_factor * odm_factor;
662 master_pipe = find_master_pipe_of_plane(ctx, state, plane_id);
665 pipes_assigned = find_pipes_assigned_to_plane(ctx, state, plane_id, pipes);
667 find_more_pipes_for_stream(ctx, state, stream->stream_id, pipes, &pipes_assigned, pipes_needed - pipes_assigned, existing_state);
669 ASSERT(pipes_assigned >= pipes_needed);
671 next_pipe_to_assign = 0;
672 for (odm_slice = 0; odm_slice < odm_factor; odm_slice++)
673 for (mpc_slice = 0; mpc_slice < mpc_factor; mpc_slice++)
674 pipe_pool->pipes_assigned_to_plane[odm_slice][mpc_slice] = pipes[next_pipe_to_assign++];
676 pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine = mpc_factor;
677 pipe_pool->num_pipes_assigned_to_plane_for_odm_combine = odm_factor;
682 static bool is_pipe_used(const struct dc_plane_pipe_pool *pool, unsigned int pipe_idx)
686 for (i = 0; i < pool->num_pipes_assigned_to_plane_for_odm_combine; i++) {
687 for (j = 0; j < pool->num_pipes_assigned_to_plane_for_mpcc_combine; j++) {
688 if (pool->pipes_assigned_to_plane[i][j] == pipe_idx && pool->pipe_used[i][j])
696 static void free_pipe(struct pipe_ctx *pipe)
698 memset(pipe, 0, sizeof(struct pipe_ctx));
701 static void free_unused_pipes_for_plane(struct dml2_context *ctx, struct dc_state *state,
702 const struct dc_plane_state *plane, const struct dc_plane_pipe_pool *pool, unsigned int stream_id, int plane_index)
705 bool is_plane_duplicate = ctx->v20.scratch.plane_duplicate_exists;
707 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
708 if (state->res_ctx.pipe_ctx[i].plane_state == plane &&
709 state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id &&
710 (!is_plane_duplicate ||
711 ctx->v20.scratch.dml_to_dc_pipe_mapping.dml_pipe_idx_to_plane_index[state->res_ctx.pipe_ctx[i].pipe_idx] == plane_index) &&
712 !is_pipe_used(pool, state->res_ctx.pipe_ctx[i].pipe_idx)) {
713 free_pipe(&state->res_ctx.pipe_ctx[i]);
718 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 struct pipe_ctx *pipe;
723 for (i = 0; i < pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
724 pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][0]];
726 pipe->top_pipe->bottom_pipe = pipe->bottom_pipe;
728 if (pipe->bottom_pipe)
729 pipe->bottom_pipe = pipe->top_pipe;
731 pipe_pool->pipe_used[odm_slice][i] = true;
735 static void map_pipes_for_stream(struct dml2_context *ctx, struct dc_state *state, const struct dc_stream_state *stream,
736 struct dc_pipe_mapping_scratch *scratch, const struct dc_state *existing_state)
739 struct pipe_ctx *master_pipe = NULL;
742 master_pipe = assign_pipes_to_stream(ctx, state, stream, scratch->odm_info.odm_factor, &scratch->pipe_pool, existing_state);
743 sort_pipes_for_splitting(&scratch->pipe_pool);
745 for (odm_slice_index = 0; odm_slice_index < scratch->odm_info.odm_factor; odm_slice_index++) {
746 remove_pipes_from_blend_trees(ctx, state, &scratch->pipe_pool, odm_slice_index);
748 add_odm_slice_to_odm_tree(ctx, state, scratch, odm_slice_index);
750 ctx->config.callbacks.acquire_secondary_pipe_for_mpc_odm(ctx->config.callbacks.dc, state,
751 master_pipe, &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][0]], true);
755 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,
756 int plane_index, struct dc_pipe_mapping_scratch *scratch, const struct dc_state *existing_state)
759 unsigned int plane_id;
760 struct pipe_ctx *master_pipe = NULL;
763 if (!get_plane_id(ctx, state, plane, stream->stream_id, plane_index, &plane_id)) {
768 master_pipe = assign_pipes_to_plane(ctx, state, stream, plane, scratch->odm_info.odm_factor,
769 scratch->mpc_info.mpc_factor, plane_index, &scratch->pipe_pool, existing_state);
770 sort_pipes_for_splitting(&scratch->pipe_pool);
772 for (odm_slice_index = 0; odm_slice_index < scratch->odm_info.odm_factor; odm_slice_index++) {
773 // We build the tree for one ODM slice at a time.
774 // Each ODM slice shares a common OPP
775 if (!is_plane_in_odm_slice(plane, odm_slice_index, scratch->odm_info.odm_slice_end_x, scratch->odm_info.odm_factor)) {
779 // Now we have a list of all pipes to be used for this plane/stream, now setup the tree.
780 scratch->odm_info.next_higher_pipe_for_odm_slice[odm_slice_index] = add_plane_to_blend_tree(ctx, state,
784 scratch->odm_info.next_higher_pipe_for_odm_slice[odm_slice_index]);
786 add_odm_slice_to_odm_tree(ctx, state, scratch, odm_slice_index);
788 for (i = 0; i < scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
790 ctx->config.callbacks.acquire_secondary_pipe_for_mpc_odm(ctx->config.callbacks.dc, state,
791 master_pipe, &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][i]], true);
795 free_unused_pipes_for_plane(ctx, state, plane, &scratch->pipe_pool, stream->stream_id, plane_index);
798 static unsigned int get_target_mpc_factor(struct dml2_context *ctx,
799 struct dc_state *state,
800 const struct dml_display_cfg_st *disp_cfg,
801 struct dml2_dml_to_dc_pipe_mapping *mapping,
802 const struct dc_stream_status *status,
803 const struct dc_stream_state *stream,
806 unsigned int plane_id;
807 unsigned int cfg_idx;
808 unsigned int mpc_factor;
810 if (ctx->architecture == dml2_architecture_20) {
811 get_plane_id(ctx, state, status->plane_states[plane_idx],
812 stream->stream_id, plane_idx, &plane_id);
813 cfg_idx = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
814 mpc_factor = (unsigned int)disp_cfg->hw.DPPPerSurface[cfg_idx];
815 } else if (ctx->architecture == dml2_architecture_21) {
816 if (ctx->config.svp_pstate.callbacks.get_stream_subvp_type(state, stream) == SUBVP_PHANTOM) {
817 struct dc_stream_state *main_stream;
818 struct dc_stream_status *main_stream_status;
820 /* get stream id of main stream */
821 main_stream = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(state, stream);
827 main_stream_status = ctx->config.callbacks.get_stream_status(state, main_stream);
828 if (!main_stream_status) {
833 /* get plane id for associated main plane */
834 get_plane_id(ctx, state, main_stream_status->plane_states[plane_idx],
835 main_stream->stream_id, plane_idx, &plane_id);
837 get_plane_id(ctx, state, status->plane_states[plane_idx],
838 stream->stream_id, plane_idx, &plane_id);
841 cfg_idx = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
842 mpc_factor = ctx->v21.mode_programming.programming->plane_programming[cfg_idx].num_dpps_required;
848 /* For stereo timings, we need to pipe split */
849 if (dml2_is_stereo_timing(stream))
855 static unsigned int get_target_odm_factor(
856 const struct dml2_context *ctx,
857 struct dc_state *state,
858 const struct dml_display_cfg_st *disp_cfg,
859 struct dml2_dml_to_dc_pipe_mapping *mapping,
860 const struct dc_stream_state *stream)
862 unsigned int cfg_idx;
864 if (ctx->architecture == dml2_architecture_20) {
865 cfg_idx = find_disp_cfg_idx_by_stream_id(
866 mapping, stream->stream_id);
867 switch (disp_cfg->hw.ODMMode[cfg_idx]) {
868 case dml_odm_mode_bypass:
870 case dml_odm_mode_combine_2to1:
872 case dml_odm_mode_combine_4to1:
877 } else if (ctx->architecture == dml2_architecture_21) {
878 if (ctx->config.svp_pstate.callbacks.get_stream_subvp_type(state, stream) == SUBVP_PHANTOM) {
879 struct dc_stream_state *main_stream;
881 /* get stream id of main stream */
882 main_stream = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(state, stream);
886 /* get cfg idx for associated main stream */
887 cfg_idx = find_disp_cfg_idx_by_stream_id(
888 mapping, main_stream->stream_id);
890 cfg_idx = find_disp_cfg_idx_by_stream_id(
891 mapping, stream->stream_id);
894 return ctx->v21.mode_programming.programming->stream_programming[cfg_idx].num_odms_required;
902 static unsigned int get_source_odm_factor(const struct dml2_context *ctx,
903 struct dc_state *state,
904 const struct dc_stream_state *stream)
906 struct pipe_ctx *otg_master = ctx->config.callbacks.get_otg_master_for_stream(&state->res_ctx, stream);
911 return ctx->config.callbacks.get_odm_slice_count(otg_master);
914 static unsigned int get_source_mpc_factor(const struct dml2_context *ctx,
915 struct dc_state *state,
916 const struct dc_plane_state *plane)
918 struct pipe_ctx *dpp_pipes[MAX_PIPES] = {0};
919 int dpp_pipe_count = ctx->config.callbacks.get_dpp_pipes_for_plane(plane,
920 &state->res_ctx, dpp_pipes);
922 ASSERT(dpp_pipe_count > 0);
923 return ctx->config.callbacks.get_mpc_slice_count(dpp_pipes[0]);
927 static void populate_mpc_factors_for_stream(
928 struct dml2_context *ctx,
929 const struct dml_display_cfg_st *disp_cfg,
930 struct dml2_dml_to_dc_pipe_mapping *mapping,
931 struct dc_state *state,
932 unsigned int stream_idx,
933 struct dml2_pipe_combine_factor odm_factor,
934 struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])
936 const struct dc_stream_status *status = &state->stream_status[stream_idx];
939 for (i = 0; i < status->plane_count; i++) {
940 mpc_factors[i].source = get_source_mpc_factor(ctx, state, status->plane_states[i]);
941 mpc_factors[i].target = (odm_factor.target == 1) ?
942 get_target_mpc_factor(ctx, state, disp_cfg, mapping, status, state->streams[stream_idx], i) : 1;
946 static void populate_odm_factors(const struct dml2_context *ctx,
947 const struct dml_display_cfg_st *disp_cfg,
948 struct dml2_dml_to_dc_pipe_mapping *mapping,
949 struct dc_state *state,
950 struct dml2_pipe_combine_factor odm_factors[MAX_PIPES])
954 for (i = 0; i < state->stream_count; i++) {
955 odm_factors[i].source = get_source_odm_factor(ctx, state, state->streams[i]);
956 odm_factors[i].target = get_target_odm_factor(
957 ctx, state, disp_cfg, mapping, state->streams[i]);
961 static bool unmap_dc_pipes_for_stream(struct dml2_context *ctx,
962 struct dc_state *state,
963 const struct dc_state *existing_state,
964 const struct dc_stream_state *stream,
965 const struct dc_stream_status *status,
966 struct dml2_pipe_combine_factor odm_factor,
967 struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])
972 for (plane_idx = 0; plane_idx < status->plane_count; plane_idx++)
973 if (mpc_factors[plane_idx].target < mpc_factors[plane_idx].source)
974 result &= ctx->config.callbacks.update_pipes_for_plane_with_slice_count(
977 ctx->config.callbacks.dc->res_pool,
978 status->plane_states[plane_idx],
979 mpc_factors[plane_idx].target);
980 if (odm_factor.target < odm_factor.source)
981 result &= ctx->config.callbacks.update_pipes_for_stream_with_slice_count(
984 ctx->config.callbacks.dc->res_pool,
990 static bool map_dc_pipes_for_stream(struct dml2_context *ctx,
991 struct dc_state *state,
992 const struct dc_state *existing_state,
993 const struct dc_stream_state *stream,
994 const struct dc_stream_status *status,
995 struct dml2_pipe_combine_factor odm_factor,
996 struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])
1001 for (plane_idx = 0; plane_idx < status->plane_count; plane_idx++)
1002 if (mpc_factors[plane_idx].target > mpc_factors[plane_idx].source)
1003 result &= ctx->config.callbacks.update_pipes_for_plane_with_slice_count(
1006 ctx->config.callbacks.dc->res_pool,
1007 status->plane_states[plane_idx],
1008 mpc_factors[plane_idx].target);
1009 if (odm_factor.target > odm_factor.source)
1010 result &= ctx->config.callbacks.update_pipes_for_stream_with_slice_count(
1013 ctx->config.callbacks.dc->res_pool,
1019 static bool map_dc_pipes_with_callbacks(struct dml2_context *ctx,
1020 struct dc_state *state,
1021 const struct dml_display_cfg_st *disp_cfg,
1022 struct dml2_dml_to_dc_pipe_mapping *mapping,
1023 const struct dc_state *existing_state)
1028 populate_odm_factors(ctx, disp_cfg, mapping, state, ctx->pipe_combine_scratch.odm_factors);
1029 for (i = 0; i < state->stream_count; i++)
1030 populate_mpc_factors_for_stream(ctx, disp_cfg, mapping, state,
1031 i, ctx->pipe_combine_scratch.odm_factors[i], ctx->pipe_combine_scratch.mpc_factors[i]);
1032 for (i = 0; i < state->stream_count; i++)
1033 result &= unmap_dc_pipes_for_stream(ctx, state, existing_state, state->streams[i],
1034 &state->stream_status[i], ctx->pipe_combine_scratch.odm_factors[i], ctx->pipe_combine_scratch.mpc_factors[i]);
1035 for (i = 0; i < state->stream_count; i++)
1036 result &= map_dc_pipes_for_stream(ctx, state, existing_state, state->streams[i],
1037 &state->stream_status[i], ctx->pipe_combine_scratch.odm_factors[i], ctx->pipe_combine_scratch.mpc_factors[i]);
1042 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)
1044 int stream_index, plane_index, i;
1046 unsigned int stream_disp_cfg_index;
1047 unsigned int plane_disp_cfg_index;
1048 unsigned int disp_cfg_index_max;
1050 unsigned int plane_id;
1051 unsigned int stream_id;
1053 const unsigned int *ODMMode, *DPPPerSurface;
1054 unsigned int odm_mode_array[__DML2_WRAPPER_MAX_STREAMS_PLANES__] = {0}, dpp_per_surface_array[__DML2_WRAPPER_MAX_STREAMS_PLANES__] = {0};
1055 struct dc_pipe_mapping_scratch scratch;
1057 if (ctx->config.map_dc_pipes_with_callbacks)
1058 return map_dc_pipes_with_callbacks(
1059 ctx, state, disp_cfg, mapping, existing_state);
1061 if (ctx->architecture == dml2_architecture_21) {
1063 * Extract ODM and DPP outputs from DML2.1 and map them in an array as required for pipe mapping in dml2_map_dc_pipes.
1064 * As data cannot be directly extracted in const pointers, assign these arrays to const pointers before proceeding to
1065 * maximize the reuse of existing code. Const pointers are required because dml2.0 dml_display_cfg_st is const.
1068 for (i = 0; i < __DML2_WRAPPER_MAX_STREAMS_PLANES__; i++) {
1069 odm_mode_array[i] = ctx->v21.mode_programming.programming->stream_programming[i].num_odms_required;
1070 dpp_per_surface_array[i] = ctx->v21.mode_programming.programming->plane_programming[i].num_dpps_required;
1073 ODMMode = (const unsigned int *)odm_mode_array;
1074 DPPPerSurface = (const unsigned int *)dpp_per_surface_array;
1075 disp_cfg_index_max = __DML2_WRAPPER_MAX_STREAMS_PLANES__;
1077 ODMMode = (unsigned int *)disp_cfg->hw.ODMMode;
1078 DPPPerSurface = disp_cfg->hw.DPPPerSurface;
1079 disp_cfg_index_max = __DML_NUM_PLANES__;
1082 for (stream_index = 0; stream_index < state->stream_count; stream_index++) {
1083 memset(&scratch, 0, sizeof(struct dc_pipe_mapping_scratch));
1085 stream_id = state->streams[stream_index]->stream_id;
1086 stream_disp_cfg_index = find_disp_cfg_idx_by_stream_id(mapping, stream_id);
1087 if (stream_disp_cfg_index >= disp_cfg_index_max)
1090 if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_bypass) {
1091 scratch.odm_info.odm_factor = 1;
1092 } else if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_combine_2to1) {
1093 scratch.odm_info.odm_factor = 2;
1094 } else if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_combine_4to1) {
1095 scratch.odm_info.odm_factor = 4;
1098 scratch.odm_info.odm_factor = 1;
1101 /* After DML2.1 update, ODM interpretation needs to change and is no longer same as for DML2.0.
1102 * This is not an issue with new resource management logic. This block ensure backcompat
1103 * with legacy pipe management with updated DML.
1105 if (ctx->architecture == dml2_architecture_21) {
1106 if (ODMMode[stream_disp_cfg_index] == 1) {
1107 scratch.odm_info.odm_factor = 1;
1108 } else if (ODMMode[stream_disp_cfg_index] == 2) {
1109 scratch.odm_info.odm_factor = 2;
1110 } else if (ODMMode[stream_disp_cfg_index] == 4) {
1111 scratch.odm_info.odm_factor = 4;
1114 scratch.odm_info.odm_factor = 1;
1117 calculate_odm_slices(state->streams[stream_index], scratch.odm_info.odm_factor, scratch.odm_info.odm_slice_end_x);
1119 // If there are no planes, you still want to setup ODM...
1120 if (state->stream_status[stream_index].plane_count == 0) {
1121 map_pipes_for_stream(ctx, state, state->streams[stream_index], &scratch, existing_state);
1124 for (plane_index = 0; plane_index < state->stream_status[stream_index].plane_count; plane_index++) {
1125 // Planes are ordered top to bottom.
1126 if (get_plane_id(ctx, state, state->stream_status[stream_index].plane_states[plane_index],
1127 stream_id, plane_index, &plane_id)) {
1128 plane_disp_cfg_index = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
1130 // Setup mpc_info for this plane
1131 scratch.mpc_info.prev_odm_pipe = NULL;
1132 if (scratch.odm_info.odm_factor == 1 && plane_disp_cfg_index < disp_cfg_index_max) {
1133 // If ODM combine is not inuse, then the number of pipes
1134 // per plane is determined by MPC combine factor
1135 scratch.mpc_info.mpc_factor = DPPPerSurface[plane_disp_cfg_index];
1137 //For stereo timings, we need to pipe split
1138 if (dml2_is_stereo_timing(state->streams[stream_index]))
1139 scratch.mpc_info.mpc_factor = 2;
1141 // If ODM combine is enabled, then we use at most 1 pipe per
1142 // odm slice per plane, i.e. MPC combine is never used
1143 scratch.mpc_info.mpc_factor = 1;
1146 ASSERT(scratch.odm_info.odm_factor * scratch.mpc_info.mpc_factor > 0);
1148 // Clear the pool assignment scratch (which is per plane)
1149 memset(&scratch.pipe_pool, 0, sizeof(struct dc_plane_pipe_pool));
1151 map_pipes_for_plane(ctx, state, state->streams[stream_index],
1152 state->stream_status[stream_index].plane_states[plane_index], plane_index, &scratch, existing_state);
1154 // Plane ID cannot be generated, therefore no DML mapping can be performed.
1161 if (!validate_pipe_assignment(ctx, state, disp_cfg, mapping))
1164 for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
1165 struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
1167 if (pipe->plane_state) {
1168 if (!ctx->config.callbacks.build_scaling_params(pipe)) {
1173 if (ctx->config.callbacks.build_test_pattern_params &&
1175 pipe->prev_odm_pipe == NULL &&
1176 pipe->top_pipe == NULL)
1177 ctx->config.callbacks.build_test_pattern_params(&state->res_ctx, pipe);