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