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