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