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