1 /* 2 * Copyright 2022 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: AMD 23 * 24 */ 25 26 // header file of functions being implemented 27 #include "dcn32_resource.h" 28 #include "dcn20/dcn20_resource.h" 29 #include "dml/dcn32/display_mode_vba_util_32.h" 30 #include "dml/dcn32/dcn32_fpu.h" 31 32 static bool is_dual_plane(enum surface_pixel_format format) 33 { 34 return format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN || format == SURFACE_PIXEL_FORMAT_GRPH_RGBE_ALPHA; 35 } 36 37 38 uint32_t dcn32_helper_mall_bytes_to_ways( 39 struct dc *dc, 40 uint32_t total_size_in_mall_bytes) 41 { 42 uint32_t cache_lines_used, lines_per_way, total_cache_lines, num_ways; 43 44 /* add 2 lines for worst case alignment */ 45 cache_lines_used = total_size_in_mall_bytes / dc->caps.cache_line_size + 2; 46 47 total_cache_lines = dc->caps.max_cab_allocation_bytes / dc->caps.cache_line_size; 48 lines_per_way = total_cache_lines / dc->caps.cache_num_ways; 49 num_ways = cache_lines_used / lines_per_way; 50 if (cache_lines_used % lines_per_way > 0) 51 num_ways++; 52 53 return num_ways; 54 } 55 56 uint32_t dcn32_helper_calculate_mall_bytes_for_cursor( 57 struct dc *dc, 58 struct pipe_ctx *pipe_ctx, 59 bool ignore_cursor_buf) 60 { 61 struct hubp *hubp = pipe_ctx->plane_res.hubp; 62 uint32_t cursor_size = hubp->curs_attr.pitch * hubp->curs_attr.height; 63 uint32_t cursor_mall_size_bytes = 0; 64 65 switch (pipe_ctx->stream->cursor_attributes.color_format) { 66 case CURSOR_MODE_MONO: 67 cursor_size /= 2; 68 break; 69 case CURSOR_MODE_COLOR_1BIT_AND: 70 case CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA: 71 case CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA: 72 cursor_size *= 4; 73 break; 74 75 case CURSOR_MODE_COLOR_64BIT_FP_PRE_MULTIPLIED: 76 case CURSOR_MODE_COLOR_64BIT_FP_UN_PRE_MULTIPLIED: 77 cursor_size *= 8; 78 break; 79 } 80 81 /* only count if cursor is enabled, and if additional allocation needed outside of the 82 * DCN cursor buffer 83 */ 84 if (pipe_ctx->stream->cursor_position.enable && (ignore_cursor_buf || 85 cursor_size > 16384)) { 86 /* cursor_num_mblk = CEILING(num_cursors*cursor_width*cursor_width*cursor_Bpe/mblk_bytes, 1) 87 * Note: add 1 mblk in case of cursor misalignment 88 */ 89 cursor_mall_size_bytes = ((cursor_size + DCN3_2_MALL_MBLK_SIZE_BYTES - 1) / 90 DCN3_2_MALL_MBLK_SIZE_BYTES + 1) * DCN3_2_MALL_MBLK_SIZE_BYTES; 91 } 92 93 return cursor_mall_size_bytes; 94 } 95 96 /** 97 * ******************************************************************************************** 98 * dcn32_helper_calculate_num_ways_for_subvp: Calculate number of ways needed for SubVP 99 * 100 * Gets total allocation required for the phantom viewport calculated by DML in bytes and 101 * converts to number of cache ways. 102 * 103 * @param [in] dc: current dc state 104 * @param [in] context: new dc state 105 * 106 * @return: number of ways required for SubVP 107 * 108 * ******************************************************************************************** 109 */ 110 uint32_t dcn32_helper_calculate_num_ways_for_subvp( 111 struct dc *dc, 112 struct dc_state *context) 113 { 114 if (context->bw_ctx.bw.dcn.mall_subvp_size_bytes > 0) { 115 if (dc->debug.force_subvp_num_ways) { 116 return dc->debug.force_subvp_num_ways; 117 } else { 118 return dcn32_helper_mall_bytes_to_ways(dc, context->bw_ctx.bw.dcn.mall_subvp_size_bytes); 119 } 120 } else { 121 return 0; 122 } 123 } 124 125 void dcn32_merge_pipes_for_subvp(struct dc *dc, 126 struct dc_state *context) 127 { 128 uint32_t i; 129 130 /* merge pipes if necessary */ 131 for (i = 0; i < dc->res_pool->pipe_count; i++) { 132 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; 133 134 // For now merge all pipes for SubVP since pipe split case isn't supported yet 135 136 /* if ODM merge we ignore mpc tree, mpo pipes will have their own flags */ 137 if (pipe->prev_odm_pipe) { 138 /*split off odm pipe*/ 139 pipe->prev_odm_pipe->next_odm_pipe = pipe->next_odm_pipe; 140 if (pipe->next_odm_pipe) 141 pipe->next_odm_pipe->prev_odm_pipe = pipe->prev_odm_pipe; 142 143 pipe->bottom_pipe = NULL; 144 pipe->next_odm_pipe = NULL; 145 pipe->plane_state = NULL; 146 pipe->stream = NULL; 147 pipe->top_pipe = NULL; 148 pipe->prev_odm_pipe = NULL; 149 if (pipe->stream_res.dsc) 150 dcn20_release_dsc(&context->res_ctx, dc->res_pool, &pipe->stream_res.dsc); 151 memset(&pipe->plane_res, 0, sizeof(pipe->plane_res)); 152 memset(&pipe->stream_res, 0, sizeof(pipe->stream_res)); 153 } else if (pipe->top_pipe && pipe->top_pipe->plane_state == pipe->plane_state) { 154 struct pipe_ctx *top_pipe = pipe->top_pipe; 155 struct pipe_ctx *bottom_pipe = pipe->bottom_pipe; 156 157 top_pipe->bottom_pipe = bottom_pipe; 158 if (bottom_pipe) 159 bottom_pipe->top_pipe = top_pipe; 160 161 pipe->top_pipe = NULL; 162 pipe->bottom_pipe = NULL; 163 pipe->plane_state = NULL; 164 pipe->stream = NULL; 165 memset(&pipe->plane_res, 0, sizeof(pipe->plane_res)); 166 memset(&pipe->stream_res, 0, sizeof(pipe->stream_res)); 167 } 168 } 169 } 170 171 bool dcn32_all_pipes_have_stream_and_plane(struct dc *dc, 172 struct dc_state *context) 173 { 174 uint32_t i; 175 176 for (i = 0; i < dc->res_pool->pipe_count; i++) { 177 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; 178 179 if (!pipe->stream) 180 continue; 181 182 if (!pipe->plane_state) 183 return false; 184 } 185 return true; 186 } 187 188 bool dcn32_subvp_in_use(struct dc *dc, 189 struct dc_state *context) 190 { 191 uint32_t i; 192 193 for (i = 0; i < dc->res_pool->pipe_count; i++) { 194 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; 195 196 if (pipe->stream && pipe->stream->mall_stream_config.type != SUBVP_NONE) 197 return true; 198 } 199 return false; 200 } 201 202 bool dcn32_mpo_in_use(struct dc_state *context) 203 { 204 uint32_t i; 205 206 for (i = 0; i < context->stream_count; i++) { 207 if (context->stream_status[i].plane_count > 1) 208 return true; 209 } 210 return false; 211 } 212 213 214 bool dcn32_any_surfaces_rotated(struct dc *dc, struct dc_state *context) 215 { 216 uint32_t i; 217 218 for (i = 0; i < dc->res_pool->pipe_count; i++) { 219 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; 220 221 if (!pipe->stream) 222 continue; 223 224 if (pipe->plane_state && pipe->plane_state->rotation != ROTATION_ANGLE_0) 225 return true; 226 } 227 return false; 228 } 229 230 bool dcn32_is_center_timing(struct pipe_ctx *pipe) 231 { 232 bool is_center_timing = false; 233 234 if (pipe->stream) { 235 if (pipe->stream->timing.v_addressable != pipe->stream->dst.height || 236 pipe->stream->timing.v_addressable != pipe->stream->src.height) { 237 is_center_timing = true; 238 } 239 } 240 241 if (pipe->plane_state) { 242 if (pipe->stream->timing.v_addressable != pipe->plane_state->dst_rect.height && 243 pipe->stream->timing.v_addressable != pipe->plane_state->src_rect.height) { 244 is_center_timing = true; 245 } 246 } 247 248 return is_center_timing; 249 } 250 251 bool dcn32_is_psr_capable(struct pipe_ctx *pipe) 252 { 253 bool psr_capable = false; 254 255 if (pipe->stream && pipe->stream->link->psr_settings.psr_version != DC_PSR_VERSION_UNSUPPORTED) { 256 psr_capable = true; 257 } 258 return psr_capable; 259 } 260 261 #define DCN3_2_NEW_DET_OVERRIDE_MIN_MULTIPLIER 7 262 263 /** 264 * ******************************************************************************************* 265 * dcn32_determine_det_override: Determine DET allocation for each pipe 266 * 267 * This function determines how much DET to allocate for each pipe. The total number of 268 * DET segments will be split equally among each of the streams, and after that the DET 269 * segments per stream will be split equally among the planes for the given stream. 270 * 271 * If there is a plane that's driven by more than 1 pipe (i.e. pipe split), then the 272 * number of DET for that given plane will be split among the pipes driving that plane. 273 * 274 * High level algorithm: 275 * 1. Split total DET among number of streams 276 * 2. For each stream, split DET among the planes 277 * 3. For each plane, check if there is a pipe split. If yes, split the DET allocation 278 * among those pipes. 279 * 4. Assign the DET override to the DML pipes. 280 * 281 * Special cases: 282 * 283 * For two displays that have a large difference in pixel rate, we may experience 284 * underflow on the larger display when we divide the DET equally. For this, we 285 * will implement a modified algorithm to assign more DET to larger display. 286 * 287 * 1. Calculate difference in pixel rates ( multiplier ) between two displays 288 * 2. If the multiplier exceeds DCN3_2_NEW_DET_OVERRIDE_MIN_MULTIPLIER, then 289 * implement the modified DET override algorithm. 290 * 3. Assign smaller DET size for lower pixel display and higher DET size for 291 * higher pixel display 292 * 293 * @param [in]: dc: Current DC state 294 * @param [in]: context: New DC state to be programmed 295 * @param [in]: pipes: Array of DML pipes 296 * 297 * @return: void 298 * 299 * ******************************************************************************************* 300 */ 301 void dcn32_determine_det_override(struct dc *dc, 302 struct dc_state *context, 303 display_e2e_pipe_params_st *pipes) 304 { 305 uint32_t i, j, k; 306 uint8_t pipe_plane_count, stream_segments, plane_segments, pipe_segments[MAX_PIPES] = {0}; 307 uint8_t pipe_counted[MAX_PIPES] = {0}; 308 uint8_t pipe_cnt = 0; 309 struct dc_plane_state *current_plane = NULL; 310 uint8_t stream_count = 0; 311 312 int phy_pix_clk_mult, lower_mode_stream_index; 313 int phy_pix_clk[MAX_PIPES] = {0}; 314 bool use_new_det_override_algorithm = false; 315 316 for (i = 0; i < context->stream_count; i++) { 317 /* Don't count SubVP streams for DET allocation */ 318 if (context->streams[i]->mall_stream_config.type != SUBVP_PHANTOM) { 319 phy_pix_clk[i] = context->streams[i]->phy_pix_clk; 320 stream_count++; 321 } 322 } 323 324 /* Check for special case with two displays, one with much higher pixel rate */ 325 if (stream_count == 2) { 326 ASSERT((phy_pix_clk[0] > 0) && (phy_pix_clk[1] > 0)); 327 if (phy_pix_clk[0] < phy_pix_clk[1]) { 328 lower_mode_stream_index = 0; 329 phy_pix_clk_mult = phy_pix_clk[1] / phy_pix_clk[0]; 330 } else { 331 lower_mode_stream_index = 1; 332 phy_pix_clk_mult = phy_pix_clk[0] / phy_pix_clk[1]; 333 } 334 335 if (phy_pix_clk_mult >= DCN3_2_NEW_DET_OVERRIDE_MIN_MULTIPLIER) 336 use_new_det_override_algorithm = true; 337 } 338 339 if (stream_count > 0) { 340 stream_segments = 18 / stream_count; 341 for (i = 0; i < context->stream_count; i++) { 342 if (context->streams[i]->mall_stream_config.type == SUBVP_PHANTOM) 343 continue; 344 345 if (use_new_det_override_algorithm) { 346 if (i == lower_mode_stream_index) 347 stream_segments = 4; 348 else 349 stream_segments = 14; 350 } 351 352 if (context->stream_status[i].plane_count > 0) 353 plane_segments = stream_segments / context->stream_status[i].plane_count; 354 else 355 plane_segments = stream_segments; 356 for (j = 0; j < dc->res_pool->pipe_count; j++) { 357 pipe_plane_count = 0; 358 if (context->res_ctx.pipe_ctx[j].stream == context->streams[i] && 359 pipe_counted[j] != 1) { 360 /* Note: pipe_plane_count indicates the number of pipes to be used for a 361 * given plane. e.g. pipe_plane_count = 1 means single pipe (i.e. not split), 362 * pipe_plane_count = 2 means 2:1 split, etc. 363 */ 364 pipe_plane_count++; 365 pipe_counted[j] = 1; 366 current_plane = context->res_ctx.pipe_ctx[j].plane_state; 367 for (k = 0; k < dc->res_pool->pipe_count; k++) { 368 if (k != j && context->res_ctx.pipe_ctx[k].stream == context->streams[i] && 369 context->res_ctx.pipe_ctx[k].plane_state == current_plane) { 370 pipe_plane_count++; 371 pipe_counted[k] = 1; 372 } 373 } 374 375 pipe_segments[j] = plane_segments / pipe_plane_count; 376 for (k = 0; k < dc->res_pool->pipe_count; k++) { 377 if (k != j && context->res_ctx.pipe_ctx[k].stream == context->streams[i] && 378 context->res_ctx.pipe_ctx[k].plane_state == current_plane) { 379 pipe_segments[k] = plane_segments / pipe_plane_count; 380 } 381 } 382 } 383 } 384 } 385 386 for (i = 0, pipe_cnt = 0; i < dc->res_pool->pipe_count; i++) { 387 if (!context->res_ctx.pipe_ctx[i].stream) 388 continue; 389 pipes[pipe_cnt].pipe.src.det_size_override = pipe_segments[i] * DCN3_2_DET_SEG_SIZE; 390 pipe_cnt++; 391 } 392 } else { 393 for (i = 0; i < dc->res_pool->pipe_count; i++) 394 pipes[i].pipe.src.det_size_override = 4 * DCN3_2_DET_SEG_SIZE; //DCN3_2_DEFAULT_DET_SIZE 395 } 396 } 397 398 void dcn32_set_det_allocations(struct dc *dc, struct dc_state *context, 399 display_e2e_pipe_params_st *pipes) 400 { 401 int i, pipe_cnt; 402 struct resource_context *res_ctx = &context->res_ctx; 403 struct pipe_ctx *pipe; 404 bool disable_unbounded_requesting = dc->debug.disable_z9_mpc || dc->debug.disable_unbounded_requesting; 405 406 for (i = 0, pipe_cnt = 0; i < dc->res_pool->pipe_count; i++) { 407 408 if (!res_ctx->pipe_ctx[i].stream) 409 continue; 410 411 pipe = &res_ctx->pipe_ctx[i]; 412 pipe_cnt++; 413 } 414 415 /* For DET allocation, we don't want to use DML policy (not optimal for utilizing all 416 * the DET available for each pipe). Use the DET override input to maintain our driver 417 * policy. 418 */ 419 if (pipe_cnt == 1) { 420 pipes[0].pipe.src.det_size_override = DCN3_2_MAX_DET_SIZE; 421 if (pipe->plane_state && !disable_unbounded_requesting && pipe->plane_state->tiling_info.gfx9.swizzle != DC_SW_LINEAR) { 422 if (!is_dual_plane(pipe->plane_state->format)) { 423 pipes[0].pipe.src.det_size_override = DCN3_2_DEFAULT_DET_SIZE; 424 pipes[0].pipe.src.unbounded_req_mode = true; 425 if (pipe->plane_state->src_rect.width >= 5120 && 426 pipe->plane_state->src_rect.height >= 2880) 427 pipes[0].pipe.src.det_size_override = 320; // 5K or higher 428 } 429 } 430 } else 431 dcn32_determine_det_override(dc, context, pipes); 432 } 433 434 /** 435 * ******************************************************************************************* 436 * dcn32_save_mall_state: Save MALL (SubVP) state for fast validation cases 437 * 438 * This function saves the MALL (SubVP) case for fast validation cases. For fast validation, 439 * there are situations where a shallow copy of the dc->current_state is created for the 440 * validation. In this case we want to save and restore the mall config because we always 441 * teardown subvp at the beginning of validation (and don't attempt to add it back if it's 442 * fast validation). If we don't restore the subvp config in cases of fast validation + 443 * shallow copy of the dc->current_state, the dc->current_state will have a partially 444 * removed subvp state when we did not intend to remove it. 445 * 446 * NOTE: This function ONLY works if the streams are not moved to a different pipe in the 447 * validation. We don't expect this to happen in fast_validation=1 cases. 448 * 449 * @param [in]: dc: Current DC state 450 * @param [in]: context: New DC state to be programmed 451 * @param [out]: temp_config: struct used to cache the existing MALL state 452 * 453 * @return: void 454 * 455 * ******************************************************************************************* 456 */ 457 void dcn32_save_mall_state(struct dc *dc, 458 struct dc_state *context, 459 struct mall_temp_config *temp_config) 460 { 461 uint32_t i; 462 463 for (i = 0; i < dc->res_pool->pipe_count; i++) { 464 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; 465 466 if (pipe->stream) 467 temp_config->mall_stream_config[i] = pipe->stream->mall_stream_config; 468 469 if (pipe->plane_state) 470 temp_config->is_phantom_plane[i] = pipe->plane_state->is_phantom; 471 } 472 } 473 474 /** 475 * ******************************************************************************************* 476 * dcn32_restore_mall_state: Restore MALL (SubVP) state for fast validation cases 477 * 478 * Restore the MALL state based on the previously saved state from dcn32_save_mall_state 479 * 480 * @param [in]: dc: Current DC state 481 * @param [in/out]: context: New DC state to be programmed, restore MALL state into here 482 * @param [in]: temp_config: struct that has the cached MALL state 483 * 484 * @return: void 485 * 486 * ******************************************************************************************* 487 */ 488 void dcn32_restore_mall_state(struct dc *dc, 489 struct dc_state *context, 490 struct mall_temp_config *temp_config) 491 { 492 uint32_t i; 493 494 for (i = 0; i < dc->res_pool->pipe_count; i++) { 495 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; 496 497 if (pipe->stream) 498 pipe->stream->mall_stream_config = temp_config->mall_stream_config[i]; 499 500 if (pipe->plane_state) 501 pipe->plane_state->is_phantom = temp_config->is_phantom_plane[i]; 502 } 503 } 504 505 #define MAX_STRETCHED_V_BLANK 1000 // in micro-seconds (must ensure to match value in FW) 506 /* 507 * Scaling factor for v_blank stretch calculations considering timing in 508 * micro-seconds and pixel clock in 100hz. 509 * Note: the parenthesis are necessary to ensure the correct order of 510 * operation where V_SCALE is used. 511 */ 512 #define V_SCALE (10000 / MAX_STRETCHED_V_BLANK) 513 514 static int get_frame_rate_at_max_stretch_100hz( 515 struct dc_stream_state *fpo_candidate_stream, 516 uint32_t fpo_vactive_margin_us) 517 { 518 struct dc_crtc_timing *timing = NULL; 519 uint32_t sec_per_100_lines; 520 uint32_t max_v_blank; 521 uint32_t curr_v_blank; 522 uint32_t v_stretch_max; 523 uint32_t stretched_frame_pix_cnt; 524 uint32_t scaled_stretched_frame_pix_cnt; 525 uint32_t scaled_refresh_rate; 526 uint32_t v_scale; 527 528 if (fpo_candidate_stream == NULL) 529 return 0; 530 531 /* check if refresh rate at least 120hz */ 532 timing = &fpo_candidate_stream->timing; 533 if (timing == NULL) 534 return 0; 535 536 v_scale = 10000 / (MAX_STRETCHED_V_BLANK + fpo_vactive_margin_us); 537 538 sec_per_100_lines = timing->pix_clk_100hz / timing->h_total + 1; 539 max_v_blank = sec_per_100_lines / v_scale + 1; 540 curr_v_blank = timing->v_total - timing->v_addressable; 541 v_stretch_max = (max_v_blank > curr_v_blank) ? (max_v_blank - curr_v_blank) : (0); 542 stretched_frame_pix_cnt = (v_stretch_max + timing->v_total) * timing->h_total; 543 scaled_stretched_frame_pix_cnt = stretched_frame_pix_cnt / 10000; 544 scaled_refresh_rate = (timing->pix_clk_100hz) / scaled_stretched_frame_pix_cnt + 1; 545 546 return scaled_refresh_rate; 547 548 } 549 550 static bool is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch( 551 struct dc_stream_state *fpo_candidate_stream, uint32_t fpo_vactive_margin_us) 552 { 553 int refresh_rate_max_stretch_100hz; 554 int min_refresh_100hz; 555 556 if (fpo_candidate_stream == NULL) 557 return false; 558 559 refresh_rate_max_stretch_100hz = get_frame_rate_at_max_stretch_100hz(fpo_candidate_stream, fpo_vactive_margin_us); 560 min_refresh_100hz = fpo_candidate_stream->timing.min_refresh_in_uhz / 10000; 561 562 if (refresh_rate_max_stretch_100hz < min_refresh_100hz) 563 return false; 564 565 return true; 566 } 567 568 static int get_refresh_rate(struct dc_stream_state *fpo_candidate_stream) 569 { 570 int refresh_rate = 0; 571 int h_v_total = 0; 572 struct dc_crtc_timing *timing = NULL; 573 574 if (fpo_candidate_stream == NULL) 575 return 0; 576 577 /* check if refresh rate at least 120hz */ 578 timing = &fpo_candidate_stream->timing; 579 if (timing == NULL) 580 return 0; 581 582 h_v_total = timing->h_total * timing->v_total; 583 if (h_v_total == 0) 584 return 0; 585 586 refresh_rate = ((timing->pix_clk_100hz * 100) / (h_v_total)) + 1; 587 return refresh_rate; 588 } 589 590 /** 591 * dcn32_can_support_mclk_switch_using_fw_based_vblank_stretch - Determines if config can support FPO 592 * 593 * @param [in]: dc - current dc state 594 * @param [in]: context - new dc state 595 * 596 * Return: Pointer to FPO stream candidate if config can support FPO, otherwise NULL 597 */ 598 struct dc_stream_state *dcn32_can_support_mclk_switch_using_fw_based_vblank_stretch(struct dc *dc, const struct dc_state *context) 599 { 600 int refresh_rate = 0; 601 const int minimum_refreshrate_supported = 120; 602 struct dc_stream_state *fpo_candidate_stream = NULL; 603 bool is_fpo_vactive = false; 604 uint32_t fpo_vactive_margin_us = 0; 605 606 if (context == NULL) 607 return NULL; 608 609 if (dc->debug.disable_fams) 610 return NULL; 611 612 if (!dc->caps.dmub_caps.mclk_sw) 613 return NULL; 614 615 if (context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching_shut_down) 616 return NULL; 617 618 /* For FPO we can support up to 2 display configs if: 619 * - first display uses FPO 620 * - Second display switches in VACTIVE */ 621 if (context->stream_count > 2) 622 return NULL; 623 else if (context->stream_count == 2) { 624 DC_FP_START(); 625 dcn32_assign_fpo_vactive_candidate(dc, context, &fpo_candidate_stream); 626 DC_FP_END(); 627 628 DC_FP_START(); 629 is_fpo_vactive = dcn32_find_vactive_pipe(dc, context, DCN3_2_MIN_ACTIVE_SWITCH_MARGIN_FPO_US); 630 DC_FP_END(); 631 if (!is_fpo_vactive || dc->debug.disable_fpo_vactive) 632 return NULL; 633 } else 634 fpo_candidate_stream = context->streams[0]; 635 636 if (!fpo_candidate_stream) 637 return NULL; 638 639 if (fpo_candidate_stream->sink->edid_caps.panel_patch.disable_fams) 640 return NULL; 641 642 refresh_rate = get_refresh_rate(fpo_candidate_stream); 643 if (refresh_rate < minimum_refreshrate_supported) 644 return NULL; 645 646 fpo_vactive_margin_us = is_fpo_vactive ? dc->debug.fpo_vactive_margin_us : 0; // For now hardcode the FPO + Vactive stretch margin to be 2000us 647 if (!is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch(fpo_candidate_stream, fpo_vactive_margin_us)) 648 return NULL; 649 650 // check if freesync enabled 651 if (!fpo_candidate_stream->allow_freesync) 652 return NULL; 653 654 if (fpo_candidate_stream->vrr_active_variable) 655 return NULL; 656 657 return fpo_candidate_stream; 658 } 659