File Index
Files and directories
Sign in
FILE

dml2_dc_resource_mgmt.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c C
   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 || (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	ASSERT(false);
  92	return __DML2_WRAPPER_MAX_STREAMS_PLANES__;
  93}
  94
  95static 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
 109static 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
 123static 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
 141static 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
 179static 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
 227static 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
 235static 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
 243static 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	 * Adding check to avoid pipe select on the head pipe by utilizing dc resource
 263	 * helper function resource_get_primary_dpp_pipe and comparing the pipe index.
 264	 */
 265	if (existing_state) {
 266		for (i = 0; i < pipe_count; i++) {
 267			if (existing_state->res_ctx.pipe_ctx[i].stream && existing_state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id) {
 268				struct pipe_ctx *head_pipe =
 269					resource_is_pipe_type(&existing_state->res_ctx.pipe_ctx[i], DPP_PIPE) ?
 270						resource_get_primary_dpp_pipe(&existing_state->res_ctx.pipe_ctx[i]) :
 271							NULL;
 272
 273				// we should always respect the head pipe from selection
 274				if (head_pipe && head_pipe->pipe_idx == i)
 275					continue;
 276				if (existing_state->res_ctx.pipe_ctx[i].plane_res.hubp &&
 277					existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i &&
 278						(existing_state->res_ctx.pipe_ctx[i].prev_odm_pipe ||
 279						existing_state->res_ctx.pipe_ctx[i].next_odm_pipe))
 280					continue;
 281
 282				preferred_pipe_candidates[num_preferred_candidates++] = i;
 283			}
 284		}
 285	}
 286
 287	return num_preferred_candidates;
 288}
 289
 290static unsigned int find_last_resort_pipe_candidates(const struct dc_state *existing_state,
 291	const int pipe_count,
 292	const unsigned int stream_id,
 293	unsigned int *last_resort_pipe_candidates)
 294{
 295	unsigned int num_last_resort_candidates = 0;
 296	int i;
 297
 298	/* There are two cases where we would like to add a given pipe into the last
 299	 * candidate array:
 300	 *
 301	 * 1. If the pipe requires a change in OPP, for example during an MPC
 302	 * to ODM transiton.
 303	 *
 304	 * 2. If the pipe already has an enabled OTG.
 305	 */
 306	if (existing_state) {
 307		for (i  = 0; i < pipe_count; i++) {
 308			struct pipe_ctx *head_pipe =
 309				resource_is_pipe_type(&existing_state->res_ctx.pipe_ctx[i], DPP_PIPE) ?
 310					resource_get_primary_dpp_pipe(&existing_state->res_ctx.pipe_ctx[i]) :
 311						NULL;
 312
 313			// we should always respect the head pipe from selection
 314			if (head_pipe && head_pipe->pipe_idx == i)
 315				continue;
 316			if ((existing_state->res_ctx.pipe_ctx[i].plane_res.hubp &&
 317				existing_state->res_ctx.pipe_ctx[i].plane_res.hubp->opp_id != i) ||
 318				existing_state->res_ctx.pipe_ctx[i].stream_res.tg)
 319				last_resort_pipe_candidates[num_last_resort_candidates++] = i;
 320		}
 321	}
 322
 323	return num_last_resort_candidates;
 324}
 325
 326static bool is_pipe_in_candidate_array(const unsigned int pipe_idx,
 327	const unsigned int *candidate_array,
 328	const unsigned int candidate_array_size)
 329{
 330	int i;
 331
 332	for (i = 0; i < candidate_array_size; i++) {
 333		if (candidate_array[i] == pipe_idx)
 334			return true;
 335	}
 336
 337	return false;
 338}
 339
 340static bool find_more_pipes_for_stream(struct dml2_context *ctx,
 341	struct dc_state *state, // The state we want to find a free mapping in
 342	unsigned int stream_id, // The stream we want this pipe to drive
 343	int *assigned_pipes,
 344	int *assigned_pipe_count,
 345	int pipes_needed,
 346	const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to
 347{
 348	struct pipe_ctx *pipe = NULL;
 349	unsigned int preferred_pipe_candidates[MAX_PIPES] = {0};
 350	unsigned int last_resort_pipe_candidates[MAX_PIPES] = {0};
 351	unsigned int num_preferred_candidates = 0;
 352	unsigned int num_last_resort_candidates = 0;
 353	int i;
 354
 355	if (existing_state) {
 356		num_preferred_candidates =
 357			find_preferred_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, preferred_pipe_candidates);
 358
 359		num_last_resort_candidates =
 360			find_last_resort_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, last_resort_pipe_candidates);
 361	}
 362
 363	// First see if any of the preferred are unmapped, and choose those instead
 364	for (i = 0; pipes_needed > 0 && i < num_preferred_candidates; i++) {
 365		pipe = &state->res_ctx.pipe_ctx[preferred_pipe_candidates[i]];
 366		if (!is_plane_using_pipe(pipe)) {
 367			pipes_needed--;
 368			// TODO: This doens't make sense really, pipe_idx should always be valid
 369			pipe->pipe_idx = preferred_pipe_candidates[i];
 370			assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
 371		}
 372	}
 373
 374	// We like to pair pipes starting from the higher order indicies for combining
 375	for (i = ctx->config.dcn_pipe_count - 1; pipes_needed > 0 && i >= 0; i--) {
 376		// Ignore any pipes that are the preferred or last resort candidate
 377		if (is_pipe_in_candidate_array(i, preferred_pipe_candidates, num_preferred_candidates) ||
 378			is_pipe_in_candidate_array(i, last_resort_pipe_candidates, num_last_resort_candidates))
 379			continue;
 380
 381		pipe = &state->res_ctx.pipe_ctx[i];
 382		if (!is_plane_using_pipe(pipe)) {
 383			pipes_needed--;
 384			// TODO: This doens't make sense really, pipe_idx should always be valid
 385			pipe->pipe_idx = i;
 386			assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
 387		}
 388	}
 389
 390	// Only use the last resort pipe candidates as a last resort
 391	for (i = 0; pipes_needed > 0 && i < num_last_resort_candidates; i++) {
 392		pipe = &state->res_ctx.pipe_ctx[last_resort_pipe_candidates[i]];
 393		if (!is_plane_using_pipe(pipe)) {
 394			pipes_needed--;
 395			// TODO: This doens't make sense really, pipe_idx should always be valid
 396			pipe->pipe_idx = last_resort_pipe_candidates[i];
 397			assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
 398		}
 399	}
 400
 401	ASSERT(pipes_needed <= 0); // Validation should prevent us from building a pipe context that exceeds the number of HW resoruces available
 402
 403	return pipes_needed <= 0;
 404}
 405
 406static bool find_more_free_pipes(struct dml2_context *ctx,
 407	struct dc_state *state, // The state we want to find a free mapping in
 408	unsigned int stream_id, // The stream we want this pipe to drive
 409	int *assigned_pipes,
 410	int *assigned_pipe_count,
 411	int pipes_needed,
 412	const struct dc_state *existing_state) // The state (optional) that we want to minimize remapping relative to
 413{
 414	struct pipe_ctx *pipe = NULL;
 415	unsigned int preferred_pipe_candidates[MAX_PIPES] = {0};
 416	unsigned int last_resort_pipe_candidates[MAX_PIPES] = {0};
 417	unsigned int num_preferred_candidates = 0;
 418	unsigned int num_last_resort_candidates = 0;
 419	int i;
 420
 421	if (existing_state) {
 422		num_preferred_candidates =
 423			find_preferred_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, preferred_pipe_candidates);
 424
 425		num_last_resort_candidates =
 426			find_last_resort_pipe_candidates(existing_state, ctx->config.dcn_pipe_count, stream_id, last_resort_pipe_candidates);
 427	}
 428
 429	// First see if any of the preferred are unmapped, and choose those instead
 430	for (i = 0; pipes_needed > 0 && i < num_preferred_candidates; i++) {
 431		pipe = &state->res_ctx.pipe_ctx[preferred_pipe_candidates[i]];
 432		if (is_pipe_free(pipe)) {
 433			pipes_needed--;
 434			// TODO: This doens't make sense really, pipe_idx should always be valid
 435			pipe->pipe_idx = preferred_pipe_candidates[i];
 436			assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
 437		}
 438	}
 439
 440	// We like to pair pipes starting from the higher order indicies for combining
 441	for (i = ctx->config.dcn_pipe_count - 1; pipes_needed > 0 && i >= 0; i--) {
 442		// Ignore any pipes that are the preferred or last resort candidate
 443		if (is_pipe_in_candidate_array(i, preferred_pipe_candidates, num_preferred_candidates) ||
 444			is_pipe_in_candidate_array(i, last_resort_pipe_candidates, num_last_resort_candidates))
 445			continue;
 446
 447		pipe = &state->res_ctx.pipe_ctx[i];
 448		if (is_pipe_free(pipe)) {
 449			pipes_needed--;
 450			// TODO: This doens't make sense really, pipe_idx should always be valid
 451			pipe->pipe_idx = i;
 452			assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
 453		}
 454	}
 455
 456	// Only use the last resort pipe candidates as a last resort
 457	for (i = 0; pipes_needed > 0 && i < num_last_resort_candidates; i++) {
 458		pipe = &state->res_ctx.pipe_ctx[last_resort_pipe_candidates[i]];
 459		if (is_pipe_free(pipe)) {
 460			pipes_needed--;
 461			// TODO: This doens't make sense really, pipe_idx should always be valid
 462			pipe->pipe_idx = last_resort_pipe_candidates[i];
 463			assigned_pipes[(*assigned_pipe_count)++] = pipe->pipe_idx;
 464		}
 465	}
 466
 467	ASSERT(pipes_needed == 0); // Validation should prevent us from building a pipe context that exceeds the number of HW resoruces available
 468
 469	return pipes_needed == 0;
 470}
 471
 472static void sort_pipes_for_splitting(struct dc_plane_pipe_pool *pipes)
 473{
 474	bool sorted, swapped;
 475	unsigned int cur_index;
 476	int odm_slice_index;
 477
 478	for (odm_slice_index = 0; odm_slice_index < pipes->num_pipes_assigned_to_plane_for_odm_combine; odm_slice_index++) {
 479		// Sort each MPCC set
 480		//Un-optimized bubble sort, but that's okay for array sizes <= 6
 481
 482		if (pipes->num_pipes_assigned_to_plane_for_mpcc_combine <= 1)
 483			sorted = true;
 484		else
 485			sorted = false;
 486
 487		cur_index = 0;
 488		swapped = false;
 489		while (!sorted) {
 490			if (pipes->pipes_assigned_to_plane[odm_slice_index][cur_index] > pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1]) {
 491				swap(pipes->pipes_assigned_to_plane[odm_slice_index][cur_index + 1],
 492					 pipes->pipes_assigned_to_plane[odm_slice_index][cur_index]);
 493
 494				swapped = true;
 495			}
 496
 497			cur_index++;
 498
 499			if (cur_index == pipes->num_pipes_assigned_to_plane_for_mpcc_combine - 1) {
 500				cur_index = 0;
 501
 502				if (swapped)
 503					sorted = false;
 504				else
 505					sorted = true;
 506
 507				swapped = false;
 508			}
 509
 510		}
 511	}
 512}
 513
 514// 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
 515static void calculate_odm_slices(const struct dc_stream_state *stream, unsigned int odm_factor, unsigned int *odm_slice_end_x)
 516{
 517	unsigned int slice_size = 0;
 518	int i;
 519
 520	if (odm_factor < 1 || odm_factor > 4) {
 521		ASSERT(false);
 522		return;
 523	}
 524
 525	slice_size = stream->src.width / odm_factor;
 526
 527	for (i = 0; i < odm_factor; i++)
 528		odm_slice_end_x[i] = (slice_size * (i + 1)) - 1;
 529
 530	odm_slice_end_x[odm_factor - 1] = stream->src.width - 1;
 531}
 532
 533static void add_odm_slice_to_odm_tree(struct dml2_context *ctx,
 534		struct dc_state *state,
 535		struct dc_pipe_mapping_scratch *scratch,
 536		unsigned int odm_slice_index)
 537{
 538	struct pipe_ctx *pipe = NULL;
 539	int i;
 540
 541	// MPCC Combine + ODM Combine is not supported, so there should never be a case where the current plane
 542	// has more than 1 pipe mapped to it for a given slice.
 543	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);
 544
 545	for (i = 0; i < scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
 546		pipe = &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][i]];
 547
 548		if (scratch->mpc_info.prev_odm_pipe)
 549			scratch->mpc_info.prev_odm_pipe->next_odm_pipe = pipe;
 550
 551		pipe->prev_odm_pipe = scratch->mpc_info.prev_odm_pipe;
 552		pipe->next_odm_pipe = NULL;
 553	}
 554	scratch->mpc_info.prev_odm_pipe = pipe;
 555}
 556
 557static struct pipe_ctx *add_plane_to_blend_tree(struct dml2_context *ctx,
 558	struct dc_state *state,
 559	const struct dc_plane_state *plane,
 560	struct dc_plane_pipe_pool *pipe_pool,
 561	unsigned int odm_slice,
 562	struct pipe_ctx *top_pipe)
 563{
 564	int i;
 565
 566	for (i = 0; i < pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
 567		if (top_pipe)
 568			top_pipe->bottom_pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]];
 569
 570		pipe_pool->pipe_used[odm_slice][i] = true;
 571
 572		state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]].top_pipe = top_pipe;
 573		state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]].bottom_pipe = NULL;
 574
 575		top_pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][i]];
 576	}
 577
 578	// After running the above loop, the top pipe actually ends up pointing to the bottom of this MPCC combine tree, so we are actually
 579	// returning the bottom pipe here
 580	return top_pipe;
 581}
 582
 583static unsigned int find_pipes_assigned_to_stream(struct dml2_context *ctx, struct dc_state *state, unsigned int stream_id, unsigned int *pipes)
 584{
 585	int i;
 586	unsigned int num_found = 0;
 587
 588	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
 589		struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
 590
 591		if (pipe->stream && pipe->stream->stream_id == stream_id && !pipe->top_pipe && !pipe->prev_odm_pipe) {
 592			while (pipe) {
 593				pipes[num_found++] = pipe->pipe_idx;
 594				pipe = pipe->next_odm_pipe;
 595			}
 596			break;
 597		}
 598	}
 599
 600	return num_found;
 601}
 602
 603static struct pipe_ctx *assign_pipes_to_stream(struct dml2_context *ctx, struct dc_state *state,
 604		const struct dc_stream_state *stream,
 605		int odm_factor,
 606		struct dc_plane_pipe_pool *pipe_pool,
 607		const struct dc_state *existing_state)
 608{
 609	struct pipe_ctx *master_pipe;
 610	unsigned int pipes_needed;
 611	unsigned int pipes_assigned;
 612	unsigned int pipes[MAX_PIPES] = {0};
 613	unsigned int next_pipe_to_assign;
 614	int odm_slice;
 615
 616	pipes_needed = odm_factor;
 617
 618	master_pipe = find_master_pipe_of_stream(ctx, state, stream->stream_id);
 619	ASSERT(master_pipe);
 620
 621	pipes_assigned = find_pipes_assigned_to_stream(ctx, state, stream->stream_id, pipes);
 622
 623	find_more_free_pipes(ctx, state, stream->stream_id, pipes, &pipes_assigned, pipes_needed - pipes_assigned, existing_state);
 624
 625	ASSERT(pipes_assigned == pipes_needed);
 626
 627	next_pipe_to_assign = 0;
 628	for (odm_slice = 0; odm_slice < odm_factor; odm_slice++)
 629		pipe_pool->pipes_assigned_to_plane[odm_slice][0] = pipes[next_pipe_to_assign++];
 630
 631	pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine = 1;
 632	pipe_pool->num_pipes_assigned_to_plane_for_odm_combine = odm_factor;
 633
 634	return master_pipe;
 635}
 636
 637static struct pipe_ctx *assign_pipes_to_plane(struct dml2_context *ctx, struct dc_state *state,
 638		const struct dc_stream_state *stream,
 639		const struct dc_plane_state *plane,
 640		int odm_factor,
 641		int mpc_factor,
 642		int plane_index,
 643		struct dc_plane_pipe_pool *pipe_pool,
 644		const struct dc_state *existing_state)
 645{
 646	struct pipe_ctx *master_pipe = NULL;
 647	unsigned int plane_id;
 648	unsigned int pipes_needed;
 649	unsigned int pipes_assigned;
 650	unsigned int pipes[MAX_PIPES] = {0};
 651	unsigned int next_pipe_to_assign;
 652	int odm_slice, mpc_slice;
 653
 654	if (!get_plane_id(ctx, state, plane, stream->stream_id, plane_index, &plane_id)) {
 655		ASSERT(false);
 656		return master_pipe;
 657	}
 658
 659	pipes_needed = mpc_factor * odm_factor;
 660
 661	master_pipe = find_master_pipe_of_plane(ctx, state, plane_id);
 662	ASSERT(master_pipe);
 663
 664	pipes_assigned = find_pipes_assigned_to_plane(ctx, state, plane_id, pipes);
 665
 666	find_more_pipes_for_stream(ctx, state, stream->stream_id, pipes, &pipes_assigned, pipes_needed - pipes_assigned, existing_state);
 667
 668	ASSERT(pipes_assigned >= pipes_needed);
 669
 670	next_pipe_to_assign = 0;
 671	for (odm_slice = 0; odm_slice < odm_factor; odm_slice++)
 672		for (mpc_slice = 0; mpc_slice < mpc_factor; mpc_slice++)
 673			pipe_pool->pipes_assigned_to_plane[odm_slice][mpc_slice] = pipes[next_pipe_to_assign++];
 674
 675	pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine = mpc_factor;
 676	pipe_pool->num_pipes_assigned_to_plane_for_odm_combine = odm_factor;
 677
 678	return master_pipe;
 679}
 680
 681static bool is_pipe_used(const struct dc_plane_pipe_pool *pool, unsigned int pipe_idx)
 682{
 683	int i, j;
 684
 685	for (i = 0; i < pool->num_pipes_assigned_to_plane_for_odm_combine; i++) {
 686		for (j = 0; j < pool->num_pipes_assigned_to_plane_for_mpcc_combine; j++) {
 687			if (pool->pipes_assigned_to_plane[i][j] == pipe_idx && pool->pipe_used[i][j])
 688				return true;
 689		}
 690	}
 691
 692	return false;
 693}
 694
 695static void free_pipe(struct pipe_ctx *pipe)
 696{
 697	memset(pipe, 0, sizeof(struct pipe_ctx));
 698}
 699
 700static void free_unused_pipes_for_plane(struct dml2_context *ctx, struct dc_state *state,
 701	const struct dc_plane_state *plane, const struct dc_plane_pipe_pool *pool, unsigned int stream_id, int plane_index)
 702{
 703	int i;
 704	bool is_plane_duplicate = ctx->v20.scratch.plane_duplicate_exists;
 705
 706	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
 707		if (state->res_ctx.pipe_ctx[i].plane_state == plane &&
 708			state->res_ctx.pipe_ctx[i].stream->stream_id == stream_id &&
 709			(!is_plane_duplicate ||
 710			ctx->v20.scratch.dml_to_dc_pipe_mapping.dml_pipe_idx_to_plane_index[state->res_ctx.pipe_ctx[i].pipe_idx] == plane_index) &&
 711			!is_pipe_used(pool, state->res_ctx.pipe_ctx[i].pipe_idx)) {
 712			free_pipe(&state->res_ctx.pipe_ctx[i]);
 713		}
 714	}
 715}
 716
 717static 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)
 718{
 719	struct pipe_ctx *pipe;
 720	int i;
 721
 722	for (i = 0; i < pipe_pool->num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
 723		pipe = &state->res_ctx.pipe_ctx[pipe_pool->pipes_assigned_to_plane[odm_slice][0]];
 724		if (pipe->top_pipe)
 725			pipe->top_pipe->bottom_pipe = pipe->bottom_pipe;
 726
 727		if (pipe->bottom_pipe)
 728			pipe->bottom_pipe = pipe->top_pipe;
 729
 730		pipe_pool->pipe_used[odm_slice][i] = true;
 731	}
 732}
 733
 734static void map_pipes_for_stream(struct dml2_context *ctx, struct dc_state *state, const struct dc_stream_state *stream,
 735		struct dc_pipe_mapping_scratch *scratch, const struct dc_state *existing_state)
 736{
 737	int odm_slice_index;
 738	struct pipe_ctx *master_pipe = NULL;
 739
 740
 741	master_pipe = assign_pipes_to_stream(ctx, state, stream, scratch->odm_info.odm_factor, &scratch->pipe_pool, existing_state);
 742	sort_pipes_for_splitting(&scratch->pipe_pool);
 743
 744	for (odm_slice_index = 0; odm_slice_index < scratch->odm_info.odm_factor; odm_slice_index++) {
 745		remove_pipes_from_blend_trees(ctx, state, &scratch->pipe_pool, odm_slice_index);
 746
 747		add_odm_slice_to_odm_tree(ctx, state, scratch, odm_slice_index);
 748
 749		ctx->config.callbacks.acquire_secondary_pipe_for_mpc_odm(ctx->config.callbacks.dc, state,
 750			master_pipe, &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][0]], true);
 751	}
 752}
 753
 754static 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,
 755		int plane_index, struct dc_pipe_mapping_scratch *scratch, const struct dc_state *existing_state)
 756{
 757	int odm_slice_index;
 758	unsigned int plane_id;
 759	struct pipe_ctx *master_pipe = NULL;
 760	int i;
 761
 762	if (!get_plane_id(ctx, state, plane, stream->stream_id, plane_index, &plane_id)) {
 763		ASSERT(false);
 764		return;
 765	}
 766
 767	master_pipe = assign_pipes_to_plane(ctx, state, stream, plane, scratch->odm_info.odm_factor,
 768			scratch->mpc_info.mpc_factor, plane_index, &scratch->pipe_pool, existing_state);
 769	sort_pipes_for_splitting(&scratch->pipe_pool);
 770
 771	for (odm_slice_index = 0; odm_slice_index < scratch->odm_info.odm_factor; odm_slice_index++) {
 772		// Now we have a list of all pipes to be used for this plane/stream, now setup the tree.
 773		scratch->odm_info.next_higher_pipe_for_odm_slice[odm_slice_index] = add_plane_to_blend_tree(ctx, state,
 774				plane,
 775				&scratch->pipe_pool,
 776				odm_slice_index,
 777				scratch->odm_info.next_higher_pipe_for_odm_slice[odm_slice_index]);
 778
 779		add_odm_slice_to_odm_tree(ctx, state, scratch, odm_slice_index);
 780
 781		for (i = 0; i < scratch->pipe_pool.num_pipes_assigned_to_plane_for_mpcc_combine; i++) {
 782
 783			ctx->config.callbacks.acquire_secondary_pipe_for_mpc_odm(ctx->config.callbacks.dc, state,
 784				master_pipe, &state->res_ctx.pipe_ctx[scratch->pipe_pool.pipes_assigned_to_plane[odm_slice_index][i]], true);
 785		}
 786	}
 787
 788	free_unused_pipes_for_plane(ctx, state, plane, &scratch->pipe_pool, stream->stream_id, plane_index);
 789}
 790
 791static unsigned int get_target_mpc_factor(struct dml2_context *ctx,
 792		struct dc_state *state,
 793		const struct dml_display_cfg_st *disp_cfg,
 794		struct dml2_dml_to_dc_pipe_mapping *mapping,
 795		const struct dc_stream_status *status,
 796		const struct dc_stream_state *stream,
 797		int plane_idx)
 798{
 799	unsigned int plane_id;
 800	unsigned int cfg_idx;
 801	unsigned int mpc_factor;
 802
 803	if (ctx->architecture == dml2_architecture_20) {
 804		get_plane_id(ctx, state, status->plane_states[plane_idx],
 805				stream->stream_id, plane_idx, &plane_id);
 806		cfg_idx = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
 807		mpc_factor = (unsigned int)disp_cfg->hw.DPPPerSurface[cfg_idx];
 808	} else if (ctx->architecture == dml2_architecture_21) {
 809		if (ctx->config.svp_pstate.callbacks.get_stream_subvp_type(state, stream) == SUBVP_PHANTOM) {
 810			struct dc_stream_state *main_stream;
 811			struct dc_stream_status *main_stream_status;
 812
 813			/* get stream id of main stream */
 814			main_stream = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(state, stream);
 815			if (!main_stream) {
 816				ASSERT(false);
 817				return 1;
 818			}
 819
 820			main_stream_status = ctx->config.callbacks.get_stream_status(state, main_stream);
 821			if (!main_stream_status) {
 822				ASSERT(false);
 823				return 1;
 824			}
 825
 826			/* get plane id for associated main plane */
 827			get_plane_id(ctx, state, main_stream_status->plane_states[plane_idx],
 828					main_stream->stream_id, plane_idx, &plane_id);
 829		} else {
 830			get_plane_id(ctx, state, status->plane_states[plane_idx],
 831					stream->stream_id, plane_idx, &plane_id);
 832		}
 833
 834		cfg_idx = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
 835		mpc_factor = ctx->v21.mode_programming.programming->plane_programming[cfg_idx].num_dpps_required;
 836	} else {
 837		mpc_factor = 1;
 838		ASSERT(false);
 839	}
 840
 841	/* For stereo timings, we need to pipe split */
 842	if (dml2_is_stereo_timing(stream))
 843		mpc_factor = 2;
 844
 845	return mpc_factor;
 846}
 847
 848static unsigned int get_target_odm_factor(
 849		const struct dml2_context *ctx,
 850		struct dc_state *state,
 851		const struct dml_display_cfg_st *disp_cfg,
 852		struct dml2_dml_to_dc_pipe_mapping *mapping,
 853		const struct dc_stream_state *stream)
 854{
 855	unsigned int cfg_idx;
 856
 857	if (ctx->architecture == dml2_architecture_20) {
 858		cfg_idx = find_disp_cfg_idx_by_stream_id(
 859				mapping, stream->stream_id);
 860		switch (disp_cfg->hw.ODMMode[cfg_idx]) {
 861		case dml_odm_mode_bypass:
 862			return 1;
 863		case dml_odm_mode_combine_2to1:
 864			return 2;
 865		case dml_odm_mode_combine_4to1:
 866			return 4;
 867		default:
 868			break;
 869		}
 870	} else if (ctx->architecture == dml2_architecture_21) {
 871		if (ctx->config.svp_pstate.callbacks.get_stream_subvp_type(state, stream) == SUBVP_PHANTOM) {
 872			struct dc_stream_state *main_stream;
 873
 874			/* get stream id of main stream */
 875			main_stream = ctx->config.svp_pstate.callbacks.get_paired_subvp_stream(state, stream);
 876			if (!main_stream)
 877				goto failed;
 878
 879			/* get cfg idx for associated main stream */
 880			cfg_idx = find_disp_cfg_idx_by_stream_id(
 881					mapping, main_stream->stream_id);
 882		} else {
 883			cfg_idx = find_disp_cfg_idx_by_stream_id(
 884					mapping, stream->stream_id);
 885		}
 886
 887		return ctx->v21.mode_programming.programming->stream_programming[cfg_idx].num_odms_required;
 888	}
 889
 890failed:
 891	ASSERT(false);
 892	return 1;
 893}
 894
 895static unsigned int get_source_odm_factor(const struct dml2_context *ctx,
 896		struct dc_state *state,
 897		const struct dc_stream_state *stream)
 898{
 899	struct pipe_ctx *otg_master = ctx->config.callbacks.get_otg_master_for_stream(&state->res_ctx, stream);
 900
 901	if (!otg_master)
 902		return 0;
 903
 904	return ctx->config.callbacks.get_odm_slice_count(otg_master);
 905}
 906
 907static unsigned int get_source_mpc_factor(const struct dml2_context *ctx,
 908		struct dc_state *state,
 909		const struct dc_plane_state *plane)
 910{
 911	struct pipe_ctx *dpp_pipes[MAX_PIPES] = {0};
 912	int dpp_pipe_count = ctx->config.callbacks.get_dpp_pipes_for_plane(plane,
 913			&state->res_ctx, dpp_pipes);
 914
 915	ASSERT(dpp_pipe_count > 0);
 916	return ctx->config.callbacks.get_mpc_slice_count(dpp_pipes[0]);
 917}
 918
 919
 920static void populate_mpc_factors_for_stream(
 921		struct dml2_context *ctx,
 922		const struct dml_display_cfg_st *disp_cfg,
 923		struct dml2_dml_to_dc_pipe_mapping *mapping,
 924		struct dc_state *state,
 925		unsigned int stream_idx,
 926		struct dml2_pipe_combine_factor odm_factor,
 927		struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])
 928{
 929	const struct dc_stream_status *status = &state->stream_status[stream_idx];
 930	int i;
 931
 932	for (i = 0; i < status->plane_count; i++) {
 933		mpc_factors[i].source = get_source_mpc_factor(ctx, state, status->plane_states[i]);
 934		mpc_factors[i].target = (odm_factor.target == 1) ?
 935				get_target_mpc_factor(ctx, state, disp_cfg, mapping, status, state->streams[stream_idx], i) : 1;
 936	}
 937}
 938
 939static void populate_odm_factors(const struct dml2_context *ctx,
 940		const struct dml_display_cfg_st *disp_cfg,
 941		struct dml2_dml_to_dc_pipe_mapping *mapping,
 942		struct dc_state *state,
 943		struct dml2_pipe_combine_factor odm_factors[MAX_PIPES])
 944{
 945	int i;
 946
 947	for (i = 0; i < state->stream_count; i++) {
 948		odm_factors[i].source = get_source_odm_factor(ctx, state, state->streams[i]);
 949		odm_factors[i].target = get_target_odm_factor(
 950				ctx, state, disp_cfg, mapping, state->streams[i]);
 951	}
 952}
 953
 954static bool unmap_dc_pipes_for_stream(struct dml2_context *ctx,
 955		struct dc_state *state,
 956		const struct dc_state *existing_state,
 957		const struct dc_stream_state *stream,
 958		const struct dc_stream_status *status,
 959		struct dml2_pipe_combine_factor odm_factor,
 960		struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])
 961{
 962	int plane_idx;
 963	bool result = true;
 964
 965	for (plane_idx = 0; plane_idx < status->plane_count; plane_idx++)
 966		if (mpc_factors[plane_idx].target < mpc_factors[plane_idx].source)
 967			result &= ctx->config.callbacks.update_pipes_for_plane_with_slice_count(
 968					state,
 969					existing_state,
 970					ctx->config.callbacks.dc->res_pool,
 971					status->plane_states[plane_idx],
 972					mpc_factors[plane_idx].target);
 973	if (odm_factor.target < odm_factor.source)
 974		result &= ctx->config.callbacks.update_pipes_for_stream_with_slice_count(
 975				state,
 976				existing_state,
 977				ctx->config.callbacks.dc->res_pool,
 978				stream,
 979				odm_factor.target);
 980	return result;
 981}
 982
 983static bool map_dc_pipes_for_stream(struct dml2_context *ctx,
 984		struct dc_state *state,
 985		const struct dc_state *existing_state,
 986		const struct dc_stream_state *stream,
 987		const struct dc_stream_status *status,
 988		struct dml2_pipe_combine_factor odm_factor,
 989		struct dml2_pipe_combine_factor mpc_factors[MAX_PIPES])
 990{
 991	int plane_idx;
 992	bool result = true;
 993
 994	for (plane_idx = 0; plane_idx < status->plane_count; plane_idx++)
 995		if (mpc_factors[plane_idx].target > mpc_factors[plane_idx].source)
 996			result &= ctx->config.callbacks.update_pipes_for_plane_with_slice_count(
 997					state,
 998					existing_state,
 999					ctx->config.callbacks.dc->res_pool,
1000					status->plane_states[plane_idx],
1001					mpc_factors[plane_idx].target);
1002	if (odm_factor.target > odm_factor.source)
1003		result &= ctx->config.callbacks.update_pipes_for_stream_with_slice_count(
1004				state,
1005				existing_state,
1006				ctx->config.callbacks.dc->res_pool,
1007				stream,
1008				odm_factor.target);
1009	return result;
1010}
1011
1012static bool map_dc_pipes_with_callbacks(struct dml2_context *ctx,
1013		struct dc_state *state,
1014		const struct dml_display_cfg_st *disp_cfg,
1015		struct dml2_dml_to_dc_pipe_mapping *mapping,
1016		const struct dc_state *existing_state)
1017{
1018	int i;
1019	bool result = true;
1020
1021	populate_odm_factors(ctx, disp_cfg, mapping, state, ctx->pipe_combine_scratch.odm_factors);
1022	for (i = 0; i < state->stream_count; i++)
1023		populate_mpc_factors_for_stream(ctx, disp_cfg, mapping, state,
1024				i, ctx->pipe_combine_scratch.odm_factors[i], ctx->pipe_combine_scratch.mpc_factors[i]);
1025	for (i = 0; i < state->stream_count; i++)
1026		result &= unmap_dc_pipes_for_stream(ctx, state, existing_state, state->streams[i],
1027				&state->stream_status[i], ctx->pipe_combine_scratch.odm_factors[i], ctx->pipe_combine_scratch.mpc_factors[i]);
1028	for (i = 0; i < state->stream_count; i++)
1029		result &= map_dc_pipes_for_stream(ctx, state, existing_state, state->streams[i],
1030				&state->stream_status[i], ctx->pipe_combine_scratch.odm_factors[i], ctx->pipe_combine_scratch.mpc_factors[i]);
1031
1032	return result;
1033}
1034
1035bool 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)
1036{
1037	int stream_index, plane_index, i;
1038
1039	unsigned int stream_disp_cfg_index;
1040	unsigned int plane_disp_cfg_index;
1041	unsigned int disp_cfg_index_max;
1042
1043	unsigned int plane_id;
1044	unsigned int stream_id;
1045
1046	const unsigned int *ODMMode, *DPPPerSurface;
1047	unsigned int odm_mode_array[__DML2_WRAPPER_MAX_STREAMS_PLANES__] = {0}, dpp_per_surface_array[__DML2_WRAPPER_MAX_STREAMS_PLANES__] = {0};
1048	struct dc_pipe_mapping_scratch scratch;
1049
1050	if (ctx->config.map_dc_pipes_with_callbacks)
1051		return map_dc_pipes_with_callbacks(
1052				ctx, state, disp_cfg, mapping, existing_state);
1053
1054	if (ctx->architecture == dml2_architecture_21) {
1055		/*
1056		 * Extract ODM and DPP outputs from DML2.1 and map them in an array as required for pipe mapping in dml2_map_dc_pipes.
1057		 * As data cannot be directly extracted in const pointers, assign these arrays to const pointers before proceeding to
1058		 * maximize the reuse of existing code. Const pointers are required because dml2.0 dml_display_cfg_st is const.
1059		 *
1060		 */
1061		for (i = 0; i < __DML2_WRAPPER_MAX_STREAMS_PLANES__; i++) {
1062			odm_mode_array[i] = ctx->v21.mode_programming.programming->stream_programming[i].num_odms_required;
1063			dpp_per_surface_array[i] = ctx->v21.mode_programming.programming->plane_programming[i].num_dpps_required;
1064		}
1065
1066		ODMMode = (const unsigned int *)odm_mode_array;
1067		DPPPerSurface = (const unsigned int *)dpp_per_surface_array;
1068		disp_cfg_index_max = __DML2_WRAPPER_MAX_STREAMS_PLANES__;
1069	} else {
1070		ODMMode = (unsigned int *)disp_cfg->hw.ODMMode;
1071		DPPPerSurface = disp_cfg->hw.DPPPerSurface;
1072		disp_cfg_index_max = __DML_NUM_PLANES__;
1073	}
1074
1075	for (stream_index = 0; stream_index < state->stream_count; stream_index++) {
1076		memset(&scratch, 0, sizeof(struct dc_pipe_mapping_scratch));
1077
1078		stream_id = state->streams[stream_index]->stream_id;
1079		stream_disp_cfg_index = find_disp_cfg_idx_by_stream_id(mapping, stream_id);
1080		if (stream_disp_cfg_index >= disp_cfg_index_max)
1081			continue;
1082
1083		if (ctx->architecture == dml2_architecture_20) {
1084			if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_bypass) {
1085				scratch.odm_info.odm_factor = 1;
1086			} else if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_combine_2to1) {
1087				scratch.odm_info.odm_factor = 2;
1088			} else if (ODMMode[stream_disp_cfg_index] == dml_odm_mode_combine_4to1) {
1089				scratch.odm_info.odm_factor = 4;
1090			} else {
1091				ASSERT(false);
1092				scratch.odm_info.odm_factor = 1;
1093			}
1094		} else if (ctx->architecture == dml2_architecture_21) {
1095		/* After DML2.1 update, ODM interpretation needs to change and is no longer same as for DML2.0.
1096		 * This is not an issue with new resource management logic. This block ensure backcompat
1097		 * with legacy pipe management with updated DML.
1098		 * */
1099			if (ODMMode[stream_disp_cfg_index] == 1) {
1100				scratch.odm_info.odm_factor = 1;
1101			} else if (ODMMode[stream_disp_cfg_index] == 2) {
1102				scratch.odm_info.odm_factor = 2;
1103			} else if (ODMMode[stream_disp_cfg_index] == 4) {
1104				scratch.odm_info.odm_factor = 4;
1105			} else {
1106				ASSERT(false);
1107				scratch.odm_info.odm_factor = 1;
1108			}
1109		}
1110		calculate_odm_slices(state->streams[stream_index], scratch.odm_info.odm_factor, scratch.odm_info.odm_slice_end_x);
1111
1112		// If there are no planes, you still want to setup ODM...
1113		if (state->stream_status[stream_index].plane_count == 0) {
1114			map_pipes_for_stream(ctx, state, state->streams[stream_index], &scratch, existing_state);
1115		}
1116
1117		for (plane_index = 0; plane_index < state->stream_status[stream_index].plane_count; plane_index++) {
1118			// Planes are ordered top to bottom.
1119			if (get_plane_id(ctx, state, state->stream_status[stream_index].plane_states[plane_index],
1120				stream_id, plane_index, &plane_id)) {
1121				plane_disp_cfg_index = find_disp_cfg_idx_by_plane_id(mapping, plane_id);
1122
1123				// Setup mpc_info for this plane
1124				scratch.mpc_info.prev_odm_pipe = NULL;
1125				if (scratch.odm_info.odm_factor == 1 && plane_disp_cfg_index < disp_cfg_index_max) {
1126					// If ODM combine is not inuse, then the number of pipes
1127					// per plane is determined by MPC combine factor
1128					scratch.mpc_info.mpc_factor = DPPPerSurface[plane_disp_cfg_index];
1129
1130					//For stereo timings, we need to pipe split
1131					if (dml2_is_stereo_timing(state->streams[stream_index]))
1132						scratch.mpc_info.mpc_factor = 2;
1133				} else {
1134					// If ODM combine is enabled, then we use at most 1 pipe per
1135					// odm slice per plane, i.e. MPC combine is never used
1136					scratch.mpc_info.mpc_factor = 1;
1137				}
1138
1139				ASSERT(scratch.odm_info.odm_factor * scratch.mpc_info.mpc_factor > 0);
1140
1141				// Clear the pool assignment scratch (which is per plane)
1142				memset(&scratch.pipe_pool, 0, sizeof(struct dc_plane_pipe_pool));
1143
1144				map_pipes_for_plane(ctx, state, state->streams[stream_index],
1145					state->stream_status[stream_index].plane_states[plane_index], plane_index, &scratch, existing_state);
1146			} else {
1147				// Plane ID cannot be generated, therefore no DML mapping can be performed.
1148				ASSERT(false);
1149			}
1150		}
1151
1152	}
1153
1154	if (!validate_pipe_assignment(ctx, state, disp_cfg, mapping))
1155		ASSERT(false);
1156
1157	for (i = 0; i < ctx->config.dcn_pipe_count; i++) {
1158		struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
1159
1160		if (pipe->plane_state) {
1161			if (!ctx->config.callbacks.build_scaling_params(pipe)) {
1162				ASSERT(false);
1163			}
1164		}
1165
1166		if (ctx->config.callbacks.build_test_pattern_params &&
1167				pipe->stream &&
1168				pipe->prev_odm_pipe == NULL &&
1169				pipe->top_pipe == NULL)
1170			ctx->config.callbacks.build_test_pattern_params(&state->res_ctx, pipe);
1171	}
1172
1173	return true;
1174}
~/ Read-only file preview.