1 /* SPDX-License-Identifier: MIT */
2 /*
3  * Copyright 2023 Advanced Micro Devices, Inc.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21  * OTHER DEALINGS IN THE SOFTWARE.
22  *
23  * Authors: AMD
24  *
25  */
26 
27 #include "dml2_mall_phantom.h"
28 
29 #include "dml2_dc_types.h"
30 #include "dml2_internal_types.h"
31 #include "dml2_utils.h"
32 #include "dml2_dc_resource_mgmt.h"
33 
34 #define MAX_ODM_FACTOR 4
35 #define MAX_MPCC_FACTOR 4
36 
37 struct dc_plane_pipe_pool {
38 	int pipes_assigned_to_plane[MAX_ODM_FACTOR][MAX_MPCC_FACTOR];
39 	bool pipe_used[MAX_ODM_FACTOR][MAX_MPCC_FACTOR];
40 	int num_pipes_assigned_to_plane_for_mpcc_combine;
41 	int num_pipes_assigned_to_plane_for_odm_combine;
42 };
43 
44 struct dc_pipe_mapping_scratch {
45 	struct {
46 		unsigned int odm_factor;
47 		unsigned int odm_slice_end_x[MAX_PIPES];
48 		struct pipe_ctx *next_higher_pipe_for_odm_slice[MAX_PIPES];
49 	} odm_info;
50 	struct {
51 		unsigned int mpc_factor;
52 		struct pipe_ctx *prev_odm_pipe;
53 	} mpc_info;
54 
55 	struct dc_plane_pipe_pool pipe_pool;
56 };
57 
get_plane_id(struct dml2_context * dml2,const struct dc_state * state,const struct dc_plane_state * plane,unsigned int stream_id,unsigned int plane_index,unsigned int * plane_id)58 static bool get_plane_id(struct dml2_context *dml2, const struct dc_state *state, const struct dc_plane_state *plane,
59 	unsigned int stream_id, unsigned int plane_index, unsigned int *plane_id)
60 {
61 	int i, j;
62 	bool is_plane_duplicate = dml2->v20.scratch.plane_duplicate_exists;
63 
64 	if (!plane_id)
65 		return false;
66 
67 	for (i = 0; i < state->stream_count; i++) {
68 		if (state->streams[i]->stream_id == stream_id) {
69 			for (j = 0; j < state->stream_status[i].plane_count; j++) {
70 				if (state->stream_status[i].plane_states[j] == plane &&
71 					(!is_plane_duplicate || (j == plane_index))) {
72 					*plane_id = (i << 16) | j;
73 					return true;
74 				}
75 			}
76 		}
77 	}
78 
79 	return false;
80 }
81 
find_disp_cfg_idx_by_plane_id(struct dml2_dml_to_dc_pipe_mapping * mapping,unsigned int plane_id)82 static int find_disp_cfg_idx_by_plane_id(struct dml2_dml_to_dc_pipe_mapping *mapping, unsigned int plane_id)
83 {
84 	int i;
85 
86 	for (i = 0; i < __DML2_WRAPPER_MAX_STREAMS_PLANES__; i++) {
87 		if (mapping->disp_cfg_to_plane_id_valid[i] && mapping->disp_cfg_to_plane_id[i] == plane_id)
88 			return  i;
89 	}
90 
91 	ASSERT(false);
92 	return __DML2_WRAPPER_MAX_STREAMS_PLANES__;
93 }
94 
find_disp_cfg_idx_by_stream_id(struct dml2_dml_to_dc_pipe_mapping * mapping,unsigned int stream_id)95 static int find_disp_cfg_idx_by_stream_id(struct dml2_dml_to_dc_pipe_mapping *mapping, unsigned int stream_id)
96 {
97 	int i;
98 
99 	for (i = 0; i < __DML2_WRAPPER_MAX_STREAMS_PLANES__; i++) {
100 		if (mapping->disp_cfg_to_stream_id_valid[i] && mapping->disp_cfg_to_stream_id[i] == stream_id)
101 			return  i;
102 	}
103 
104 	ASSERT(false);
105 	return __DML2_WRAPPER_MAX_STREAMS_PLANES__;
106 }
107 
108 // The master pipe of a stream is defined as the top pipe in odm slice 0
find_master_pipe_of_stream(struct dml2_context * ctx,struct dc_state * state,unsigned int stream_id)109 static struct pipe_ctx *find_master_pipe_of_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int stream_id)
110 {
111 	int i;
112 
113 	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
114 		if (state->res_ctx.pipe_ctx[i].stream && state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id) {
115 			if (!state->res_ctx.pipe_ctx[i].prev_odm_pipe && !state->res_ctx.pipe_ctx[i].top_pipe)
116 				return &state->res_ctx.pipe_ctx[i];
117 		}
118 	}
119 
120 	return NULL;
121 }
122 
find_master_pipe_of_plane(struct dml2_context * ctx,struct dc_state * state,unsigned int plane_id)123 static struct pipe_ctx *find_master_pipe_of_plane(struct dml2_context *ctx,
124 	struct dc_state *state, unsigned int plane_id)
125 {
126 	int i;
127 	unsigned int plane_id_assigned_to_pipe;
128 
129 	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
130 		if (state->res_ctx.pipe_ctx[i].plane_state && get_plane_id(ctx, state, state->res_ctx.pipe_ctx[i].plane_state,
131 			state->res_ctx.pipe_ctx[i].stream->stream_id,
132 			ctx->v20.scratch.dml_to_dc_pipe_mapping.dml_pipe_idx_to_plane_index[state->res_ctx.pipe_ctx[i].pipe_idx], &plane_id_assigned_to_pipe)) {
133 			if (plane_id_assigned_to_pipe == plane_id)
134 				return &state->res_ctx.pipe_ctx[i];
135 		}
136 	}
137 
138 	return NULL;
139 }
140 
find_pipes_assigned_to_plane(struct dml2_context * ctx,struct dc_state * state,unsigned int plane_id,unsigned int * pipes)141 static unsigned int find_pipes_assigned_to_plane(struct dml2_context *ctx,
142 	struct dc_state *state, unsigned int plane_id, unsigned int *pipes)
143 {
144 	int i;
145 	unsigned int num_found = 0;
146 	unsigned int plane_id_assigned_to_pipe = -1;
147 
148 	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
149 		struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
150 
151 		if (!pipe->plane_state || !pipe->stream)
152 			continue;
153 
154 		get_plane_id(ctx, state, pipe->plane_state, pipe->stream->stream_id,
155 					ctx->v20.scratch.dml_to_dc_pipe_mapping.dml_pipe_idx_to_plane_index[pipe->pipe_idx],
156 					&plane_id_assigned_to_pipe);
157 		if (plane_id_assigned_to_pipe == plane_id && !pipe->prev_odm_pipe
158 				&& (!pipe->top_pipe || pipe->top_pipe->plane_state != pipe->plane_state)) {
159 			while (pipe) {
160 				struct pipe_ctx *mpc_pipe = pipe;
161 
162 				while (mpc_pipe) {
163 					pipes[num_found++] = mpc_pipe->pipe_idx;
164 					mpc_pipe = mpc_pipe->bottom_pipe;
165 					if (!mpc_pipe)
166 						break;
167 					if (mpc_pipe->plane_state != pipe->plane_state)
168 						mpc_pipe = NULL;
169 				}
170 				pipe = pipe->next_odm_pipe;
171 			}
172 			break;
173 		}
174 	}
175 
176 	return num_found;
177 }
178 
validate_pipe_assignment(const struct dml2_context * ctx,const struct dc_state * state,const struct dml_display_cfg_st * disp_cfg,const struct dml2_dml_to_dc_pipe_mapping * mapping)179 static bool validate_pipe_assignment(const struct dml2_context *ctx, const struct dc_state *state, const struct dml_display_cfg_st *disp_cfg, const struct dml2_dml_to_dc_pipe_mapping *mapping)
180 {
181 //	int i, j, k;
182 //
183 //	unsigned int plane_id;
184 //
185 //	unsigned int disp_cfg_index;
186 //
187 //	unsigned int pipes_assigned_to_plane[MAX_PIPES];
188 //	unsigned int num_pipes_assigned_to_plane;
189 //
190 //	struct pipe_ctx *top_pipe;
191 //
192 //	for (i = 0; i < state->stream_count; i++) {
193 //		for (j = 0; j < state->stream_status[i]->plane_count; j++) {
194 //			if (get_plane_id(state, state->stream_status.plane_states[j], &plane_id)) {
195 //				disp_cfg_index = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
196 //				num_pipes_assigned_to_plane = find_pipes_assigned_to_plane(ctx, state, plane_id, pipes_assigned_to_plane);
197 //
198 //				if (disp_cfg_index >= 0 && num_pipes_assigned_to_plane > 0) {
199 //					// Verify the number of pipes assigned matches
200 //					if (disp_cfg->hw.DPPPerSurface != num_pipes_assigned_to_plane)
201 //						return false;
202 //
203 //					top_pipe = find_top_pipe_in_tree(state->res_ctx.pipe_ctx[pipes_assigned_to_plane[0]]);
204 //
205 //					// Verify MPC and ODM combine
206 //					if (disp_cfg->hw.ODMMode == dml_odm_mode_bypass) {
207 //						verify_combine_tree(top_pipe, state->streams[i]->stream_id, plane_id, state, false);
208 //					} else {
209 //						verify_combine_tree(top_pipe, state->streams[i]->stream_id, plane_id, state, true);
210 //					}
211 //
212 //					// TODO: could also do additional verification that the pipes in tree are the same as
213 //					// pipes_assigned_to_plane
214 //				} else {
215 //					ASSERT(false);
216 //					return false;
217 //				}
218 //			} else {
219 //				ASSERT(false);
220 //				return false;
221 //			}
222 //		}
223 //	}
224 	return true;
225 }
226 
is_plane_using_pipe(const struct pipe_ctx * pipe)227 static bool is_plane_using_pipe(const struct pipe_ctx *pipe)
228 {
229 	if (pipe->plane_state)
230 		return true;
231 
232 	return false;
233 }
234 
is_pipe_free(const struct pipe_ctx * pipe)235 static bool is_pipe_free(const struct pipe_ctx *pipe)
236 {
237 	if (!pipe->plane_state && !pipe->stream)
238 		return true;
239 
240 	return false;
241 }
242 
find_preferred_pipe_candidates(const struct dc_state * existing_state,const int pipe_count,const unsigned int stream_id,unsigned int * preferred_pipe_candidates)243 static unsigned int find_preferred_pipe_candidates(const struct dc_state *existing_state,
244 	const int pipe_count,
245 	const unsigned int stream_id,
246 	unsigned int *preferred_pipe_candidates)
247 {
248 	unsigned int num_preferred_candidates = 0;
249 	int i;
250 
251 	/* There is only one case which we consider for adding a pipe to the preferred
252 	 * pipe candidate array:
253 	 *
254 	 * 1. If the existing stream id of the pipe is equivalent to the stream id
255 	 * of the stream we are trying to achieve MPC/ODM combine for. This allows
256 	 * us to minimize the changes in pipe topology during the transition.
257 	 *
258 	 * However this condition comes with a caveat. We need to ignore pipes that will
259 	 * require a change in OPP but still have the same stream id. For example during
260 	 * an MPC to ODM transiton.
261 	 */
262 	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)
267 					continue;
268 
269 				preferred_pipe_candidates[num_preferred_candidates++] = i;
270 			}
271 		}
272 	}
273 
274 	return num_preferred_candidates;
275 }
276 
find_last_resort_pipe_candidates(const struct dc_state * existing_state,const int pipe_count,const unsigned int stream_id,unsigned int * last_resort_pipe_candidates)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)
281 {
282 	unsigned int num_last_resort_candidates = 0;
283 	int i;
284 
285 	/* There are two cases where we would like to add a given pipe into the last
286 	 * candidate array:
287 	 *
288 	 * 1. If the pipe requires a change in OPP, for example during an MPC
289 	 * to ODM transiton.
290 	 *
291 	 * 2. If the pipe already has an enabled OTG.
292 	 */
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;
299 		}
300 	}
301 
302 	return num_last_resort_candidates;
303 }
304 
is_pipe_in_candidate_array(const unsigned int pipe_idx,const unsigned int * candidate_array,const unsigned int candidate_array_size)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)
308 {
309 	int i;
310 
311 	for (i = 0; i < candidate_array_size; i++) {
312 		if (candidate_array[i] == pipe_idx)
313 			return true;
314 	}
315 
316 	return false;
317 }
318 
find_more_pipes_for_stream(struct dml2_context * ctx,struct dc_state * state,unsigned int stream_id,int * assigned_pipes,int * assigned_pipe_count,int pipes_needed,const struct dc_state * existing_state)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
322 	int *assigned_pipes,
323 	int *assigned_pipe_count,
324 	int pipes_needed,
325 	const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to
326 {
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;
332 	int i;
333 
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);
337 
338 		num_last_resort_candidates =
339 			find_last_resort_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, last_resort_pipe_candidates);
340 	}
341 
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)) {
346 			pipes_needed--;
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;
350 		}
351 	}
352 
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))
358 			continue;
359 
360 		pipe = &state->res_ctx.pipe_ctx[i];
361 		if (!is_plane_using_pipe(pipe)) {
362 			pipes_needed--;
363 			// TODO: This doens't make sense really, pipe_idx should always be valid
364 			pipe->pipe_idx = i;
365 			assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
366 		}
367 	}
368 
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)) {
373 			pipes_needed--;
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;
377 		}
378 	}
379 
380 	ASSERT(pipes_needed <= 0); // Validation should prevent us from building a pipe context that exceeds the number of HW resoruces available
381 
382 	return pipes_needed <= 0;
383 }
384 
find_more_free_pipes(struct dml2_context * ctx,struct dc_state * state,unsigned int stream_id,int * assigned_pipes,int * assigned_pipe_count,int pipes_needed,const struct dc_state * existing_state)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
388 	int *assigned_pipes,
389 	int *assigned_pipe_count,
390 	int pipes_needed,
391 	const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to
392 {
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;
398 	int i;
399 
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);
403 
404 		num_last_resort_candidates =
405 			find_last_resort_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, last_resort_pipe_candidates);
406 	}
407 
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)) {
412 			pipes_needed--;
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;
416 		}
417 	}
418 
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))
424 			continue;
425 
426 		pipe = &state->res_ctx.pipe_ctx[i];
427 		if (is_pipe_free(pipe)) {
428 			pipes_needed--;
429 			// TODO: This doens't make sense really, pipe_idx should always be valid
430 			pipe->pipe_idx = i;
431 			assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
432 		}
433 	}
434 
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)) {
439 			pipes_needed--;
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;
443 		}
444 	}
445 
446 	ASSERT(pipes_needed == 0); // Validation should prevent us from building a pipe context that exceeds the number of HW resoruces available
447 
448 	return pipes_needed == 0;
449 }
450 
sort_pipes_for_splitting(struct dc_plane_pipe_pool * pipes)451 static void sort_pipes_for_splitting(struct dc_plane_pipe_pool *pipes)
452 {
453 	bool sorted, swapped;
454 	unsigned int cur_index;
455 	unsigned int temp;
456 	int odm_slice_index;
457 
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
461 
462 		if (pipes->num_pipes_assigned_to_plane_for_mpcc_combine <= 1)
463 			sorted = true;
464 		else
465 			sorted = false;
466 
467 		cur_index = 0;
468 		swapped = false;
469 		while (!sorted) {
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;
474 
475 				swapped = true;
476 			}
477 
478 			cur_index++;
479 
480 			if (cur_index == pipes->num_pipes_assigned_to_plane_for_mpcc_combine - 1) {
481 				cur_index = 0;
482 
483 				if (swapped)
484 					sorted = false;
485 				else
486 					sorted = true;
487 
488 				swapped = false;
489 			}
490 
491 		}
492 	}
493 }
494 
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
calculate_odm_slices(const struct dc_stream_state * stream,unsigned int odm_factor,unsigned int * odm_slice_end_x)496 static void calculate_odm_slices(const struct dc_stream_state *stream, unsigned int odm_factor, unsigned int *odm_slice_end_x)
497 {
498 	unsigned int slice_size = 0;
499 	int i;
500 
501 	if (odm_factor < 1 || odm_factor > 4) {
502 		ASSERT(false);
503 		return;
504 	}
505 
506 	slice_size = stream->src.width / odm_factor;
507 
508 	for (i = 0; i < odm_factor; i++)
509 		odm_slice_end_x[i] = (slice_size * (i + 1)) - 1;
510 
511 	odm_slice_end_x[odm_factor - 1] = stream->src.width - 1;
512 }
513 
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)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)
515 {
516 	unsigned int slice_start_x, slice_end_x;
517 
518 	if (slice_index == 0)
519 		slice_start_x = 0;
520 	else
521 		slice_start_x = odm_slice_end_x[slice_index - 1] + 1;
522 
523 	slice_end_x = odm_slice_end_x[slice_index];
524 
525 	if (plane->clip_rect.x + plane->clip_rect.width < slice_start_x)
526 		return false;
527 
528 	if (plane->clip_rect.x > slice_end_x)
529 		return false;
530 
531 	return true;
532 }
533 
add_odm_slice_to_odm_tree(struct dml2_context * ctx,struct dc_state * state,struct dc_pipe_mapping_scratch * scratch,unsigned int odm_slice_index)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)
538 {
539 	struct pipe_ctx *pipe = NULL;
540 	int i;
541 
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);
545 
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]];
548 
549 		if (scratch->mpc_info.prev_odm_pipe)
550 			scratch->mpc_info.prev_odm_pipe->next_odm_pipe = pipe;
551 
552 		pipe->prev_odm_pipe = scratch->mpc_info.prev_odm_pipe;
553 		pipe->next_odm_pipe = NULL;
554 	}
555 	scratch->mpc_info.prev_odm_pipe = pipe;
556 }
557 
add_plane_to_blend_tree(struct dml2_context * ctx,struct dc_state * state,const struct dc_plane_state * plane,struct dc_plane_pipe_pool * pipe_pool,unsigned int odm_slice,struct pipe_ctx * top_pipe)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)
564 {
565 	int i;
566 
567 	for (i = 0; i < pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
568 		if (top_pipe)
569 			top_pipe->bottom_pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]];
570 
571 		pipe_pool->pipe_used[odm_slice][i] = true;
572 
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;
575 
576 		top_pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]];
577 	}
578 
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
581 	return top_pipe;
582 }
583 
find_pipes_assigned_to_stream(struct dml2_context * ctx,struct dc_state * state,unsigned int stream_id,unsigned int * pipes)584 static unsigned int find_pipes_assigned_to_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int stream_id, unsigned int *pipes)
585 {
586 	int i;
587 	unsigned int num_found = 0;
588 
589 	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
590 		struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
591 
592 		if (pipe->stream && pipe->stream->stream_id == stream_id && !pipe->top_pipe && !pipe->prev_odm_pipe) {
593 			while (pipe) {
594 				pipes[num_found++] = pipe->pipe_idx;
595 				pipe = pipe->next_odm_pipe;
596 			}
597 			break;
598 		}
599 	}
600 
601 	return num_found;
602 }
603 
assign_pipes_to_stream(struct dml2_context * ctx,struct dc_state * state,const struct dc_stream_state * stream,int odm_factor,struct dc_plane_pipe_pool * pipe_pool,const struct dc_state * existing_state)604 static struct pipe_ctx *assign_pipes_to_stream(struct dml2_context *ctx, struct dc_state *state,
605 		const struct dc_stream_state *stream,
606 		int odm_factor,
607 		struct dc_plane_pipe_pool *pipe_pool,
608 		const struct dc_state *existing_state)
609 {
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;
615 	int odm_slice;
616 
617 	pipes_needed = odm_factor;
618 
619 	master_pipe = find_master_pipe_of_stream(ctx, state, stream->stream_id);
620 	ASSERT(master_pipe);
621 
622 	pipes_assigned = find_pipes_assigned_to_stream(ctx, state, stream->stream_id, pipes);
623 
624 	find_more_free_pipes(ctx, state, stream->stream_id, pipes, &pipes_assigned, pipes_needed - pipes_assigned, existing_state);
625 
626 	ASSERT(pipes_assigned == pipes_needed);
627 
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++];
631 
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;
634 
635 	return master_pipe;
636 }
637 
assign_pipes_to_plane(struct dml2_context * ctx,struct dc_state * state,const struct dc_stream_state * stream,const struct dc_plane_state * plane,int odm_factor,int mpc_factor,int plane_index,struct dc_plane_pipe_pool * pipe_pool,const struct dc_state * existing_state)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,
641 		int odm_factor,
642 		int mpc_factor,
643 		int plane_index,
644 		struct dc_plane_pipe_pool *pipe_pool,
645 		const struct dc_state *existing_state)
646 {
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;
654 
655 	if (!get_plane_id(ctx, state, plane, stream->stream_id, plane_index, &plane_id)) {
656 		ASSERT(false);
657 		return master_pipe;
658 	}
659 
660 	pipes_needed = mpc_factor * odm_factor;
661 
662 	master_pipe = find_master_pipe_of_plane(ctx, state, plane_id);
663 	ASSERT(master_pipe);
664 
665 	pipes_assigned = find_pipes_assigned_to_plane(ctx, state, plane_id, pipes);
666 
667 	find_more_pipes_for_stream(ctx, state, stream->stream_id, pipes, &pipes_assigned, pipes_needed - pipes_assigned, existing_state);
668 
669 	ASSERT(pipes_assigned >= pipes_needed);
670 
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++];
675 
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;
678 
679 	return master_pipe;
680 }
681 
is_pipe_used(const struct dc_plane_pipe_pool * pool,unsigned int pipe_idx)682 static bool is_pipe_used(const struct dc_plane_pipe_pool *pool, unsigned int pipe_idx)
683 {
684 	int i, j;
685 
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])
689 				return true;
690 		}
691 	}
692 
693 	return false;
694 }
695 
free_pipe(struct pipe_ctx * pipe)696 static void free_pipe(struct pipe_ctx *pipe)
697 {
698 	memset(pipe, 0, sizeof(struct pipe_ctx));
699 }
700 
free_unused_pipes_for_plane(struct dml2_context * ctx,struct dc_state * state,const struct dc_plane_state * plane,const struct dc_plane_pipe_pool * pool,unsigned int stream_id,int plane_index)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)
703 {
704 	int i;
705 	bool is_plane_duplicate = ctx->v20.scratch.plane_duplicate_exists;
706 
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]);
714 		}
715 	}
716 }
717 
remove_pipes_from_blend_trees(struct dml2_context * ctx,struct dc_state * state,struct dc_plane_pipe_pool * pipe_pool,unsigned int odm_slice)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)
719 {
720 	struct pipe_ctx *pipe;
721 	int i;
722 
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]];
725 		if (pipe->top_pipe)
726 			pipe->top_pipe->bottom_pipe = pipe->bottom_pipe;
727 
728 		if (pipe->bottom_pipe)
729 			pipe->bottom_pipe = pipe->top_pipe;
730 
731 		pipe_pool->pipe_used[odm_slice][i] = true;
732 	}
733 }
734 
map_pipes_for_stream(struct dml2_context * ctx,struct dc_state * state,const struct dc_stream_state * stream,struct dc_pipe_mapping_scratch * scratch,const struct dc_state * existing_state)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)
737 {
738 	int odm_slice_index;
739 	struct pipe_ctx *master_pipe = NULL;
740 
741 
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);
744 
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);
747 
748 		add_odm_slice_to_odm_tree(ctx, state, scratch, odm_slice_index);
749 
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);
752 	}
753 }
754 
map_pipes_for_plane(struct dml2_context * ctx,struct dc_state * state,const struct dc_stream_state * stream,const struct dc_plane_state * plane,int plane_index,struct dc_pipe_mapping_scratch * scratch,const struct dc_state * existing_state)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)
757 {
758 	int odm_slice_index;
759 	unsigned int plane_id;
760 	struct pipe_ctx *master_pipe = NULL;
761 	int i;
762 
763 	if (!get_plane_id(ctx, state, plane, stream->stream_id, plane_index, &plane_id)) {
764 		ASSERT(false);
765 		return;
766 	}
767 
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);
771 
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)) {
776 			continue;
777 		}
778 
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,
781 				plane,
782 				&scratch->pipe_pool,
783 				odm_slice_index,
784 				scratch->odm_info.next_higher_pipe_for_odm_slice[odm_slice_index]);
785 
786 		add_odm_slice_to_odm_tree(ctx, state, scratch, odm_slice_index);
787 
788 		for (i = 0; i < scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
789 
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);
792 		}
793 	}
794 
795 	free_unused_pipes_for_plane(ctx, state, plane, &scratch->pipe_pool, stream->stream_id, plane_index);
796 }
797 
get_target_mpc_factor(struct dml2_context * ctx,struct dc_state * state,const struct dml_display_cfg_st * disp_cfg,struct dml2_dml_to_dc_pipe_mapping * mapping,const struct dc_stream_status * status,const struct dc_stream_state * stream,int plane_idx)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,
804 		int plane_idx)
805 {
806 	unsigned int plane_id;
807 	unsigned int cfg_idx;
808 	unsigned int mpc_factor;
809 
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;
819 
820 			/* get stream id of main stream */
821 			main_stream = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(state, stream);
822 			if (!main_stream) {
823 				ASSERT(false);
824 				return 1;
825 			}
826 
827 			main_stream_status = ctx->config.callbacks.get_stream_status(state, main_stream);
828 			if (!main_stream_status) {
829 				ASSERT(false);
830 				return 1;
831 			}
832 
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);
836 		} else {
837 			get_plane_id(ctx, state, status->plane_states[plane_idx],
838 					stream->stream_id, plane_idx, &plane_id);
839 		}
840 
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;
843 	} else {
844 		mpc_factor = 1;
845 		ASSERT(false);
846 	}
847 
848 	/* For stereo timings, we need to pipe split */
849 	if (dml2_is_stereo_timing(stream))
850 		mpc_factor = 2;
851 
852 	return mpc_factor;
853 }
854 
get_target_odm_factor(const struct dml2_context * ctx,struct dc_state * state,const struct dml_display_cfg_st * disp_cfg,struct dml2_dml_to_dc_pipe_mapping * mapping,const struct dc_stream_state * stream)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)
861 {
862 	unsigned int cfg_idx;
863 
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:
869 			return 1;
870 		case dml_odm_mode_combine_2to1:
871 			return 2;
872 		case dml_odm_mode_combine_4to1:
873 			return 4;
874 		default:
875 			break;
876 		}
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;
880 
881 			/* get stream id of main stream */
882 			main_stream = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(state, stream);
883 			if (!main_stream)
884 				goto failed;
885 
886 			/* get cfg idx for associated main stream */
887 			cfg_idx = find_disp_cfg_idx_by_stream_id(
888 					mapping, main_stream->stream_id);
889 		} else {
890 			cfg_idx = find_disp_cfg_idx_by_stream_id(
891 					mapping, stream->stream_id);
892 		}
893 
894 		return ctx->v21.mode_programming.programming->stream_programming[cfg_idx].num_odms_required;
895 	}
896 
897 failed:
898 	ASSERT(false);
899 	return 1;
900 }
901 
get_source_odm_factor(const struct dml2_context * ctx,struct dc_state * state,const struct dc_stream_state * stream)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)
905 {
906 	struct pipe_ctx *otg_master = ctx->config.callbacks.get_otg_master_for_stream(&state->res_ctx, stream);
907 
908 	if (!otg_master)
909 		return 0;
910 
911 	return ctx->config.callbacks.get_odm_slice_count(otg_master);
912 }
913 
get_source_mpc_factor(const struct dml2_context * ctx,struct dc_state * state,const struct dc_plane_state * plane)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)
917 {
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);
921 
922 	ASSERT(dpp_pipe_count > 0);
923 	return ctx->config.callbacks.get_mpc_slice_count(dpp_pipes[0]);
924 }
925 
926 
populate_mpc_factors_for_stream(struct dml2_context * ctx,const struct dml_display_cfg_st * disp_cfg,struct dml2_dml_to_dc_pipe_mapping * mapping,struct dc_state * state,unsigned int stream_idx,struct dml2_pipe_combine_factor odm_factor,struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])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])
935 {
936 	const struct dc_stream_status *status = &state->stream_status[stream_idx];
937 	int i;
938 
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;
943 	}
944 }
945 
populate_odm_factors(const struct dml2_context * ctx,const struct dml_display_cfg_st * disp_cfg,struct dml2_dml_to_dc_pipe_mapping * mapping,struct dc_state * state,struct dml2_pipe_combine_factor odm_factors[MAX_PIPES])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])
951 {
952 	int i;
953 
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]);
958 	}
959 }
960 
unmap_dc_pipes_for_stream(struct dml2_context * ctx,struct dc_state * state,const struct dc_state * existing_state,const struct dc_stream_state * stream,const struct dc_stream_status * status,struct dml2_pipe_combine_factor odm_factor,struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])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])
968 {
969 	int plane_idx;
970 	bool result = true;
971 
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(
975 					state,
976 					existing_state,
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(
982 				state,
983 				existing_state,
984 				ctx->config.callbacks.dc->res_pool,
985 				stream,
986 				odm_factor.target);
987 	return result;
988 }
989 
map_dc_pipes_for_stream(struct dml2_context * ctx,struct dc_state * state,const struct dc_state * existing_state,const struct dc_stream_state * stream,const struct dc_stream_status * status,struct dml2_pipe_combine_factor odm_factor,struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])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])
997 {
998 	int plane_idx;
999 	bool result = true;
1000 
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(
1004 					state,
1005 					existing_state,
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(
1011 				state,
1012 				existing_state,
1013 				ctx->config.callbacks.dc->res_pool,
1014 				stream,
1015 				odm_factor.target);
1016 	return result;
1017 }
1018 
map_dc_pipes_with_callbacks(struct dml2_context * ctx,struct dc_state * state,const struct dml_display_cfg_st * disp_cfg,struct dml2_dml_to_dc_pipe_mapping * mapping,const struct dc_state * existing_state)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)
1024 {
1025 	int i;
1026 	bool result = true;
1027 
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]);
1038 
1039 	return result;
1040 }
1041 
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)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)
1043 {
1044 	int stream_index, plane_index, i;
1045 
1046 	unsigned int stream_disp_cfg_index;
1047 	unsigned int plane_disp_cfg_index;
1048 	unsigned int disp_cfg_index_max;
1049 
1050 	unsigned int plane_id;
1051 	unsigned int stream_id;
1052 
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;
1056 
1057 	if (ctx->config.map_dc_pipes_with_callbacks)
1058 		return map_dc_pipes_with_callbacks(
1059 				ctx, state, disp_cfg, mapping, existing_state);
1060 
1061 	if (ctx->architecture == dml2_architecture_21) {
1062 		/*
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.
1066 		 *
1067 		 */
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;
1071 		}
1072 
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__;
1076 	} else {
1077 		ODMMode = (unsigned int *)disp_cfg->hw.ODMMode;
1078 		DPPPerSurface = disp_cfg->hw.DPPPerSurface;
1079 		disp_cfg_index_max = __DML_NUM_PLANES__;
1080 	}
1081 
1082 	for (stream_index = 0; stream_index < state->stream_count; stream_index++) {
1083 		memset(&scratch, 0, sizeof(struct dc_pipe_mapping_scratch));
1084 
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)
1088 			continue;
1089 
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;
1096 		} else {
1097 			ASSERT(false);
1098 			scratch.odm_info.odm_factor = 1;
1099 		}
1100 
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.
1104 		 * */
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;
1112 			} else {
1113 				ASSERT(false);
1114 				scratch.odm_info.odm_factor = 1;
1115 			}
1116 		}
1117 		calculate_odm_slices(state->streams[stream_index], scratch.odm_info.odm_factor, scratch.odm_info.odm_slice_end_x);
1118 
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);
1122 		}
1123 
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);
1129 
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];
1136 
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;
1140 				} else {
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;
1144 				}
1145 
1146 				ASSERT(scratch.odm_info.odm_factor * scratch.mpc_info.mpc_factor > 0);
1147 
1148 				// Clear the pool assignment scratch (which is per plane)
1149 				memset(&scratch.pipe_pool, 0, sizeof(struct dc_plane_pipe_pool));
1150 
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);
1153 			} else {
1154 				// Plane ID cannot be generated, therefore no DML mapping can be performed.
1155 				ASSERT(false);
1156 			}
1157 		}
1158 
1159 	}
1160 
1161 	if (!validate_pipe_assignment(ctx, state, disp_cfg, mapping))
1162 		ASSERT(false);
1163 
1164 	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
1165 		struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
1166 
1167 		if (pipe->plane_state) {
1168 			if (!ctx->config.callbacks.build_scaling_params(pipe)) {
1169 				ASSERT(false);
1170 			}
1171 		}
1172 
1173 		if (ctx->config.callbacks.build_test_pattern_params &&
1174 				pipe->stream &&
1175 				pipe->prev_odm_pipe == NULL &&
1176 				pipe->top_pipe == NULL)
1177 			ctx->config.callbacks.build_test_pattern_params(&state->res_ctx, pipe);
1178 	}
1179 
1180 	return true;
1181 }
1182