1/* 2 * Copyright 2012-15 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#include "dm_services.h" 27 28#include "resource.h" 29#include "include/irq_service_interface.h" 30#include "link_encoder.h" 31#include "stream_encoder.h" 32#include "opp.h" 33#include "timing_generator.h" 34#include "transform.h" 35#include "dccg.h" 36#include "dchubbub.h" 37#include "dpp.h" 38#include "core_types.h" 39#include "set_mode_types.h" 40#include "virtual/virtual_stream_encoder.h" 41#include "dpcd_defs.h" 42#include "link_enc_cfg.h" 43#include "link.h" 44#include "clk_mgr.h" 45#include "dc_state_priv.h" 46#include "virtual/virtual_link_hwss.h" 47#include "link/hwss/link_hwss_dio.h" 48#include "link/hwss/link_hwss_dpia.h" 49#include "link/hwss/link_hwss_hpo_dp.h" 50#include "link/hwss/link_hwss_dio_fixed_vs_pe_retimer.h" 51#include "link/hwss/link_hwss_hpo_fixed_vs_pe_retimer_dp.h" 52 53#if defined(CONFIG_DRM_AMD_DC_SI) 54#include "dce60/dce60_resource.h" 55#endif 56#include "dce80/dce80_resource.h" 57#include "dce100/dce100_resource.h" 58#include "dce110/dce110_resource.h" 59#include "dce112/dce112_resource.h" 60#include "dce120/dce120_resource.h" 61#include "dcn10/dcn10_resource.h" 62#include "dcn20/dcn20_resource.h" 63#include "dcn21/dcn21_resource.h" 64#include "dcn201/dcn201_resource.h" 65#include "dcn30/dcn30_resource.h" 66#include "dcn301/dcn301_resource.h" 67#include "dcn302/dcn302_resource.h" 68#include "dcn303/dcn303_resource.h" 69#include "dcn31/dcn31_resource.h" 70#include "dcn314/dcn314_resource.h" 71#include "dcn315/dcn315_resource.h" 72#include "dcn316/dcn316_resource.h" 73#include "dcn32/dcn32_resource.h" 74#include "dcn321/dcn321_resource.h" 75#include "dcn35/dcn35_resource.h" 76#include "dcn351/dcn351_resource.h" 77 78#define VISUAL_CONFIRM_BASE_DEFAULT 3 79#define VISUAL_CONFIRM_BASE_MIN 1 80#define VISUAL_CONFIRM_BASE_MAX 10 81/* we choose 240 because it is a common denominator of common v addressable 82 * such as 2160, 1440, 1200, 960. So we take 1/240 portion of v addressable as 83 * the visual confirm dpp offset height. So visual confirm height can stay 84 * relatively the same independent from timing used. 85 */ 86#define VISUAL_CONFIRM_DPP_OFFSET_DENO 240 87 88#define DC_LOGGER \ 89 dc->ctx->logger 90#define DC_LOGGER_INIT(logger) 91 92#include "dml2/dml2_wrapper.h" 93 94#define UNABLE_TO_SPLIT -1 95 96enum dce_version resource_parse_asic_id(struct hw_asic_id asic_id) 97{ 98 enum dce_version dc_version = DCE_VERSION_UNKNOWN; 99 100 switch (asic_id.chip_family) { 101 102#if defined(CONFIG_DRM_AMD_DC_SI) 103 case FAMILY_SI: 104 if (ASIC_REV_IS_TAHITI_P(asic_id.hw_internal_rev) || 105 ASIC_REV_IS_PITCAIRN_PM(asic_id.hw_internal_rev) || 106 ASIC_REV_IS_CAPEVERDE_M(asic_id.hw_internal_rev)) 107 dc_version = DCE_VERSION_6_0; 108 else if (ASIC_REV_IS_OLAND_M(asic_id.hw_internal_rev)) 109 dc_version = DCE_VERSION_6_4; 110 else 111 dc_version = DCE_VERSION_6_1; 112 break; 113#endif 114 case FAMILY_CI: 115 dc_version = DCE_VERSION_8_0; 116 break; 117 case FAMILY_KV: 118 if (ASIC_REV_IS_KALINDI(asic_id.hw_internal_rev) || 119 ASIC_REV_IS_BHAVANI(asic_id.hw_internal_rev) || 120 ASIC_REV_IS_GODAVARI(asic_id.hw_internal_rev)) 121 dc_version = DCE_VERSION_8_3; 122 else 123 dc_version = DCE_VERSION_8_1; 124 break; 125 case FAMILY_CZ: 126 dc_version = DCE_VERSION_11_0; 127 break; 128 129 case FAMILY_VI: 130 if (ASIC_REV_IS_TONGA_P(asic_id.hw_internal_rev) || 131 ASIC_REV_IS_FIJI_P(asic_id.hw_internal_rev)) { 132 dc_version = DCE_VERSION_10_0; 133 break; 134 } 135 if (ASIC_REV_IS_POLARIS10_P(asic_id.hw_internal_rev) || 136 ASIC_REV_IS_POLARIS11_M(asic_id.hw_internal_rev) || 137 ASIC_REV_IS_POLARIS12_V(asic_id.hw_internal_rev)) { 138 dc_version = DCE_VERSION_11_2; 139 } 140 if (ASIC_REV_IS_VEGAM(asic_id.hw_internal_rev)) 141 dc_version = DCE_VERSION_11_22; 142 break; 143 case FAMILY_AI: 144 if (ASICREV_IS_VEGA20_P(asic_id.hw_internal_rev)) 145 dc_version = DCE_VERSION_12_1; 146 else 147 dc_version = DCE_VERSION_12_0; 148 break; 149 case FAMILY_RV: 150 dc_version = DCN_VERSION_1_0; 151 if (ASICREV_IS_RAVEN2(asic_id.hw_internal_rev)) 152 dc_version = DCN_VERSION_1_01; 153 if (ASICREV_IS_RENOIR(asic_id.hw_internal_rev)) 154 dc_version = DCN_VERSION_2_1; 155 if (ASICREV_IS_GREEN_SARDINE(asic_id.hw_internal_rev)) 156 dc_version = DCN_VERSION_2_1; 157 break; 158 159 case FAMILY_NV: 160 dc_version = DCN_VERSION_2_0; 161 if (asic_id.chip_id == DEVICE_ID_NV_13FE || asic_id.chip_id == DEVICE_ID_NV_143F) { 162 dc_version = DCN_VERSION_2_01; 163 break; 164 } 165 if (ASICREV_IS_SIENNA_CICHLID_P(asic_id.hw_internal_rev)) 166 dc_version = DCN_VERSION_3_0; 167 if (ASICREV_IS_DIMGREY_CAVEFISH_P(asic_id.hw_internal_rev)) 168 dc_version = DCN_VERSION_3_02; 169 if (ASICREV_IS_BEIGE_GOBY_P(asic_id.hw_internal_rev)) 170 dc_version = DCN_VERSION_3_03; 171 break; 172 173 case FAMILY_VGH: 174 dc_version = DCN_VERSION_3_01; 175 break; 176 177 case FAMILY_YELLOW_CARP: 178 if (ASICREV_IS_YELLOW_CARP(asic_id.hw_internal_rev)) 179 dc_version = DCN_VERSION_3_1; 180 break; 181 case AMDGPU_FAMILY_GC_10_3_6: 182 if (ASICREV_IS_GC_10_3_6(asic_id.hw_internal_rev)) 183 dc_version = DCN_VERSION_3_15; 184 break; 185 case AMDGPU_FAMILY_GC_10_3_7: 186 if (ASICREV_IS_GC_10_3_7(asic_id.hw_internal_rev)) 187 dc_version = DCN_VERSION_3_16; 188 break; 189 case AMDGPU_FAMILY_GC_11_0_0: 190 dc_version = DCN_VERSION_3_2; 191 if (ASICREV_IS_GC_11_0_2(asic_id.hw_internal_rev)) 192 dc_version = DCN_VERSION_3_21; 193 break; 194 case AMDGPU_FAMILY_GC_11_0_1: 195 dc_version = DCN_VERSION_3_14; 196 break; 197 case AMDGPU_FAMILY_GC_11_5_0: 198 dc_version = DCN_VERSION_3_5; 199 if (ASICREV_IS_GC_11_0_4(asic_id.hw_internal_rev)) 200 dc_version = DCN_VERSION_3_51; 201 break; 202 default: 203 dc_version = DCE_VERSION_UNKNOWN; 204 break; 205 } 206 return dc_version; 207} 208 209struct resource_pool *dc_create_resource_pool(struct dc *dc, 210 const struct dc_init_data *init_data, 211 enum dce_version dc_version) 212{ 213 struct resource_pool *res_pool = NULL; 214 215 switch (dc_version) { 216#if defined(CONFIG_DRM_AMD_DC_SI) 217 case DCE_VERSION_6_0: 218 res_pool = dce60_create_resource_pool( 219 init_data->num_virtual_links, dc); 220 break; 221 case DCE_VERSION_6_1: 222 res_pool = dce61_create_resource_pool( 223 init_data->num_virtual_links, dc); 224 break; 225 case DCE_VERSION_6_4: 226 res_pool = dce64_create_resource_pool( 227 init_data->num_virtual_links, dc); 228 break; 229#endif 230 case DCE_VERSION_8_0: 231 res_pool = dce80_create_resource_pool( 232 init_data->num_virtual_links, dc); 233 break; 234 case DCE_VERSION_8_1: 235 res_pool = dce81_create_resource_pool( 236 init_data->num_virtual_links, dc); 237 break; 238 case DCE_VERSION_8_3: 239 res_pool = dce83_create_resource_pool( 240 init_data->num_virtual_links, dc); 241 break; 242 case DCE_VERSION_10_0: 243 res_pool = dce100_create_resource_pool( 244 init_data->num_virtual_links, dc); 245 break; 246 case DCE_VERSION_11_0: 247 res_pool = dce110_create_resource_pool( 248 init_data->num_virtual_links, dc, 249 init_data->asic_id); 250 break; 251 case DCE_VERSION_11_2: 252 case DCE_VERSION_11_22: 253 res_pool = dce112_create_resource_pool( 254 init_data->num_virtual_links, dc); 255 break; 256 case DCE_VERSION_12_0: 257 case DCE_VERSION_12_1: 258 res_pool = dce120_create_resource_pool( 259 init_data->num_virtual_links, dc); 260 break; 261 262#if defined(CONFIG_DRM_AMD_DC_FP) 263 case DCN_VERSION_1_0: 264 case DCN_VERSION_1_01: 265 res_pool = dcn10_create_resource_pool(init_data, dc); 266 break; 267 case DCN_VERSION_2_0: 268 res_pool = dcn20_create_resource_pool(init_data, dc); 269 break; 270 case DCN_VERSION_2_1: 271 res_pool = dcn21_create_resource_pool(init_data, dc); 272 break; 273 case DCN_VERSION_2_01: 274 res_pool = dcn201_create_resource_pool(init_data, dc); 275 break; 276 case DCN_VERSION_3_0: 277 res_pool = dcn30_create_resource_pool(init_data, dc); 278 break; 279 case DCN_VERSION_3_01: 280 res_pool = dcn301_create_resource_pool(init_data, dc); 281 break; 282 case DCN_VERSION_3_02: 283 res_pool = dcn302_create_resource_pool(init_data, dc); 284 break; 285 case DCN_VERSION_3_03: 286 res_pool = dcn303_create_resource_pool(init_data, dc); 287 break; 288 case DCN_VERSION_3_1: 289 res_pool = dcn31_create_resource_pool(init_data, dc); 290 break; 291 case DCN_VERSION_3_14: 292 res_pool = dcn314_create_resource_pool(init_data, dc); 293 break; 294 case DCN_VERSION_3_15: 295 res_pool = dcn315_create_resource_pool(init_data, dc); 296 break; 297 case DCN_VERSION_3_16: 298 res_pool = dcn316_create_resource_pool(init_data, dc); 299 break; 300 case DCN_VERSION_3_2: 301 res_pool = dcn32_create_resource_pool(init_data, dc); 302 break; 303 case DCN_VERSION_3_21: 304 res_pool = dcn321_create_resource_pool(init_data, dc); 305 break; 306 case DCN_VERSION_3_5: 307 res_pool = dcn35_create_resource_pool(init_data, dc); 308 break; 309 case DCN_VERSION_3_51: 310 res_pool = dcn351_create_resource_pool(init_data, dc); 311 break; 312#endif /* CONFIG_DRM_AMD_DC_FP */ 313 default: 314 break; 315 } 316 317 if (res_pool != NULL) { 318 if (dc->ctx->dc_bios->fw_info_valid) { 319 res_pool->ref_clocks.xtalin_clock_inKhz = 320 dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency; 321 /* initialize with firmware data first, no all 322 * ASIC have DCCG SW component. FPGA or 323 * simulation need initialization of 324 * dccg_ref_clock_inKhz, dchub_ref_clock_inKhz 325 * with xtalin_clock_inKhz 326 */ 327 res_pool->ref_clocks.dccg_ref_clock_inKhz = 328 res_pool->ref_clocks.xtalin_clock_inKhz; 329 res_pool->ref_clocks.dchub_ref_clock_inKhz = 330 res_pool->ref_clocks.xtalin_clock_inKhz; 331 if (dc->debug.using_dml2) 332 if (res_pool->hubbub && res_pool->hubbub->funcs->get_dchub_ref_freq) 333 res_pool->hubbub->funcs->get_dchub_ref_freq(res_pool->hubbub, 334 res_pool->ref_clocks.dccg_ref_clock_inKhz, 335 &res_pool->ref_clocks.dchub_ref_clock_inKhz); 336 } else 337 ASSERT_CRITICAL(false); 338 } 339 340 return res_pool; 341} 342 343void dc_destroy_resource_pool(struct dc *dc) 344{ 345 if (dc) { 346 if (dc->res_pool) 347 dc->res_pool->funcs->destroy(&dc->res_pool); 348 349 kfree(dc->hwseq); 350 } 351} 352 353static void update_num_audio( 354 const struct resource_straps *straps, 355 unsigned int *num_audio, 356 struct audio_support *aud_support) 357{ 358 aud_support->dp_audio = true; 359 aud_support->hdmi_audio_native = false; 360 aud_support->hdmi_audio_on_dongle = false; 361 362 if (straps->hdmi_disable == 0) { 363 if (straps->dc_pinstraps_audio & 0x2) { 364 aud_support->hdmi_audio_on_dongle = true; 365 aud_support->hdmi_audio_native = true; 366 } 367 } 368 369 switch (straps->audio_stream_number) { 370 case 0: /* multi streams supported */ 371 break; 372 case 1: /* multi streams not supported */ 373 *num_audio = 1; 374 break; 375 default: 376 DC_ERR("DC: unexpected audio fuse!\n"); 377 } 378} 379 380bool resource_construct( 381 unsigned int num_virtual_links, 382 struct dc *dc, 383 struct resource_pool *pool, 384 const struct resource_create_funcs *create_funcs) 385{ 386 struct dc_context *ctx = dc->ctx; 387 const struct resource_caps *caps = pool->res_cap; 388 int i; 389 unsigned int num_audio = caps->num_audio; 390 struct resource_straps straps = {0}; 391 392 if (create_funcs->read_dce_straps) 393 create_funcs->read_dce_straps(dc->ctx, &straps); 394 395 pool->audio_count = 0; 396 if (create_funcs->create_audio) { 397 /* find the total number of streams available via the 398 * AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT 399 * registers (one for each pin) starting from pin 1 400 * up to the max number of audio pins. 401 * We stop on the first pin where 402 * PORT_CONNECTIVITY == 1 (as instructed by HW team). 403 */ 404 update_num_audio(&straps, &num_audio, &pool->audio_support); 405 for (i = 0; i < caps->num_audio; i++) { 406 struct audio *aud = create_funcs->create_audio(ctx, i); 407 408 if (aud == NULL) { 409 DC_ERR("DC: failed to create audio!\n"); 410 return false; 411 } 412 if (!aud->funcs->endpoint_valid(aud)) { 413 aud->funcs->destroy(&aud); 414 break; 415 } 416 pool->audios[i] = aud; 417 pool->audio_count++; 418 } 419 } 420 421 pool->stream_enc_count = 0; 422 if (create_funcs->create_stream_encoder) { 423 for (i = 0; i < caps->num_stream_encoder; i++) { 424 pool->stream_enc[i] = create_funcs->create_stream_encoder(i, ctx); 425 if (pool->stream_enc[i] == NULL) 426 DC_ERR("DC: failed to create stream_encoder!\n"); 427 pool->stream_enc_count++; 428 } 429 } 430 431 pool->hpo_dp_stream_enc_count = 0; 432 if (create_funcs->create_hpo_dp_stream_encoder) { 433 for (i = 0; i < caps->num_hpo_dp_stream_encoder; i++) { 434 pool->hpo_dp_stream_enc[i] = create_funcs->create_hpo_dp_stream_encoder(i+ENGINE_ID_HPO_DP_0, ctx); 435 if (pool->hpo_dp_stream_enc[i] == NULL) 436 DC_ERR("DC: failed to create HPO DP stream encoder!\n"); 437 pool->hpo_dp_stream_enc_count++; 438 439 } 440 } 441 442 pool->hpo_dp_link_enc_count = 0; 443 if (create_funcs->create_hpo_dp_link_encoder) { 444 for (i = 0; i < caps->num_hpo_dp_link_encoder; i++) { 445 pool->hpo_dp_link_enc[i] = create_funcs->create_hpo_dp_link_encoder(i, ctx); 446 if (pool->hpo_dp_link_enc[i] == NULL) 447 DC_ERR("DC: failed to create HPO DP link encoder!\n"); 448 pool->hpo_dp_link_enc_count++; 449 } 450 } 451 452 for (i = 0; i < caps->num_mpc_3dlut; i++) { 453 pool->mpc_lut[i] = dc_create_3dlut_func(); 454 if (pool->mpc_lut[i] == NULL) 455 DC_ERR("DC: failed to create MPC 3dlut!\n"); 456 pool->mpc_shaper[i] = dc_create_transfer_func(); 457 if (pool->mpc_shaper[i] == NULL) 458 DC_ERR("DC: failed to create MPC shaper!\n"); 459 } 460 461 dc->caps.dynamic_audio = false; 462 if (pool->audio_count < pool->stream_enc_count) { 463 dc->caps.dynamic_audio = true; 464 } 465 for (i = 0; i < num_virtual_links; i++) { 466 pool->stream_enc[pool->stream_enc_count] = 467 virtual_stream_encoder_create( 468 ctx, ctx->dc_bios); 469 if (pool->stream_enc[pool->stream_enc_count] == NULL) { 470 DC_ERR("DC: failed to create stream_encoder!\n"); 471 return false; 472 } 473 pool->stream_enc_count++; 474 } 475 476 dc->hwseq = create_funcs->create_hwseq(ctx); 477 478 return true; 479} 480static int find_matching_clock_source( 481 const struct resource_pool *pool, 482 struct clock_source *clock_source) 483{ 484 485 int i; 486 487 for (i = 0; i < pool->clk_src_count; i++) { 488 if (pool->clock_sources[i] == clock_source) 489 return i; 490 } 491 return -1; 492} 493 494void resource_unreference_clock_source( 495 struct resource_context *res_ctx, 496 const struct resource_pool *pool, 497 struct clock_source *clock_source) 498{ 499 int i = find_matching_clock_source(pool, clock_source); 500 501 if (i > -1) 502 res_ctx->clock_source_ref_count[i]--; 503 504 if (pool->dp_clock_source == clock_source) 505 res_ctx->dp_clock_source_ref_count--; 506} 507 508void resource_reference_clock_source( 509 struct resource_context *res_ctx, 510 const struct resource_pool *pool, 511 struct clock_source *clock_source) 512{ 513 int i = find_matching_clock_source(pool, clock_source); 514 515 if (i > -1) 516 res_ctx->clock_source_ref_count[i]++; 517 518 if (pool->dp_clock_source == clock_source) 519 res_ctx->dp_clock_source_ref_count++; 520} 521 522int resource_get_clock_source_reference( 523 struct resource_context *res_ctx, 524 const struct resource_pool *pool, 525 struct clock_source *clock_source) 526{ 527 int i = find_matching_clock_source(pool, clock_source); 528 529 if (i > -1) 530 return res_ctx->clock_source_ref_count[i]; 531 532 if (pool->dp_clock_source == clock_source) 533 return res_ctx->dp_clock_source_ref_count; 534 535 return -1; 536} 537 538bool resource_are_vblanks_synchronizable( 539 struct dc_stream_state *stream1, 540 struct dc_stream_state *stream2) 541{ 542 uint32_t base60_refresh_rates[] = {10, 20, 5}; 543 uint8_t i; 544 uint8_t rr_count = ARRAY_SIZE(base60_refresh_rates); 545 uint64_t frame_time_diff; 546 547 if (stream1->ctx->dc->config.vblank_alignment_dto_params && 548 stream1->ctx->dc->config.vblank_alignment_max_frame_time_diff > 0 && 549 dc_is_dp_signal(stream1->signal) && 550 dc_is_dp_signal(stream2->signal) && 551 false == stream1->has_non_synchronizable_pclk && 552 false == stream2->has_non_synchronizable_pclk && 553 stream1->timing.flags.VBLANK_SYNCHRONIZABLE && 554 stream2->timing.flags.VBLANK_SYNCHRONIZABLE) { 555 /* disable refresh rates higher than 60Hz for now */ 556 if (stream1->timing.pix_clk_100hz*100/stream1->timing.h_total/ 557 stream1->timing.v_total > 60) 558 return false; 559 if (stream2->timing.pix_clk_100hz*100/stream2->timing.h_total/ 560 stream2->timing.v_total > 60) 561 return false; 562 frame_time_diff = (uint64_t)10000 * 563 stream1->timing.h_total * 564 stream1->timing.v_total * 565 stream2->timing.pix_clk_100hz; 566 frame_time_diff = div_u64(frame_time_diff, stream1->timing.pix_clk_100hz); 567 frame_time_diff = div_u64(frame_time_diff, stream2->timing.h_total); 568 frame_time_diff = div_u64(frame_time_diff, stream2->timing.v_total); 569 for (i = 0; i < rr_count; i++) { 570 int64_t diff = (int64_t)div_u64(frame_time_diff * base60_refresh_rates[i], 10) - 10000; 571 572 if (diff < 0) 573 diff = -diff; 574 if (diff < stream1->ctx->dc->config.vblank_alignment_max_frame_time_diff) 575 return true; 576 } 577 } 578 return false; 579} 580 581bool resource_are_streams_timing_synchronizable( 582 struct dc_stream_state *stream1, 583 struct dc_stream_state *stream2) 584{ 585 if (stream1->timing.h_total != stream2->timing.h_total) 586 return false; 587 588 if (stream1->timing.v_total != stream2->timing.v_total) 589 return false; 590 591 if (stream1->timing.h_addressable 592 != stream2->timing.h_addressable) 593 return false; 594 595 if (stream1->timing.v_addressable 596 != stream2->timing.v_addressable) 597 return false; 598 599 if (stream1->timing.v_front_porch 600 != stream2->timing.v_front_porch) 601 return false; 602 603 if (stream1->timing.pix_clk_100hz 604 != stream2->timing.pix_clk_100hz) 605 return false; 606 607 if (stream1->clamping.c_depth != stream2->clamping.c_depth) 608 return false; 609 610 if (stream1->phy_pix_clk != stream2->phy_pix_clk 611 && (!dc_is_dp_signal(stream1->signal) 612 || !dc_is_dp_signal(stream2->signal))) 613 return false; 614 615 if (stream1->view_format != stream2->view_format) 616 return false; 617 618 if (stream1->ignore_msa_timing_param || stream2->ignore_msa_timing_param) 619 return false; 620 621 return true; 622} 623static bool is_dp_and_hdmi_sharable( 624 struct dc_stream_state *stream1, 625 struct dc_stream_state *stream2) 626{ 627 if (stream1->ctx->dc->caps.disable_dp_clk_share) 628 return false; 629 630 if (stream1->clamping.c_depth != COLOR_DEPTH_888 || 631 stream2->clamping.c_depth != COLOR_DEPTH_888) 632 return false; 633 634 return true; 635 636} 637 638static bool is_sharable_clk_src( 639 const struct pipe_ctx *pipe_with_clk_src, 640 const struct pipe_ctx *pipe) 641{ 642 if (pipe_with_clk_src->clock_source == NULL) 643 return false; 644 645 if (pipe_with_clk_src->stream->signal == SIGNAL_TYPE_VIRTUAL) 646 return false; 647 648 if (dc_is_dp_signal(pipe_with_clk_src->stream->signal) || 649 (dc_is_dp_signal(pipe->stream->signal) && 650 !is_dp_and_hdmi_sharable(pipe_with_clk_src->stream, 651 pipe->stream))) 652 return false; 653 654 if (dc_is_hdmi_signal(pipe_with_clk_src->stream->signal) 655 && dc_is_dual_link_signal(pipe->stream->signal)) 656 return false; 657 658 if (dc_is_hdmi_signal(pipe->stream->signal) 659 && dc_is_dual_link_signal(pipe_with_clk_src->stream->signal)) 660 return false; 661 662 if (!resource_are_streams_timing_synchronizable( 663 pipe_with_clk_src->stream, pipe->stream)) 664 return false; 665 666 return true; 667} 668 669struct clock_source *resource_find_used_clk_src_for_sharing( 670 struct resource_context *res_ctx, 671 struct pipe_ctx *pipe_ctx) 672{ 673 int i; 674 675 for (i = 0; i < MAX_PIPES; i++) { 676 if (is_sharable_clk_src(&res_ctx->pipe_ctx[i], pipe_ctx)) 677 return res_ctx->pipe_ctx[i].clock_source; 678 } 679 680 return NULL; 681} 682 683static enum pixel_format convert_pixel_format_to_dalsurface( 684 enum surface_pixel_format surface_pixel_format) 685{ 686 enum pixel_format dal_pixel_format = PIXEL_FORMAT_UNKNOWN; 687 688 switch (surface_pixel_format) { 689 case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS: 690 dal_pixel_format = PIXEL_FORMAT_INDEX8; 691 break; 692 case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: 693 dal_pixel_format = PIXEL_FORMAT_RGB565; 694 break; 695 case SURFACE_PIXEL_FORMAT_GRPH_RGB565: 696 dal_pixel_format = PIXEL_FORMAT_RGB565; 697 break; 698 case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: 699 dal_pixel_format = PIXEL_FORMAT_ARGB8888; 700 break; 701 case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: 702 dal_pixel_format = PIXEL_FORMAT_ARGB8888; 703 break; 704 case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: 705 dal_pixel_format = PIXEL_FORMAT_ARGB2101010; 706 break; 707 case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: 708 dal_pixel_format = PIXEL_FORMAT_ARGB2101010; 709 break; 710 case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: 711 dal_pixel_format = PIXEL_FORMAT_ARGB2101010_XRBIAS; 712 break; 713 case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: 714 case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: 715 dal_pixel_format = PIXEL_FORMAT_FP16; 716 break; 717 case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: 718 case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: 719 dal_pixel_format = PIXEL_FORMAT_420BPP8; 720 break; 721 case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr: 722 case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb: 723 dal_pixel_format = PIXEL_FORMAT_420BPP10; 724 break; 725 case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: 726 case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616: 727 default: 728 dal_pixel_format = PIXEL_FORMAT_UNKNOWN; 729 break; 730 } 731 return dal_pixel_format; 732} 733 734static inline void get_vp_scan_direction( 735 enum dc_rotation_angle rotation, 736 bool horizontal_mirror, 737 bool *orthogonal_rotation, 738 bool *flip_vert_scan_dir, 739 bool *flip_horz_scan_dir) 740{ 741 *orthogonal_rotation = false; 742 *flip_vert_scan_dir = false; 743 *flip_horz_scan_dir = false; 744 if (rotation == ROTATION_ANGLE_180) { 745 *flip_vert_scan_dir = true; 746 *flip_horz_scan_dir = true; 747 } else if (rotation == ROTATION_ANGLE_90) { 748 *orthogonal_rotation = true; 749 *flip_horz_scan_dir = true; 750 } else if (rotation == ROTATION_ANGLE_270) { 751 *orthogonal_rotation = true; 752 *flip_vert_scan_dir = true; 753 } 754 755 if (horizontal_mirror) 756 *flip_horz_scan_dir = !*flip_horz_scan_dir; 757} 758 759/* 760 * This is a preliminary vp size calculation to allow us to check taps support. 761 * The result is completely overridden afterwards. 762 */ 763static void calculate_viewport_size(struct pipe_ctx *pipe_ctx) 764{ 765 struct scaler_data *data = &pipe_ctx->plane_res.scl_data; 766 767 data->viewport.width = dc_fixpt_ceil(dc_fixpt_mul_int(data->ratios.horz, data->recout.width)); 768 data->viewport.height = dc_fixpt_ceil(dc_fixpt_mul_int(data->ratios.vert, data->recout.height)); 769 data->viewport_c.width = dc_fixpt_ceil(dc_fixpt_mul_int(data->ratios.horz_c, data->recout.width)); 770 data->viewport_c.height = dc_fixpt_ceil(dc_fixpt_mul_int(data->ratios.vert_c, data->recout.height)); 771 if (pipe_ctx->plane_state->rotation == ROTATION_ANGLE_90 || 772 pipe_ctx->plane_state->rotation == ROTATION_ANGLE_270) { 773 swap(data->viewport.width, data->viewport.height); 774 swap(data->viewport_c.width, data->viewport_c.height); 775 } 776} 777 778static struct rect intersect_rec(const struct rect *r0, const struct rect *r1) 779{ 780 struct rect rec; 781 int r0_x_end = r0->x + r0->width; 782 int r1_x_end = r1->x + r1->width; 783 int r0_y_end = r0->y + r0->height; 784 int r1_y_end = r1->y + r1->height; 785 786 rec.x = r0->x > r1->x ? r0->x : r1->x; 787 rec.width = r0_x_end > r1_x_end ? r1_x_end - rec.x : r0_x_end - rec.x; 788 rec.y = r0->y > r1->y ? r0->y : r1->y; 789 rec.height = r0_y_end > r1_y_end ? r1_y_end - rec.y : r0_y_end - rec.y; 790 791 /* in case that there is no intersection */ 792 if (rec.width < 0 || rec.height < 0) 793 memset(&rec, 0, sizeof(rec)); 794 795 return rec; 796} 797 798static struct rect shift_rec(const struct rect *rec_in, int x, int y) 799{ 800 struct rect rec_out = *rec_in; 801 802 rec_out.x += x; 803 rec_out.y += y; 804 805 return rec_out; 806} 807 808static struct rect calculate_odm_slice_in_timing_active(struct pipe_ctx *pipe_ctx) 809{ 810 const struct dc_stream_state *stream = pipe_ctx->stream; 811 int odm_slice_count = resource_get_odm_slice_count(pipe_ctx); 812 int odm_slice_idx = resource_get_odm_slice_index(pipe_ctx); 813 bool is_last_odm_slice = (odm_slice_idx + 1) == odm_slice_count; 814 int h_active = stream->timing.h_addressable + 815 stream->timing.h_border_left + 816 stream->timing.h_border_right; 817 int odm_slice_width = h_active / odm_slice_count; 818 struct rect odm_rec; 819 820 odm_rec.x = odm_slice_width * odm_slice_idx; 821 odm_rec.width = is_last_odm_slice ? 822 /* last slice width is the reminder of h_active */ 823 h_active - odm_slice_width * (odm_slice_count - 1) : 824 /* odm slice width is the floor of h_active / count */ 825 odm_slice_width; 826 odm_rec.y = 0; 827 odm_rec.height = stream->timing.v_addressable + 828 stream->timing.v_border_bottom + 829 stream->timing.v_border_top; 830 831 return odm_rec; 832} 833 834static struct rect calculate_plane_rec_in_timing_active( 835 struct pipe_ctx *pipe_ctx, 836 const struct rect *rec_in) 837{ 838 /* 839 * The following diagram shows an example where we map a 1920x1200 840 * desktop to a 2560x1440 timing with a plane rect in the middle 841 * of the screen. To map a plane rect from Stream Source to Timing 842 * Active space, we first multiply stream scaling ratios (i.e 2304/1920 843 * horizontal and 1440/1200 vertical) to the plane's x and y, then 844 * we add stream destination offsets (i.e 128 horizontal, 0 vertical). 845 * This will give us a plane rect's position in Timing Active. However 846 * we have to remove the fractional. The rule is that we find left/right 847 * and top/bottom positions and round the value to the adjacent integer. 848 * 849 * Stream Source Space 850 * ------------ 851 * __________________________________________________ 852 * |Stream Source (1920 x 1200) ^ | 853 * | y | 854 * | <------- w --------|> | 855 * | __________________V | 856 * |<-- x -->|Plane//////////////| ^ | 857 * | |(pre scale)////////| | | 858 * | |///////////////////| | | 859 * | |///////////////////| h | 860 * | |///////////////////| | | 861 * | |///////////////////| | | 862 * | |///////////////////| V | 863 * | | 864 * | | 865 * |__________________________________________________| 866 * 867 * 868 * Timing Active Space 869 * --------------------------------- 870 * 871 * Timing Active (2560 x 1440) 872 * __________________________________________________ 873 * |*****| Stteam Destination (2304 x 1440) |*****| 874 * |*****| |*****| 875 * |<128>| |*****| 876 * |*****| __________________ |*****| 877 * |*****| |Plane/////////////| |*****| 878 * |*****| |(post scale)//////| |*****| 879 * |*****| |//////////////////| |*****| 880 * |*****| |//////////////////| |*****| 881 * |*****| |//////////////////| |*****| 882 * |*****| |//////////////////| |*****| 883 * |*****| |*****| 884 * |*****| |*****| 885 * |*****| |*****| 886 * |*****|______________________________________|*****| 887 * 888 * So the resulting formulas are shown below: 889 * 890 * recout_x = 128 + round(plane_x * 2304 / 1920) 891 * recout_w = 128 + round((plane_x + plane_w) * 2304 / 1920) - recout_x 892 * recout_y = 0 + round(plane_y * 1440 / 1280) 893 * recout_h = 0 + round((plane_y + plane_h) * 1440 / 1200) - recout_y 894 * 895 * NOTE: fixed point division is not error free. To reduce errors 896 * introduced by fixed point division, we divide only after 897 * multiplication is complete. 898 */ 899 const struct dc_stream_state *stream = pipe_ctx->stream; 900 struct rect rec_out = {0}; 901 struct fixed31_32 temp; 902 903 temp = dc_fixpt_from_fraction(rec_in->x * stream->dst.width, 904 stream->src.width); 905 rec_out.x = stream->dst.x + dc_fixpt_round(temp); 906 907 temp = dc_fixpt_from_fraction( 908 (rec_in->x + rec_in->width) * stream->dst.width, 909 stream->src.width); 910 rec_out.width = stream->dst.x + dc_fixpt_round(temp) - rec_out.x; 911 912 temp = dc_fixpt_from_fraction(rec_in->y * stream->dst.height, 913 stream->src.height); 914 rec_out.y = stream->dst.y + dc_fixpt_round(temp); 915 916 temp = dc_fixpt_from_fraction( 917 (rec_in->y + rec_in->height) * stream->dst.height, 918 stream->src.height); 919 rec_out.height = stream->dst.y + dc_fixpt_round(temp) - rec_out.y; 920 921 return rec_out; 922} 923 924static struct rect calculate_mpc_slice_in_timing_active( 925 struct pipe_ctx *pipe_ctx, 926 struct rect *plane_clip_rec) 927{ 928 const struct dc_stream_state *stream = pipe_ctx->stream; 929 int mpc_slice_count = resource_get_mpc_slice_count(pipe_ctx); 930 int mpc_slice_idx = resource_get_mpc_slice_index(pipe_ctx); 931 int epimo = mpc_slice_count - plane_clip_rec->width % mpc_slice_count - 1; 932 struct rect mpc_rec; 933 934 mpc_rec.width = plane_clip_rec->width / mpc_slice_count; 935 mpc_rec.x = plane_clip_rec->x + mpc_rec.width * mpc_slice_idx; 936 mpc_rec.height = plane_clip_rec->height; 937 mpc_rec.y = plane_clip_rec->y; 938 ASSERT(mpc_slice_count == 1 || 939 stream->view_format != VIEW_3D_FORMAT_SIDE_BY_SIDE || 940 mpc_rec.width % 2 == 0); 941 942 /* extra pixels in the division remainder need to go to pipes after 943 * the extra pixel index minus one(epimo) defined here as: 944 */ 945 if (mpc_slice_idx > epimo) { 946 mpc_rec.x += mpc_slice_idx - epimo - 1; 947 mpc_rec.width += 1; 948 } 949 950 if (stream->view_format == VIEW_3D_FORMAT_TOP_AND_BOTTOM) { 951 ASSERT(mpc_rec.height % 2 == 0); 952 mpc_rec.height /= 2; 953 } 954 return mpc_rec; 955} 956 957static void adjust_recout_for_visual_confirm(struct rect *recout, 958 struct pipe_ctx *pipe_ctx) 959{ 960 struct dc *dc = pipe_ctx->stream->ctx->dc; 961 int dpp_offset, base_offset; 962 963 if (dc->debug.visual_confirm == VISUAL_CONFIRM_DISABLE || !pipe_ctx->plane_res.dpp) 964 return; 965 966 dpp_offset = pipe_ctx->stream->timing.v_addressable / VISUAL_CONFIRM_DPP_OFFSET_DENO; 967 dpp_offset *= pipe_ctx->plane_res.dpp->inst; 968 969 if ((dc->debug.visual_confirm_rect_height >= VISUAL_CONFIRM_BASE_MIN) && 970 dc->debug.visual_confirm_rect_height <= VISUAL_CONFIRM_BASE_MAX) 971 base_offset = dc->debug.visual_confirm_rect_height; 972 else 973 base_offset = VISUAL_CONFIRM_BASE_DEFAULT; 974 975 recout->height -= base_offset; 976 recout->height -= dpp_offset; 977} 978 979/* 980 * The function maps a plane clip from Stream Source Space to ODM Slice Space 981 * and calculates the rec of the overlapping area of MPC slice of the plane 982 * clip, ODM slice associated with the pipe context and stream destination rec. 983 */ 984static void calculate_recout(struct pipe_ctx *pipe_ctx) 985{ 986 /* 987 * A plane clip represents the desired plane size and position in Stream 988 * Source Space. Stream Source is the destination where all planes are 989 * blended (i.e. positioned, scaled and overlaid). It is a canvas where 990 * all planes associated with the current stream are drawn together. 991 * After Stream Source is completed, we will further scale and 992 * reposition the entire canvas of the stream source to Stream 993 * Destination in Timing Active Space. This could be due to display 994 * overscan adjustment where we will need to rescale and reposition all 995 * the planes so they can fit into a TV with overscan or downscale 996 * upscale features such as GPU scaling or VSR. 997 * 998 * This two step blending is a virtual procedure in software. In 999 * hardware there is no such thing as Stream Source. all planes are 1000 * blended once in Timing Active Space. Software virtualizes a Stream 1001 * Source space to decouple the math complicity so scaling param 1002 * calculation focuses on one step at a time. 1003 * 1004 * In the following two diagrams, user applied 10% overscan adjustment 1005 * so the Stream Source needs to be scaled down a little before mapping 1006 * to Timing Active Space. As a result the Plane Clip is also scaled 1007 * down by the same ratio, Plane Clip position (i.e. x and y) with 1008 * respect to Stream Source is also scaled down. To map it in Timing 1009 * Active Space additional x and y offsets from Stream Destination are 1010 * added to Plane Clip as well. 1011 * 1012 * Stream Source Space 1013 * ------------ 1014 * __________________________________________________ 1015 * |Stream Source (3840 x 2160) ^ | 1016 * | y | 1017 * | | | 1018 * | __________________V | 1019 * |<-- x -->|Plane Clip/////////| | 1020 * | |(pre scale)////////| | 1021 * | |///////////////////| | 1022 * | |///////////////////| | 1023 * | |///////////////////| | 1024 * | |///////////////////| | 1025 * | |///////////////////| | 1026 * | | 1027 * | | 1028 * |__________________________________________________| 1029 * 1030 * 1031 * Timing Active Space (3840 x 2160) 1032 * --------------------------------- 1033 * 1034 * Timing Active 1035 * __________________________________________________ 1036 * | y_____________________________________________ | 1037 * |x |Stream Destination (3456 x 1944) | | 1038 * | | | | 1039 * | | __________________ | | 1040 * | | |Plane Clip////////| | | 1041 * | | |(post scale)//////| | | 1042 * | | |//////////////////| | | 1043 * | | |//////////////////| | | 1044 * | | |//////////////////| | | 1045 * | | |//////////////////| | | 1046 * | | | | 1047 * | | | | 1048 * | |____________________________________________| | 1049 * |__________________________________________________| 1050 * 1051 * 1052 * In Timing Active Space a plane clip could be further sliced into 1053 * pieces called MPC slices. Each Pipe Context is responsible for 1054 * processing only one MPC slice so the plane processing workload can be 1055 * distributed to multiple DPP Pipes. MPC slices could be blended 1056 * together to a single ODM slice. Each ODM slice is responsible for 1057 * processing a portion of Timing Active divided horizontally so the 1058 * output pixel processing workload can be distributed to multiple OPP 1059 * pipes. All ODM slices are mapped together in ODM block so all MPC 1060 * slices belong to different ODM slices could be pieced together to 1061 * form a single image in Timing Active. MPC slices must belong to 1062 * single ODM slice. If an MPC slice goes across ODM slice boundary, it 1063 * needs to be divided into two MPC slices one for each ODM slice. 1064 * 1065 * In the following diagram the output pixel processing workload is 1066 * divided horizontally into two ODM slices one for each OPP blend tree. 1067 * OPP0 blend tree is responsible for processing left half of Timing 1068 * Active, while OPP2 blend tree is responsible for processing right 1069 * half. 1070 * 1071 * The plane has two MPC slices. However since the right MPC slice goes 1072 * across ODM boundary, two DPP pipes are needed one for each OPP blend 1073 * tree. (i.e. DPP1 for OPP0 blend tree and DPP2 for OPP2 blend tree). 1074 * 1075 * Assuming that we have a Pipe Context associated with OPP0 and DPP1 1076 * working on processing the plane in the diagram. We want to know the 1077 * width and height of the shaded rectangle and its relative position 1078 * with respect to the ODM slice0. This is called the recout of the pipe 1079 * context. 1080 * 1081 * Planes can be at arbitrary size and position and there could be an 1082 * arbitrary number of MPC and ODM slices. The algorithm needs to take 1083 * all scenarios into account. 1084 * 1085 * Timing Active Space (3840 x 2160) 1086 * --------------------------------- 1087 * 1088 * Timing Active 1089 * __________________________________________________ 1090 * |OPP0(ODM slice0)^ |OPP2(ODM slice1) | 1091 * | y | | 1092 * | | <- w -> | 1093 * | _____V________|____ | 1094 * | |DPP0 ^ |DPP1 |DPP2| | 1095 * |<------ x |-----|->|/////| | | 1096 * | | | |/////| | | 1097 * | | h |/////| | | 1098 * | | | |/////| | | 1099 * | |_____V__|/////|____| | 1100 * | | | 1101 * | | | 1102 * | | | 1103 * |_________________________|________________________| 1104 * 1105 * 1106 */ 1107 struct rect plane_clip; 1108 struct rect mpc_slice_of_plane_clip; 1109 struct rect odm_slice; 1110 struct rect overlapping_area; 1111 1112 plane_clip = calculate_plane_rec_in_timing_active(pipe_ctx, 1113 &pipe_ctx->plane_state->clip_rect); 1114 /* guard plane clip from drawing beyond stream dst here */ 1115 plane_clip = intersect_rec(&plane_clip, 1116 &pipe_ctx->stream->dst); 1117 mpc_slice_of_plane_clip = calculate_mpc_slice_in_timing_active( 1118 pipe_ctx, &plane_clip); 1119 odm_slice = calculate_odm_slice_in_timing_active(pipe_ctx); 1120 overlapping_area = intersect_rec(&mpc_slice_of_plane_clip, &odm_slice); 1121 if (overlapping_area.height > 0 && 1122 overlapping_area.width > 0) { 1123 /* shift the overlapping area so it is with respect to current 1124 * ODM slice's position 1125 */ 1126 pipe_ctx->plane_res.scl_data.recout = shift_rec( 1127 &overlapping_area, 1128 -odm_slice.x, -odm_slice.y); 1129 adjust_recout_for_visual_confirm( 1130 &pipe_ctx->plane_res.scl_data.recout, 1131 pipe_ctx); 1132 } else { 1133 /* if there is no overlap, zero recout */ 1134 memset(&pipe_ctx->plane_res.scl_data.recout, 0, 1135 sizeof(struct rect)); 1136 } 1137 1138} 1139 1140static void calculate_scaling_ratios(struct pipe_ctx *pipe_ctx) 1141{ 1142 const struct dc_plane_state *plane_state = pipe_ctx->plane_state; 1143 const struct dc_stream_state *stream = pipe_ctx->stream; 1144 struct rect surf_src = plane_state->src_rect; 1145 const int in_w = stream->src.width; 1146 const int in_h = stream->src.height; 1147 const int out_w = stream->dst.width; 1148 const int out_h = stream->dst.height; 1149 1150 /*Swap surf_src height and width since scaling ratios are in recout rotation*/ 1151 if (pipe_ctx->plane_state->rotation == ROTATION_ANGLE_90 || 1152 pipe_ctx->plane_state->rotation == ROTATION_ANGLE_270) 1153 swap(surf_src.height, surf_src.width); 1154 1155 pipe_ctx->plane_res.scl_data.ratios.horz = dc_fixpt_from_fraction( 1156 surf_src.width, 1157 plane_state->dst_rect.width); 1158 pipe_ctx->plane_res.scl_data.ratios.vert = dc_fixpt_from_fraction( 1159 surf_src.height, 1160 plane_state->dst_rect.height); 1161 1162 if (stream->view_format == VIEW_3D_FORMAT_SIDE_BY_SIDE) 1163 pipe_ctx->plane_res.scl_data.ratios.horz.value *= 2; 1164 else if (stream->view_format == VIEW_3D_FORMAT_TOP_AND_BOTTOM) 1165 pipe_ctx->plane_res.scl_data.ratios.vert.value *= 2; 1166 1167 pipe_ctx->plane_res.scl_data.ratios.vert.value = div64_s64( 1168 pipe_ctx->plane_res.scl_data.ratios.vert.value * in_h, out_h); 1169 pipe_ctx->plane_res.scl_data.ratios.horz.value = div64_s64( 1170 pipe_ctx->plane_res.scl_data.ratios.horz.value * in_w, out_w); 1171 1172 pipe_ctx->plane_res.scl_data.ratios.horz_c = pipe_ctx->plane_res.scl_data.ratios.horz; 1173 pipe_ctx->plane_res.scl_data.ratios.vert_c = pipe_ctx->plane_res.scl_data.ratios.vert; 1174 1175 if (pipe_ctx->plane_res.scl_data.format == PIXEL_FORMAT_420BPP8 1176 || pipe_ctx->plane_res.scl_data.format == PIXEL_FORMAT_420BPP10) { 1177 pipe_ctx->plane_res.scl_data.ratios.horz_c.value /= 2; 1178 pipe_ctx->plane_res.scl_data.ratios.vert_c.value /= 2; 1179 } 1180 pipe_ctx->plane_res.scl_data.ratios.horz = dc_fixpt_truncate( 1181 pipe_ctx->plane_res.scl_data.ratios.horz, 19); 1182 pipe_ctx->plane_res.scl_data.ratios.vert = dc_fixpt_truncate( 1183 pipe_ctx->plane_res.scl_data.ratios.vert, 19); 1184 pipe_ctx->plane_res.scl_data.ratios.horz_c = dc_fixpt_truncate( 1185 pipe_ctx->plane_res.scl_data.ratios.horz_c, 19); 1186 pipe_ctx->plane_res.scl_data.ratios.vert_c = dc_fixpt_truncate( 1187 pipe_ctx->plane_res.scl_data.ratios.vert_c, 19); 1188} 1189 1190 1191/* 1192 * We completely calculate vp offset, size and inits here based entirely on scaling 1193 * ratios and recout for pixel perfect pipe combine. 1194 */ 1195static void calculate_init_and_vp( 1196 bool flip_scan_dir, 1197 int recout_offset_within_recout_full, 1198 int recout_size, 1199 int src_size, 1200 int taps, 1201 struct fixed31_32 ratio, 1202 struct fixed31_32 *init, 1203 int *vp_offset, 1204 int *vp_size) 1205{ 1206 struct fixed31_32 temp; 1207 int int_part; 1208 1209 /* 1210 * First of the taps starts sampling pixel number <init_int_part> corresponding to recout 1211 * pixel 1. Next recout pixel samples int part of <init + scaling ratio> and so on. 1212 * All following calculations are based on this logic. 1213 * 1214 * Init calculated according to formula: 1215 * init = (scaling_ratio + number_of_taps + 1) / 2 1216 * init_bot = init + scaling_ratio 1217 * to get pixel perfect combine add the fraction from calculating vp offset 1218 */ 1219 temp = dc_fixpt_mul_int(ratio, recout_offset_within_recout_full); 1220 *vp_offset = dc_fixpt_floor(temp); 1221 temp.value &= 0xffffffff; 1222 *init = dc_fixpt_truncate(dc_fixpt_add(dc_fixpt_div_int( 1223 dc_fixpt_add_int(ratio, taps + 1), 2), temp), 19); 1224 /* 1225 * If viewport has non 0 offset and there are more taps than covered by init then 1226 * we should decrease the offset and increase init so we are never sampling 1227 * outside of viewport. 1228 */ 1229 int_part = dc_fixpt_floor(*init); 1230 if (int_part < taps) { 1231 int_part = taps - int_part; 1232 if (int_part > *vp_offset) 1233 int_part = *vp_offset; 1234 *vp_offset -= int_part; 1235 *init = dc_fixpt_add_int(*init, int_part); 1236 } 1237 /* 1238 * If taps are sampling outside of viewport at end of recout and there are more pixels 1239 * available in the surface we should increase the viewport size, regardless set vp to 1240 * only what is used. 1241 */ 1242 temp = dc_fixpt_add(*init, dc_fixpt_mul_int(ratio, recout_size - 1)); 1243 *vp_size = dc_fixpt_floor(temp); 1244 if (*vp_size + *vp_offset > src_size) 1245 *vp_size = src_size - *vp_offset; 1246 1247 /* We did all the math assuming we are scanning same direction as display does, 1248 * however mirror/rotation changes how vp scans vs how it is offset. If scan direction 1249 * is flipped we simply need to calculate offset from the other side of plane. 1250 * Note that outside of viewport all scaling hardware works in recout space. 1251 */ 1252 if (flip_scan_dir) 1253 *vp_offset = src_size - *vp_offset - *vp_size; 1254} 1255 1256static void calculate_inits_and_viewports(struct pipe_ctx *pipe_ctx) 1257{ 1258 const struct dc_plane_state *plane_state = pipe_ctx->plane_state; 1259 struct scaler_data *data = &pipe_ctx->plane_res.scl_data; 1260 struct rect src = plane_state->src_rect; 1261 struct rect recout_dst_in_active_timing; 1262 struct rect recout_clip_in_active_timing; 1263 struct rect recout_clip_in_recout_dst; 1264 struct rect overlap_in_active_timing; 1265 struct rect odm_slice = calculate_odm_slice_in_timing_active(pipe_ctx); 1266 int vpc_div = (data->format == PIXEL_FORMAT_420BPP8 1267 || data->format == PIXEL_FORMAT_420BPP10) ? 2 : 1; 1268 bool orthogonal_rotation, flip_vert_scan_dir, flip_horz_scan_dir; 1269 1270 recout_clip_in_active_timing = shift_rec( 1271 &data->recout, odm_slice.x, odm_slice.y); 1272 recout_dst_in_active_timing = calculate_plane_rec_in_timing_active( 1273 pipe_ctx, &plane_state->dst_rect); 1274 overlap_in_active_timing = intersect_rec(&recout_clip_in_active_timing, 1275 &recout_dst_in_active_timing); 1276 if (overlap_in_active_timing.width > 0 && 1277 overlap_in_active_timing.height > 0) 1278 recout_clip_in_recout_dst = shift_rec(&overlap_in_active_timing, 1279 -recout_dst_in_active_timing.x, 1280 -recout_dst_in_active_timing.y); 1281 else 1282 memset(&recout_clip_in_recout_dst, 0, sizeof(struct rect)); 1283 1284 /* 1285 * Work in recout rotation since that requires less transformations 1286 */ 1287 get_vp_scan_direction( 1288 plane_state->rotation, 1289 plane_state->horizontal_mirror, 1290 &orthogonal_rotation, 1291 &flip_vert_scan_dir, 1292 &flip_horz_scan_dir); 1293 1294 if (orthogonal_rotation) { 1295 swap(src.width, src.height); 1296 swap(flip_vert_scan_dir, flip_horz_scan_dir); 1297 } 1298 1299 calculate_init_and_vp( 1300 flip_horz_scan_dir, 1301 recout_clip_in_recout_dst.x, 1302 data->recout.width, 1303 src.width, 1304 data->taps.h_taps, 1305 data->ratios.horz, 1306 &data->inits.h, 1307 &data->viewport.x, 1308 &data->viewport.width); 1309 calculate_init_and_vp( 1310 flip_horz_scan_dir, 1311 recout_clip_in_recout_dst.x, 1312 data->recout.width, 1313 src.width / vpc_div, 1314 data->taps.h_taps_c, 1315 data->ratios.horz_c, 1316 &data->inits.h_c, 1317 &data->viewport_c.x, 1318 &data->viewport_c.width); 1319 calculate_init_and_vp( 1320 flip_vert_scan_dir, 1321 recout_clip_in_recout_dst.y, 1322 data->recout.height, 1323 src.height, 1324 data->taps.v_taps, 1325 data->ratios.vert, 1326 &data->inits.v, 1327 &data->viewport.y, 1328 &data->viewport.height); 1329 calculate_init_and_vp( 1330 flip_vert_scan_dir, 1331 recout_clip_in_recout_dst.y, 1332 data->recout.height, 1333 src.height / vpc_div, 1334 data->taps.v_taps_c, 1335 data->ratios.vert_c, 1336 &data->inits.v_c, 1337 &data->viewport_c.y, 1338 &data->viewport_c.height); 1339 if (orthogonal_rotation) { 1340 swap(data->viewport.x, data->viewport.y); 1341 swap(data->viewport.width, data->viewport.height); 1342 swap(data->viewport_c.x, data->viewport_c.y); 1343 swap(data->viewport_c.width, data->viewport_c.height); 1344 } 1345 data->viewport.x += src.x; 1346 data->viewport.y += src.y; 1347 ASSERT(src.x % vpc_div == 0 && src.y % vpc_div == 0); 1348 data->viewport_c.x += src.x / vpc_div; 1349 data->viewport_c.y += src.y / vpc_div; 1350} 1351 1352static bool is_subvp_high_refresh_candidate(struct dc_stream_state *stream) 1353{ 1354 uint32_t refresh_rate; 1355 struct dc *dc = stream->ctx->dc; 1356 1357 refresh_rate = (stream->timing.pix_clk_100hz * (uint64_t)100 + 1358 stream->timing.v_total * stream->timing.h_total - (uint64_t)1); 1359 refresh_rate = div_u64(refresh_rate, stream->timing.v_total); 1360 refresh_rate = div_u64(refresh_rate, stream->timing.h_total); 1361 1362 /* If there's any stream that fits the SubVP high refresh criteria, 1363 * we must return true. This is because cursor updates are asynchronous 1364 * with full updates, so we could transition into a SubVP config and 1365 * remain in HW cursor mode if there's no cursor update which will 1366 * then cause corruption. 1367 */ 1368 if ((refresh_rate >= 120 && refresh_rate <= 175 && 1369 stream->timing.v_addressable >= 1080 && 1370 stream->timing.v_addressable <= 2160) && 1371 (dc->current_state->stream_count > 1 || 1372 (dc->current_state->stream_count == 1 && !stream->allow_freesync))) 1373 return true; 1374 1375 return false; 1376} 1377 1378static enum controller_dp_test_pattern convert_dp_to_controller_test_pattern( 1379 enum dp_test_pattern test_pattern) 1380{ 1381 enum controller_dp_test_pattern controller_test_pattern; 1382 1383 switch (test_pattern) { 1384 case DP_TEST_PATTERN_COLOR_SQUARES: 1385 controller_test_pattern = 1386 CONTROLLER_DP_TEST_PATTERN_COLORSQUARES; 1387 break; 1388 case DP_TEST_PATTERN_COLOR_SQUARES_CEA: 1389 controller_test_pattern = 1390 CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA; 1391 break; 1392 case DP_TEST_PATTERN_VERTICAL_BARS: 1393 controller_test_pattern = 1394 CONTROLLER_DP_TEST_PATTERN_VERTICALBARS; 1395 break; 1396 case DP_TEST_PATTERN_HORIZONTAL_BARS: 1397 controller_test_pattern = 1398 CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS; 1399 break; 1400 case DP_TEST_PATTERN_COLOR_RAMP: 1401 controller_test_pattern = 1402 CONTROLLER_DP_TEST_PATTERN_COLORRAMP; 1403 break; 1404 default: 1405 controller_test_pattern = 1406 CONTROLLER_DP_TEST_PATTERN_VIDEOMODE; 1407 break; 1408 } 1409 1410 return controller_test_pattern; 1411} 1412 1413static enum controller_dp_color_space convert_dp_to_controller_color_space( 1414 enum dp_test_pattern_color_space color_space) 1415{ 1416 enum controller_dp_color_space controller_color_space; 1417 1418 switch (color_space) { 1419 case DP_TEST_PATTERN_COLOR_SPACE_RGB: 1420 controller_color_space = CONTROLLER_DP_COLOR_SPACE_RGB; 1421 break; 1422 case DP_TEST_PATTERN_COLOR_SPACE_YCBCR601: 1423 controller_color_space = CONTROLLER_DP_COLOR_SPACE_YCBCR601; 1424 break; 1425 case DP_TEST_PATTERN_COLOR_SPACE_YCBCR709: 1426 controller_color_space = CONTROLLER_DP_COLOR_SPACE_YCBCR709; 1427 break; 1428 case DP_TEST_PATTERN_COLOR_SPACE_UNDEFINED: 1429 default: 1430 controller_color_space = CONTROLLER_DP_COLOR_SPACE_UDEFINED; 1431 break; 1432 } 1433 1434 return controller_color_space; 1435} 1436 1437void resource_build_test_pattern_params(struct resource_context *res_ctx, 1438 struct pipe_ctx *otg_master) 1439{ 1440 int odm_slice_width, last_odm_slice_width, offset = 0; 1441 struct pipe_ctx *opp_heads[MAX_PIPES]; 1442 struct test_pattern_params *params; 1443 int odm_cnt = 1; 1444 enum controller_dp_test_pattern controller_test_pattern; 1445 enum controller_dp_color_space controller_color_space; 1446 enum dc_color_depth color_depth = otg_master->stream->timing.display_color_depth; 1447 int h_active = otg_master->stream->timing.h_addressable + 1448 otg_master->stream->timing.h_border_left + 1449 otg_master->stream->timing.h_border_right; 1450 int v_active = otg_master->stream->timing.v_addressable + 1451 otg_master->stream->timing.v_border_bottom + 1452 otg_master->stream->timing.v_border_top; 1453 int i; 1454 1455 controller_test_pattern = convert_dp_to_controller_test_pattern( 1456 otg_master->stream->test_pattern.type); 1457 controller_color_space = convert_dp_to_controller_color_space( 1458 otg_master->stream->test_pattern.color_space); 1459 1460 if (controller_test_pattern == CONTROLLER_DP_TEST_PATTERN_VIDEOMODE) 1461 return; 1462 1463 odm_cnt = resource_get_opp_heads_for_otg_master(otg_master, res_ctx, opp_heads); 1464 1465 odm_slice_width = h_active / odm_cnt; 1466 last_odm_slice_width = h_active - odm_slice_width * (odm_cnt - 1); 1467 1468 for (i = 0; i < odm_cnt; i++) { 1469 params = &opp_heads[i]->stream_res.test_pattern_params; 1470 params->test_pattern = controller_test_pattern; 1471 params->color_space = controller_color_space; 1472 params->color_depth = color_depth; 1473 params->height = v_active; 1474 params->offset = offset; 1475 1476 if (i < odm_cnt - 1) 1477 params->width = odm_slice_width; 1478 else 1479 params->width = last_odm_slice_width; 1480 1481 offset += odm_slice_width; 1482 } 1483} 1484 1485bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx) 1486{ 1487 const struct dc_plane_state *plane_state = pipe_ctx->plane_state; 1488 struct dc_crtc_timing *timing = &pipe_ctx->stream->timing; 1489 const struct rect odm_slice_rec = calculate_odm_slice_in_timing_active(pipe_ctx); 1490 bool res = false; 1491 1492 DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); 1493 1494 /* Invalid input */ 1495 if (!plane_state->dst_rect.width || 1496 !plane_state->dst_rect.height || 1497 !plane_state->src_rect.width || 1498 !plane_state->src_rect.height) { 1499 ASSERT(0); 1500 return false; 1501 } 1502 1503 /* Timing borders are part of vactive that we are also supposed to skip in addition 1504 * to any stream dst offset. Since dm logic assumes dst is in addressable 1505 * space we need to add the left and top borders to dst offsets temporarily. 1506 * TODO: fix in DM, stream dst is supposed to be in vactive 1507 */ 1508 pipe_ctx->stream->dst.x += timing->h_border_left; 1509 pipe_ctx->stream->dst.y += timing->v_border_top; 1510 1511 /* Calculate H and V active size */ 1512 pipe_ctx->plane_res.scl_data.h_active = odm_slice_rec.width; 1513 pipe_ctx->plane_res.scl_data.v_active = odm_slice_rec.height; 1514 pipe_ctx->plane_res.scl_data.format = convert_pixel_format_to_dalsurface( 1515 pipe_ctx->plane_state->format); 1516 1517 /* depends on h_active */ 1518 calculate_recout(pipe_ctx); 1519 /* depends on pixel format */ 1520 calculate_scaling_ratios(pipe_ctx); 1521 /* depends on scaling ratios and recout, does not calculate offset yet */ 1522 calculate_viewport_size(pipe_ctx); 1523 1524 /* 1525 * LB calculations depend on vp size, h/v_active and scaling ratios 1526 * Setting line buffer pixel depth to 24bpp yields banding 1527 * on certain displays, such as the Sharp 4k. 36bpp is needed 1528 * to support SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616 and 1529 * SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616 with actual > 10 bpc 1530 * precision on DCN display engines, but apparently not for DCE, as 1531 * far as testing on DCE-11.2 and DCE-8 showed. Various DCE parts have 1532 * problems: Carrizo with DCE_VERSION_11_0 does not like 36 bpp lb depth, 1533 * neither do DCE-8 at 4k resolution, or DCE-11.2 (broken identify pixel 1534 * passthrough). Therefore only use 36 bpp on DCN where it is actually needed. 1535 */ 1536 if (plane_state->ctx->dce_version > DCE_VERSION_MAX) 1537 pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_36BPP; 1538 else 1539 pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_30BPP; 1540 1541 pipe_ctx->plane_res.scl_data.lb_params.alpha_en = plane_state->per_pixel_alpha; 1542 1543 if (pipe_ctx->plane_res.xfm != NULL) 1544 res = pipe_ctx->plane_res.xfm->funcs->transform_get_optimal_number_of_taps( 1545 pipe_ctx->plane_res.xfm, &pipe_ctx->plane_res.scl_data, &plane_state->scaling_quality); 1546 1547 if (pipe_ctx->plane_res.dpp != NULL) 1548 res = pipe_ctx->plane_res.dpp->funcs->dpp_get_optimal_number_of_taps( 1549 pipe_ctx->plane_res.dpp, &pipe_ctx->plane_res.scl_data, &plane_state->scaling_quality); 1550 1551 1552 if (!res) { 1553 /* Try 24 bpp linebuffer */ 1554 pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_24BPP; 1555 1556 if (pipe_ctx->plane_res.xfm != NULL) 1557 res = pipe_ctx->plane_res.xfm->funcs->transform_get_optimal_number_of_taps( 1558 pipe_ctx->plane_res.xfm, 1559 &pipe_ctx->plane_res.scl_data, 1560 &plane_state->scaling_quality); 1561 1562 if (pipe_ctx->plane_res.dpp != NULL) 1563 res = pipe_ctx->plane_res.dpp->funcs->dpp_get_optimal_number_of_taps( 1564 pipe_ctx->plane_res.dpp, 1565 &pipe_ctx->plane_res.scl_data, 1566 &plane_state->scaling_quality); 1567 } 1568 1569 /* 1570 * Depends on recout, scaling ratios, h_active and taps 1571 * May need to re-check lb size after this in some obscure scenario 1572 */ 1573 if (res) 1574 calculate_inits_and_viewports(pipe_ctx); 1575 1576 /* 1577 * Handle side by side and top bottom 3d recout offsets after vp calculation 1578 * since 3d is special and needs to calculate vp as if there is no recout offset 1579 * This may break with rotation, good thing we aren't mixing hw rotation and 3d 1580 */ 1581 if (pipe_ctx->top_pipe && pipe_ctx->top_pipe->plane_state == plane_state) { 1582 ASSERT(plane_state->rotation == ROTATION_ANGLE_0 || 1583 (pipe_ctx->stream->view_format != VIEW_3D_FORMAT_TOP_AND_BOTTOM && 1584 pipe_ctx->stream->view_format != VIEW_3D_FORMAT_SIDE_BY_SIDE)); 1585 if (pipe_ctx->stream->view_format == VIEW_3D_FORMAT_TOP_AND_BOTTOM) 1586 pipe_ctx->plane_res.scl_data.recout.y += pipe_ctx->plane_res.scl_data.recout.height; 1587 else if (pipe_ctx->stream->view_format == VIEW_3D_FORMAT_SIDE_BY_SIDE) 1588 pipe_ctx->plane_res.scl_data.recout.x += pipe_ctx->plane_res.scl_data.recout.width; 1589 } 1590 1591 /* Clamp minimum viewport size */ 1592 if (pipe_ctx->plane_res.scl_data.viewport.height < MIN_VIEWPORT_SIZE) 1593 pipe_ctx->plane_res.scl_data.viewport.height = MIN_VIEWPORT_SIZE; 1594 if (pipe_ctx->plane_res.scl_data.viewport.width < MIN_VIEWPORT_SIZE) 1595 pipe_ctx->plane_res.scl_data.viewport.width = MIN_VIEWPORT_SIZE; 1596 1597 1598 DC_LOG_SCALER("%s pipe %d:\nViewport: height:%d width:%d x:%d y:%d Recout: height:%d width:%d x:%d y:%d HACTIVE:%d VACTIVE:%d\n" 1599 "src_rect: height:%d width:%d x:%d y:%d dst_rect: height:%d width:%d x:%d y:%d clip_rect: height:%d width:%d x:%d y:%d\n", 1600 __func__, 1601 pipe_ctx->pipe_idx, 1602 pipe_ctx->plane_res.scl_data.viewport.height, 1603 pipe_ctx->plane_res.scl_data.viewport.width, 1604 pipe_ctx->plane_res.scl_data.viewport.x, 1605 pipe_ctx->plane_res.scl_data.viewport.y, 1606 pipe_ctx->plane_res.scl_data.recout.height, 1607 pipe_ctx->plane_res.scl_data.recout.width, 1608 pipe_ctx->plane_res.scl_data.recout.x, 1609 pipe_ctx->plane_res.scl_data.recout.y, 1610 pipe_ctx->plane_res.scl_data.h_active, 1611 pipe_ctx->plane_res.scl_data.v_active, 1612 plane_state->src_rect.height, 1613 plane_state->src_rect.width, 1614 plane_state->src_rect.x, 1615 plane_state->src_rect.y, 1616 plane_state->dst_rect.height, 1617 plane_state->dst_rect.width, 1618 plane_state->dst_rect.x, 1619 plane_state->dst_rect.y, 1620 plane_state->clip_rect.height, 1621 plane_state->clip_rect.width, 1622 plane_state->clip_rect.x, 1623 plane_state->clip_rect.y); 1624 1625 pipe_ctx->stream->dst.x -= timing->h_border_left; 1626 pipe_ctx->stream->dst.y -= timing->v_border_top; 1627 1628 return res; 1629} 1630 1631 1632enum dc_status resource_build_scaling_params_for_context( 1633 const struct dc *dc, 1634 struct dc_state *context) 1635{ 1636 int i; 1637 1638 for (i = 0; i < MAX_PIPES; i++) { 1639 if (context->res_ctx.pipe_ctx[i].plane_state != NULL && 1640 context->res_ctx.pipe_ctx[i].stream != NULL) 1641 if (!resource_build_scaling_params(&context->res_ctx.pipe_ctx[i])) 1642 return DC_FAIL_SCALING; 1643 } 1644 1645 return DC_OK; 1646} 1647 1648struct pipe_ctx *resource_find_free_secondary_pipe_legacy( 1649 struct resource_context *res_ctx, 1650 const struct resource_pool *pool, 1651 const struct pipe_ctx *primary_pipe) 1652{ 1653 int i; 1654 struct pipe_ctx *secondary_pipe = NULL; 1655 1656 /* 1657 * We add a preferred pipe mapping to avoid the chance that 1658 * MPCCs already in use will need to be reassigned to other trees. 1659 * For example, if we went with the strict, assign backwards logic: 1660 * 1661 * (State 1) 1662 * Display A on, no surface, top pipe = 0 1663 * Display B on, no surface, top pipe = 1 1664 * 1665 * (State 2) 1666 * Display A on, no surface, top pipe = 0 1667 * Display B on, surface enable, top pipe = 1, bottom pipe = 5 1668 * 1669 * (State 3) 1670 * Display A on, surface enable, top pipe = 0, bottom pipe = 5 1671 * Display B on, surface enable, top pipe = 1, bottom pipe = 4 1672 * 1673 * The state 2->3 transition requires remapping MPCC 5 from display B 1674 * to display A. 1675 * 1676 * However, with the preferred pipe logic, state 2 would look like: 1677 * 1678 * (State 2) 1679 * Display A on, no surface, top pipe = 0 1680 * Display B on, surface enable, top pipe = 1, bottom pipe = 4 1681 * 1682 * This would then cause 2->3 to not require remapping any MPCCs. 1683 */ 1684 if (primary_pipe) { 1685 int preferred_pipe_idx = (pool->pipe_count - 1) - primary_pipe->pipe_idx; 1686 if (res_ctx->pipe_ctx[preferred_pipe_idx].stream == NULL) { 1687 secondary_pipe = &res_ctx->pipe_ctx[preferred_pipe_idx]; 1688 secondary_pipe->pipe_idx = preferred_pipe_idx; 1689 } 1690 } 1691 1692 /* 1693 * search backwards for the second pipe to keep pipe 1694 * assignment more consistent 1695 */ 1696 if (!secondary_pipe) 1697 for (i = pool->pipe_count - 1; i >= 0; i--) { 1698 if (res_ctx->pipe_ctx[i].stream == NULL) { 1699 secondary_pipe = &res_ctx->pipe_ctx[i]; 1700 secondary_pipe->pipe_idx = i; 1701 break; 1702 } 1703 } 1704 1705 return secondary_pipe; 1706} 1707 1708int resource_find_free_pipe_used_as_sec_opp_head_by_cur_otg_master( 1709 const struct resource_context *cur_res_ctx, 1710 struct resource_context *new_res_ctx, 1711 const struct pipe_ctx *cur_otg_master) 1712{ 1713 const struct pipe_ctx *cur_sec_opp_head = cur_otg_master->next_odm_pipe; 1714 struct pipe_ctx *new_pipe; 1715 int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND; 1716 1717 while (cur_sec_opp_head) { 1718 new_pipe = &new_res_ctx->pipe_ctx[cur_sec_opp_head->pipe_idx]; 1719 if (resource_is_pipe_type(new_pipe, FREE_PIPE)) { 1720 free_pipe_idx = cur_sec_opp_head->pipe_idx; 1721 break; 1722 } 1723 cur_sec_opp_head = cur_sec_opp_head->next_odm_pipe; 1724 } 1725 1726 return free_pipe_idx; 1727} 1728 1729int resource_find_free_pipe_used_in_cur_mpc_blending_tree( 1730 const struct resource_context *cur_res_ctx, 1731 struct resource_context *new_res_ctx, 1732 const struct pipe_ctx *cur_opp_head) 1733{ 1734 const struct pipe_ctx *cur_sec_dpp = cur_opp_head->bottom_pipe; 1735 struct pipe_ctx *new_pipe; 1736 int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND; 1737 1738 while (cur_sec_dpp) { 1739 /* find a free pipe used in current opp blend tree, 1740 * this is to avoid MPO pipe switching to different opp blending 1741 * tree 1742 */ 1743 new_pipe = &new_res_ctx->pipe_ctx[cur_sec_dpp->pipe_idx]; 1744 if (resource_is_pipe_type(new_pipe, FREE_PIPE)) { 1745 free_pipe_idx = cur_sec_dpp->pipe_idx; 1746 break; 1747 } 1748 cur_sec_dpp = cur_sec_dpp->bottom_pipe; 1749 } 1750 1751 return free_pipe_idx; 1752} 1753 1754int recource_find_free_pipe_not_used_in_cur_res_ctx( 1755 const struct resource_context *cur_res_ctx, 1756 struct resource_context *new_res_ctx, 1757 const struct resource_pool *pool) 1758{ 1759 int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND; 1760 const struct pipe_ctx *new_pipe, *cur_pipe; 1761 int i; 1762 1763 for (i = 0; i < pool->pipe_count; i++) { 1764 cur_pipe = &cur_res_ctx->pipe_ctx[i]; 1765 new_pipe = &new_res_ctx->pipe_ctx[i]; 1766 1767 if (resource_is_pipe_type(cur_pipe, FREE_PIPE) && 1768 resource_is_pipe_type(new_pipe, FREE_PIPE)) { 1769 free_pipe_idx = i; 1770 break; 1771 } 1772 } 1773 1774 return free_pipe_idx; 1775} 1776 1777int recource_find_free_pipe_used_as_otg_master_in_cur_res_ctx( 1778 const struct resource_context *cur_res_ctx, 1779 struct resource_context *new_res_ctx, 1780 const struct resource_pool *pool) 1781{ 1782 int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND; 1783 const struct pipe_ctx *new_pipe, *cur_pipe; 1784 int i; 1785 1786 for (i = 0; i < pool->pipe_count; i++) { 1787 cur_pipe = &cur_res_ctx->pipe_ctx[i]; 1788 new_pipe = &new_res_ctx->pipe_ctx[i]; 1789 1790 if (resource_is_pipe_type(cur_pipe, OTG_MASTER) && 1791 resource_is_pipe_type(new_pipe, FREE_PIPE)) { 1792 free_pipe_idx = i; 1793 break; 1794 } 1795 } 1796 1797 return free_pipe_idx; 1798} 1799 1800int resource_find_free_pipe_used_as_cur_sec_dpp( 1801 const struct resource_context *cur_res_ctx, 1802 struct resource_context *new_res_ctx, 1803 const struct resource_pool *pool) 1804{ 1805 int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND; 1806 const struct pipe_ctx *new_pipe, *cur_pipe; 1807 int i; 1808 1809 for (i = 0; i < pool->pipe_count; i++) { 1810 cur_pipe = &cur_res_ctx->pipe_ctx[i]; 1811 new_pipe = &new_res_ctx->pipe_ctx[i]; 1812 1813 if (resource_is_pipe_type(cur_pipe, DPP_PIPE) && 1814 !resource_is_pipe_type(cur_pipe, OPP_HEAD) && 1815 resource_is_pipe_type(new_pipe, FREE_PIPE)) { 1816 free_pipe_idx = i; 1817 break; 1818 } 1819 } 1820 1821 return free_pipe_idx; 1822} 1823 1824int resource_find_free_pipe_used_as_cur_sec_dpp_in_mpcc_combine( 1825 const struct resource_context *cur_res_ctx, 1826 struct resource_context *new_res_ctx, 1827 const struct resource_pool *pool) 1828{ 1829 int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND; 1830 const struct pipe_ctx *new_pipe, *cur_pipe; 1831 int i; 1832 1833 for (i = 0; i < pool->pipe_count; i++) { 1834 cur_pipe = &cur_res_ctx->pipe_ctx[i]; 1835 new_pipe = &new_res_ctx->pipe_ctx[i]; 1836 1837 if (resource_is_pipe_type(cur_pipe, DPP_PIPE) && 1838 !resource_is_pipe_type(cur_pipe, OPP_HEAD) && 1839 resource_get_mpc_slice_index(cur_pipe) > 0 && 1840 resource_is_pipe_type(new_pipe, FREE_PIPE)) { 1841 free_pipe_idx = i; 1842 break; 1843 } 1844 } 1845 1846 return free_pipe_idx; 1847} 1848 1849int resource_find_any_free_pipe(struct resource_context *new_res_ctx, 1850 const struct resource_pool *pool) 1851{ 1852 int free_pipe_idx = FREE_PIPE_INDEX_NOT_FOUND; 1853 const struct pipe_ctx *new_pipe; 1854 int i; 1855 1856 for (i = 0; i < pool->pipe_count; i++) { 1857 new_pipe = &new_res_ctx->pipe_ctx[i]; 1858 1859 if (resource_is_pipe_type(new_pipe, FREE_PIPE)) { 1860 free_pipe_idx = i; 1861 break; 1862 } 1863 } 1864 1865 return free_pipe_idx; 1866} 1867 1868bool resource_is_pipe_type(const struct pipe_ctx *pipe_ctx, enum pipe_type type) 1869{ 1870 switch (type) { 1871 case OTG_MASTER: 1872 return !pipe_ctx->prev_odm_pipe && 1873 !pipe_ctx->top_pipe && 1874 pipe_ctx->stream; 1875 case OPP_HEAD: 1876 return !pipe_ctx->top_pipe && pipe_ctx->stream; 1877 case DPP_PIPE: 1878 return pipe_ctx->plane_state && pipe_ctx->stream; 1879 case FREE_PIPE: 1880 return !pipe_ctx->plane_state && !pipe_ctx->stream; 1881 default: 1882 return false; 1883 } 1884} 1885 1886struct pipe_ctx *resource_get_otg_master_for_stream( 1887 struct resource_context *res_ctx, 1888 const struct dc_stream_state *stream) 1889{ 1890 int i; 1891 1892 for (i = 0; i < MAX_PIPES; i++) { 1893 if (res_ctx->pipe_ctx[i].stream == stream && 1894 resource_is_pipe_type(&res_ctx->pipe_ctx[i], OTG_MASTER)) 1895 return &res_ctx->pipe_ctx[i]; 1896 } 1897 return NULL; 1898} 1899 1900int resource_get_opp_heads_for_otg_master(const struct pipe_ctx *otg_master, 1901 struct resource_context *res_ctx, 1902 struct pipe_ctx *opp_heads[MAX_PIPES]) 1903{ 1904 struct pipe_ctx *opp_head = &res_ctx->pipe_ctx[otg_master->pipe_idx]; 1905 int i = 0; 1906 1907 if (!resource_is_pipe_type(otg_master, OTG_MASTER)) { 1908 ASSERT(0); 1909 return 0; 1910 } 1911 while (opp_head) { 1912 ASSERT(i < MAX_PIPES); 1913 opp_heads[i++] = opp_head; 1914 opp_head = opp_head->next_odm_pipe; 1915 } 1916 return i; 1917} 1918 1919int resource_get_dpp_pipes_for_opp_head(const struct pipe_ctx *opp_head, 1920 struct resource_context *res_ctx, 1921 struct pipe_ctx *dpp_pipes[MAX_PIPES]) 1922{ 1923 struct pipe_ctx *pipe = &res_ctx->pipe_ctx[opp_head->pipe_idx]; 1924 int i = 0; 1925 1926 if (!resource_is_pipe_type(opp_head, OPP_HEAD)) { 1927 ASSERT(0); 1928 return 0; 1929 } 1930 while (pipe && resource_is_pipe_type(pipe, DPP_PIPE)) { 1931 ASSERT(i < MAX_PIPES); 1932 dpp_pipes[i++] = pipe; 1933 pipe = pipe->bottom_pipe; 1934 } 1935 return i; 1936} 1937 1938int resource_get_dpp_pipes_for_plane(const struct dc_plane_state *plane, 1939 struct resource_context *res_ctx, 1940 struct pipe_ctx *dpp_pipes[MAX_PIPES]) 1941{ 1942 int i = 0, j; 1943 struct pipe_ctx *pipe; 1944 1945 for (j = 0; j < MAX_PIPES; j++) { 1946 pipe = &res_ctx->pipe_ctx[j]; 1947 if (pipe->plane_state == plane && pipe->prev_odm_pipe == NULL) { 1948 if (resource_is_pipe_type(pipe, OPP_HEAD) || 1949 pipe->top_pipe->plane_state != plane) 1950 break; 1951 } 1952 } 1953 1954 if (j < MAX_PIPES) { 1955 if (pipe->next_odm_pipe) 1956 while (pipe) { 1957 dpp_pipes[i++] = pipe; 1958 pipe = pipe->next_odm_pipe; 1959 } 1960 else 1961 while (pipe && pipe->plane_state == plane) { 1962 dpp_pipes[i++] = pipe; 1963 pipe = pipe->bottom_pipe; 1964 } 1965 } 1966 return i; 1967} 1968 1969struct pipe_ctx *resource_get_otg_master(const struct pipe_ctx *pipe_ctx) 1970{ 1971 struct pipe_ctx *otg_master = resource_get_opp_head(pipe_ctx); 1972 1973 while (otg_master->prev_odm_pipe) 1974 otg_master = otg_master->prev_odm_pipe; 1975 return otg_master; 1976} 1977 1978struct pipe_ctx *resource_get_opp_head(const struct pipe_ctx *pipe_ctx) 1979{ 1980 struct pipe_ctx *opp_head = (struct pipe_ctx *) pipe_ctx; 1981 1982 ASSERT(!resource_is_pipe_type(opp_head, FREE_PIPE)); 1983 while (opp_head->top_pipe) 1984 opp_head = opp_head->top_pipe; 1985 return opp_head; 1986} 1987 1988struct pipe_ctx *resource_get_primary_dpp_pipe(const struct pipe_ctx *dpp_pipe) 1989{ 1990 struct pipe_ctx *pri_dpp_pipe = (struct pipe_ctx *) dpp_pipe; 1991 1992 ASSERT(resource_is_pipe_type(dpp_pipe, DPP_PIPE)); 1993 while (pri_dpp_pipe->prev_odm_pipe) 1994 pri_dpp_pipe = pri_dpp_pipe->prev_odm_pipe; 1995 while (pri_dpp_pipe->top_pipe && 1996 pri_dpp_pipe->top_pipe->plane_state == pri_dpp_pipe->plane_state) 1997 pri_dpp_pipe = pri_dpp_pipe->top_pipe; 1998 return pri_dpp_pipe; 1999} 2000 2001 2002int resource_get_mpc_slice_index(const struct pipe_ctx *pipe_ctx) 2003{ 2004 struct pipe_ctx *split_pipe = pipe_ctx->top_pipe; 2005 int index = 0; 2006 2007 while (split_pipe && split_pipe->plane_state == pipe_ctx->plane_state) { 2008 index++; 2009 split_pipe = split_pipe->top_pipe; 2010 } 2011 2012 return index; 2013} 2014 2015int resource_get_mpc_slice_count(const struct pipe_ctx *pipe) 2016{ 2017 int mpc_split_count = 1; 2018 const struct pipe_ctx *other_pipe = pipe->bottom_pipe; 2019 2020 while (other_pipe && other_pipe->plane_state == pipe->plane_state) { 2021 mpc_split_count++; 2022 other_pipe = other_pipe->bottom_pipe; 2023 } 2024 other_pipe = pipe->top_pipe; 2025 while (other_pipe && other_pipe->plane_state == pipe->plane_state) { 2026 mpc_split_count++; 2027 other_pipe = other_pipe->top_pipe; 2028 } 2029 2030 return mpc_split_count; 2031} 2032 2033int resource_get_odm_slice_count(const struct pipe_ctx *pipe) 2034{ 2035 int odm_split_count = 1; 2036 2037 pipe = resource_get_otg_master(pipe); 2038 2039 while (pipe->next_odm_pipe) { 2040 odm_split_count++; 2041 pipe = pipe->next_odm_pipe; 2042 } 2043 return odm_split_count; 2044} 2045 2046int resource_get_odm_slice_index(const struct pipe_ctx *pipe_ctx) 2047{ 2048 int index = 0; 2049 2050 pipe_ctx = resource_get_opp_head(pipe_ctx); 2051 if (!pipe_ctx) 2052 return 0; 2053 2054 while (pipe_ctx->prev_odm_pipe) { 2055 index++; 2056 pipe_ctx = pipe_ctx->prev_odm_pipe; 2057 } 2058 2059 return index; 2060} 2061 2062bool resource_is_pipe_topology_changed(const struct dc_state *state_a, 2063 const struct dc_state *state_b) 2064{ 2065 int i; 2066 const struct pipe_ctx *pipe_a, *pipe_b; 2067 2068 if (state_a->stream_count != state_b->stream_count) 2069 return true; 2070 2071 for (i = 0; i < MAX_PIPES; i++) { 2072 pipe_a = &state_a->res_ctx.pipe_ctx[i]; 2073 pipe_b = &state_b->res_ctx.pipe_ctx[i]; 2074 2075 if (pipe_a->stream && !pipe_b->stream) 2076 return true; 2077 else if (!pipe_a->stream && pipe_b->stream) 2078 return true; 2079 2080 if (pipe_a->plane_state && !pipe_b->plane_state) 2081 return true; 2082 else if (!pipe_a->plane_state && pipe_b->plane_state) 2083 return true; 2084 2085 if (pipe_a->bottom_pipe && pipe_b->bottom_pipe) { 2086 if (pipe_a->bottom_pipe->pipe_idx != pipe_b->bottom_pipe->pipe_idx) 2087 return true; 2088 if ((pipe_a->bottom_pipe->plane_state == pipe_a->plane_state) && 2089 (pipe_b->bottom_pipe->plane_state != pipe_b->plane_state)) 2090 return true; 2091 else if ((pipe_a->bottom_pipe->plane_state != pipe_a->plane_state) && 2092 (pipe_b->bottom_pipe->plane_state == pipe_b->plane_state)) 2093 return true; 2094 } else if (pipe_a->bottom_pipe || pipe_b->bottom_pipe) { 2095 return true; 2096 } 2097 2098 if (pipe_a->next_odm_pipe && pipe_b->next_odm_pipe) { 2099 if (pipe_a->next_odm_pipe->pipe_idx != pipe_b->next_odm_pipe->pipe_idx) 2100 return true; 2101 } else if (pipe_a->next_odm_pipe || pipe_b->next_odm_pipe) { 2102 return true; 2103 } 2104 } 2105 return false; 2106} 2107 2108bool resource_is_odm_topology_changed(const struct pipe_ctx *otg_master_a, 2109 const struct pipe_ctx *otg_master_b) 2110{ 2111 const struct pipe_ctx *opp_head_a = otg_master_a; 2112 const struct pipe_ctx *opp_head_b = otg_master_b; 2113 2114 if (!resource_is_pipe_type(otg_master_a, OTG_MASTER) || 2115 !resource_is_pipe_type(otg_master_b, OTG_MASTER)) 2116 return true; 2117 2118 while (opp_head_a && opp_head_b) { 2119 if (opp_head_a->stream_res.opp != opp_head_b->stream_res.opp) 2120 return true; 2121 if ((opp_head_a->next_odm_pipe && !opp_head_b->next_odm_pipe) || 2122 (!opp_head_a->next_odm_pipe && opp_head_b->next_odm_pipe)) 2123 return true; 2124 opp_head_a = opp_head_a->next_odm_pipe; 2125 opp_head_b = opp_head_b->next_odm_pipe; 2126 } 2127 2128 return false; 2129} 2130 2131/* 2132 * Sample log: 2133 * pipe topology update 2134 * ________________________ 2135 * | plane0 slice0 stream0| 2136 * |DPP0----OPP0----OTG0----| <--- case 0 (OTG master pipe with plane) 2137 * | plane1 | | | 2138 * |DPP1----| | | <--- case 5 (DPP pipe not in last slice) 2139 * | plane0 slice1 | | 2140 * |DPP2----OPP2----| | <--- case 2 (OPP head pipe with plane) 2141 * | plane1 | | 2142 * |DPP3----| | <--- case 4 (DPP pipe in last slice) 2143 * | slice0 stream1| 2144 * |DPG4----OPP4----OTG4----| <--- case 1 (OTG master pipe without plane) 2145 * | slice1 | | 2146 * |DPG5----OPP5----| | <--- case 3 (OPP head pipe without plane) 2147 * |________________________| 2148 */ 2149 2150static void resource_log_pipe(struct dc *dc, struct pipe_ctx *pipe, 2151 int stream_idx, int slice_idx, int plane_idx, int slice_count, 2152 bool is_primary) 2153{ 2154 DC_LOGGER_INIT(dc->ctx->logger); 2155 2156 if (slice_idx == 0 && plane_idx == 0 && is_primary) { 2157 /* case 0 (OTG master pipe with plane) */ 2158 DC_LOG_DC(" | plane%d slice%d stream%d|", 2159 plane_idx, slice_idx, stream_idx); 2160 DC_LOG_DC(" |DPP%d----OPP%d----OTG%d----|", 2161 pipe->plane_res.dpp->inst, 2162 pipe->stream_res.opp->inst, 2163 pipe->stream_res.tg->inst); 2164 } else if (slice_idx == 0 && plane_idx == -1) { 2165 /* case 1 (OTG master pipe without plane) */ 2166 DC_LOG_DC(" | slice%d stream%d|", 2167 slice_idx, stream_idx); 2168 DC_LOG_DC(" |DPG%d----OPP%d----OTG%d----|", 2169 pipe->stream_res.opp->inst, 2170 pipe->stream_res.opp->inst, 2171 pipe->stream_res.tg->inst); 2172 } else if (slice_idx != 0 && plane_idx == 0 && is_primary) { 2173 /* case 2 (OPP head pipe with plane) */ 2174 DC_LOG_DC(" | plane%d slice%d | |", 2175 plane_idx, slice_idx); 2176 DC_LOG_DC(" |DPP%d----OPP%d----| |", 2177 pipe->plane_res.dpp->inst, 2178 pipe->stream_res.opp->inst); 2179 } else if (slice_idx != 0 && plane_idx == -1) { 2180 /* case 3 (OPP head pipe without plane) */ 2181 DC_LOG_DC(" | slice%d | |", slice_idx); 2182 DC_LOG_DC(" |DPG%d----OPP%d----| |", 2183 pipe->plane_res.dpp->inst, 2184 pipe->stream_res.opp->inst); 2185 } else if (slice_idx == slice_count - 1) { 2186 /* case 4 (DPP pipe in last slice) */ 2187 DC_LOG_DC(" | plane%d | |", plane_idx); 2188 DC_LOG_DC(" |DPP%d----| |", 2189 pipe->plane_res.dpp->inst); 2190 } else { 2191 /* case 5 (DPP pipe not in last slice) */ 2192 DC_LOG_DC(" | plane%d | | |", plane_idx); 2193 DC_LOG_DC(" |DPP%d----| | |", 2194 pipe->plane_res.dpp->inst); 2195 } 2196} 2197 2198static void resource_log_pipe_for_stream(struct dc *dc, struct dc_state *state, 2199 struct pipe_ctx *otg_master, int stream_idx) 2200{ 2201 struct pipe_ctx *opp_heads[MAX_PIPES]; 2202 struct pipe_ctx *dpp_pipes[MAX_PIPES]; 2203 2204 int slice_idx, dpp_idx, plane_idx, slice_count, dpp_count; 2205 bool is_primary; 2206 DC_LOGGER_INIT(dc->ctx->logger); 2207 2208 slice_count = resource_get_opp_heads_for_otg_master(otg_master, 2209 &state->res_ctx, opp_heads); 2210 for (slice_idx = 0; slice_idx < slice_count; slice_idx++) { 2211 plane_idx = -1; 2212 if (opp_heads[slice_idx]->plane_state) { 2213 dpp_count = resource_get_dpp_pipes_for_opp_head( 2214 opp_heads[slice_idx], 2215 &state->res_ctx, 2216 dpp_pipes); 2217 for (dpp_idx = 0; dpp_idx < dpp_count; dpp_idx++) { 2218 is_primary = !dpp_pipes[dpp_idx]->top_pipe || 2219 dpp_pipes[dpp_idx]->top_pipe->plane_state != dpp_pipes[dpp_idx]->plane_state; 2220 if (is_primary) 2221 plane_idx++; 2222 resource_log_pipe(dc, dpp_pipes[dpp_idx], 2223 stream_idx, slice_idx, 2224 plane_idx, slice_count, 2225 is_primary); 2226 } 2227 } else { 2228 resource_log_pipe(dc, opp_heads[slice_idx], 2229 stream_idx, slice_idx, plane_idx, 2230 slice_count, true); 2231 } 2232 2233 } 2234} 2235 2236static int resource_stream_to_stream_idx(struct dc_state *state, 2237 struct dc_stream_state *stream) 2238{ 2239 int i, stream_idx = -1; 2240 2241 for (i = 0; i < state->stream_count; i++) 2242 if (state->streams[i] == stream) { 2243 stream_idx = i; 2244 break; 2245 } 2246 2247 /* never return negative array index */ 2248 if (stream_idx == -1) { 2249 ASSERT(0); 2250 return 0; 2251 } 2252 2253 return stream_idx; 2254} 2255 2256void resource_log_pipe_topology_update(struct dc *dc, struct dc_state *state) 2257{ 2258 struct pipe_ctx *otg_master; 2259 int stream_idx, phantom_stream_idx; 2260 DC_LOGGER_INIT(dc->ctx->logger); 2261 2262 DC_LOG_DC(" pipe topology update"); 2263 DC_LOG_DC(" ________________________"); 2264 for (stream_idx = 0; stream_idx < state->stream_count; stream_idx++) { 2265 if (state->streams[stream_idx]->is_phantom) 2266 continue; 2267 2268 otg_master = resource_get_otg_master_for_stream( 2269 &state->res_ctx, state->streams[stream_idx]); 2270 resource_log_pipe_for_stream(dc, state, otg_master, stream_idx); 2271 } 2272 if (state->phantom_stream_count > 0) { 2273 DC_LOG_DC(" | (phantom pipes) |"); 2274 for (stream_idx = 0; stream_idx < state->stream_count; stream_idx++) { 2275 if (state->stream_status[stream_idx].mall_stream_config.type != SUBVP_MAIN) 2276 continue; 2277 2278 phantom_stream_idx = resource_stream_to_stream_idx(state, 2279 state->stream_status[stream_idx].mall_stream_config.paired_stream); 2280 otg_master = resource_get_otg_master_for_stream( 2281 &state->res_ctx, state->streams[phantom_stream_idx]); 2282 resource_log_pipe_for_stream(dc, state, otg_master, stream_idx); 2283 } 2284 } 2285 DC_LOG_DC(" |________________________|\n"); 2286} 2287 2288static struct pipe_ctx *get_tail_pipe( 2289 struct pipe_ctx *head_pipe) 2290{ 2291 struct pipe_ctx *tail_pipe = head_pipe->bottom_pipe; 2292 2293 while (tail_pipe) { 2294 head_pipe = tail_pipe; 2295 tail_pipe = tail_pipe->bottom_pipe; 2296 } 2297 2298 return head_pipe; 2299} 2300 2301static struct pipe_ctx *get_last_opp_head( 2302 struct pipe_ctx *opp_head) 2303{ 2304 ASSERT(resource_is_pipe_type(opp_head, OPP_HEAD)); 2305 while (opp_head->next_odm_pipe) 2306 opp_head = opp_head->next_odm_pipe; 2307 return opp_head; 2308} 2309 2310static struct pipe_ctx *get_last_dpp_pipe_in_mpcc_combine( 2311 struct pipe_ctx *dpp_pipe) 2312{ 2313 ASSERT(resource_is_pipe_type(dpp_pipe, DPP_PIPE)); 2314 while (dpp_pipe->bottom_pipe && 2315 dpp_pipe->plane_state == dpp_pipe->bottom_pipe->plane_state) 2316 dpp_pipe = dpp_pipe->bottom_pipe; 2317 return dpp_pipe; 2318} 2319 2320static bool update_pipe_params_after_odm_slice_count_change( 2321 struct pipe_ctx *otg_master, 2322 struct dc_state *context, 2323 const struct resource_pool *pool) 2324{ 2325 int i; 2326 struct pipe_ctx *pipe; 2327 bool result = true; 2328 2329 for (i = 0; i < pool->pipe_count && result; i++) { 2330 pipe = &context->res_ctx.pipe_ctx[i]; 2331 if (pipe->stream == otg_master->stream && pipe->plane_state) 2332 result = resource_build_scaling_params(pipe); 2333 } 2334 2335 if (pool->funcs->build_pipe_pix_clk_params) 2336 pool->funcs->build_pipe_pix_clk_params(otg_master); 2337 2338 resource_build_test_pattern_params(&context->res_ctx, otg_master); 2339 2340 return result; 2341} 2342 2343static bool update_pipe_params_after_mpc_slice_count_change( 2344 const struct dc_plane_state *plane, 2345 struct dc_state *context, 2346 const struct resource_pool *pool) 2347{ 2348 int i; 2349 struct pipe_ctx *pipe; 2350 bool result = true; 2351 2352 for (i = 0; i < pool->pipe_count && result; i++) { 2353 pipe = &context->res_ctx.pipe_ctx[i]; 2354 if (pipe->plane_state == plane) 2355 result = resource_build_scaling_params(pipe); 2356 } 2357 return result; 2358} 2359 2360static int acquire_first_split_pipe( 2361 struct resource_context *res_ctx, 2362 const struct resource_pool *pool, 2363 struct dc_stream_state *stream) 2364{ 2365 int i; 2366 2367 for (i = 0; i < pool->pipe_count; i++) { 2368 struct pipe_ctx *split_pipe = &res_ctx->pipe_ctx[i]; 2369 2370 if (split_pipe->top_pipe && 2371 split_pipe->top_pipe->plane_state == split_pipe->plane_state) { 2372 split_pipe->top_pipe->bottom_pipe = split_pipe->bottom_pipe; 2373 if (split_pipe->bottom_pipe) 2374 split_pipe->bottom_pipe->top_pipe = split_pipe->top_pipe; 2375 2376 if (split_pipe->top_pipe->plane_state) 2377 resource_build_scaling_params(split_pipe->top_pipe); 2378 2379 memset(split_pipe, 0, sizeof(*split_pipe)); 2380 split_pipe->stream_res.tg = pool->timing_generators[i]; 2381 split_pipe->plane_res.hubp = pool->hubps[i]; 2382 split_pipe->plane_res.ipp = pool->ipps[i]; 2383 split_pipe->plane_res.dpp = pool->dpps[i]; 2384 split_pipe->stream_res.opp = pool->opps[i]; 2385 split_pipe->plane_res.mpcc_inst = pool->dpps[i]->inst; 2386 split_pipe->pipe_idx = i; 2387 2388 split_pipe->stream = stream; 2389 return i; 2390 } 2391 } 2392 return FREE_PIPE_INDEX_NOT_FOUND; 2393} 2394 2395static void update_stream_engine_usage( 2396 struct resource_context *res_ctx, 2397 const struct resource_pool *pool, 2398 struct stream_encoder *stream_enc, 2399 bool acquired) 2400{ 2401 int i; 2402 2403 for (i = 0; i < pool->stream_enc_count; i++) { 2404 if (pool->stream_enc[i] == stream_enc) 2405 res_ctx->is_stream_enc_acquired[i] = acquired; 2406 } 2407} 2408 2409static void update_hpo_dp_stream_engine_usage( 2410 struct resource_context *res_ctx, 2411 const struct resource_pool *pool, 2412 struct hpo_dp_stream_encoder *hpo_dp_stream_enc, 2413 bool acquired) 2414{ 2415 int i; 2416 2417 for (i = 0; i < pool->hpo_dp_stream_enc_count; i++) { 2418 if (pool->hpo_dp_stream_enc[i] == hpo_dp_stream_enc) 2419 res_ctx->is_hpo_dp_stream_enc_acquired[i] = acquired; 2420 } 2421} 2422 2423static inline int find_acquired_hpo_dp_link_enc_for_link( 2424 const struct resource_context *res_ctx, 2425 const struct dc_link *link) 2426{ 2427 int i; 2428 2429 for (i = 0; i < ARRAY_SIZE(res_ctx->hpo_dp_link_enc_to_link_idx); i++) 2430 if (res_ctx->hpo_dp_link_enc_ref_cnts[i] > 0 && 2431 res_ctx->hpo_dp_link_enc_to_link_idx[i] == link->link_index) 2432 return i; 2433 2434 return -1; 2435} 2436 2437static inline int find_free_hpo_dp_link_enc(const struct resource_context *res_ctx, 2438 const struct resource_pool *pool) 2439{ 2440 int i; 2441 2442 for (i = 0; i < ARRAY_SIZE(res_ctx->hpo_dp_link_enc_ref_cnts); i++) 2443 if (res_ctx->hpo_dp_link_enc_ref_cnts[i] == 0) 2444 break; 2445 2446 return (i < ARRAY_SIZE(res_ctx->hpo_dp_link_enc_ref_cnts) && 2447 i < pool->hpo_dp_link_enc_count) ? i : -1; 2448} 2449 2450static inline void acquire_hpo_dp_link_enc( 2451 struct resource_context *res_ctx, 2452 unsigned int link_index, 2453 int enc_index) 2454{ 2455 res_ctx->hpo_dp_link_enc_to_link_idx[enc_index] = link_index; 2456 res_ctx->hpo_dp_link_enc_ref_cnts[enc_index] = 1; 2457} 2458 2459static inline void retain_hpo_dp_link_enc( 2460 struct resource_context *res_ctx, 2461 int enc_index) 2462{ 2463 res_ctx->hpo_dp_link_enc_ref_cnts[enc_index]++; 2464} 2465 2466static inline void release_hpo_dp_link_enc( 2467 struct resource_context *res_ctx, 2468 int enc_index) 2469{ 2470 ASSERT(res_ctx->hpo_dp_link_enc_ref_cnts[enc_index] > 0); 2471 res_ctx->hpo_dp_link_enc_ref_cnts[enc_index]--; 2472} 2473 2474static bool add_hpo_dp_link_enc_to_ctx(struct resource_context *res_ctx, 2475 const struct resource_pool *pool, 2476 struct pipe_ctx *pipe_ctx, 2477 struct dc_stream_state *stream) 2478{ 2479 int enc_index; 2480 2481 enc_index = find_acquired_hpo_dp_link_enc_for_link(res_ctx, stream->link); 2482 2483 if (enc_index >= 0) { 2484 retain_hpo_dp_link_enc(res_ctx, enc_index); 2485 } else { 2486 enc_index = find_free_hpo_dp_link_enc(res_ctx, pool); 2487 if (enc_index >= 0) 2488 acquire_hpo_dp_link_enc(res_ctx, stream->link->link_index, enc_index); 2489 } 2490 2491 if (enc_index >= 0) 2492 pipe_ctx->link_res.hpo_dp_link_enc = pool->hpo_dp_link_enc[enc_index]; 2493 2494 return pipe_ctx->link_res.hpo_dp_link_enc != NULL; 2495} 2496 2497static void remove_hpo_dp_link_enc_from_ctx(struct resource_context *res_ctx, 2498 struct pipe_ctx *pipe_ctx, 2499 struct dc_stream_state *stream) 2500{ 2501 int enc_index; 2502 2503 enc_index = find_acquired_hpo_dp_link_enc_for_link(res_ctx, stream->link); 2504 2505 if (enc_index >= 0) { 2506 release_hpo_dp_link_enc(res_ctx, enc_index); 2507 pipe_ctx->link_res.hpo_dp_link_enc = NULL; 2508 } 2509} 2510 2511enum dc_status resource_add_otg_master_for_stream_output(struct dc_state *new_ctx, 2512 const struct resource_pool *pool, 2513 struct dc_stream_state *stream) 2514{ 2515 struct dc *dc = stream->ctx->dc; 2516 2517 return dc->res_pool->funcs->add_stream_to_ctx(dc, new_ctx, stream); 2518} 2519 2520void resource_remove_otg_master_for_stream_output(struct dc_state *context, 2521 const struct resource_pool *pool, 2522 struct dc_stream_state *stream) 2523{ 2524 struct pipe_ctx *otg_master = resource_get_otg_master_for_stream( 2525 &context->res_ctx, stream); 2526 2527 if (!otg_master) 2528 return; 2529 2530 ASSERT(resource_get_odm_slice_count(otg_master) == 1); 2531 ASSERT(otg_master->plane_state == NULL); 2532 ASSERT(otg_master->stream_res.stream_enc); 2533 update_stream_engine_usage( 2534 &context->res_ctx, 2535 pool, 2536 otg_master->stream_res.stream_enc, 2537 false); 2538 2539 if (stream->ctx->dc->link_srv->dp_is_128b_132b_signal(otg_master)) { 2540 update_hpo_dp_stream_engine_usage( 2541 &context->res_ctx, pool, 2542 otg_master->stream_res.hpo_dp_stream_enc, 2543 false); 2544 remove_hpo_dp_link_enc_from_ctx( 2545 &context->res_ctx, otg_master, stream); 2546 } 2547 if (otg_master->stream_res.audio) 2548 update_audio_usage( 2549 &context->res_ctx, 2550 pool, 2551 otg_master->stream_res.audio, 2552 false); 2553 2554 resource_unreference_clock_source(&context->res_ctx, 2555 pool, 2556 otg_master->clock_source); 2557 2558 if (pool->funcs->remove_stream_from_ctx) 2559 pool->funcs->remove_stream_from_ctx( 2560 stream->ctx->dc, context, stream); 2561 memset(otg_master, 0, sizeof(*otg_master)); 2562} 2563 2564/* For each OPP head of an OTG master, add top plane at plane index 0. 2565 * 2566 * In the following example, the stream has 2 ODM slices without a top plane. 2567 * By adding a plane 0 to OPP heads, we are configuring our hardware to render 2568 * plane 0 by using each OPP head's DPP. 2569 * 2570 * Inter-pipe Relation (Before Adding Plane) 2571 * __________________________________________________ 2572 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER | 2573 * | | | slice 0 | | 2574 * | 0 | |blank ----ODM----------- | 2575 * | | | slice 1 | | | 2576 * | 1 | |blank ---- | | 2577 * |________|_______________|___________|_____________| 2578 * 2579 * Inter-pipe Relation (After Adding Plane) 2580 * __________________________________________________ 2581 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER | 2582 * | | plane 0 | slice 0 | | 2583 * | 0 | -------------------------ODM----------- | 2584 * | | plane 0 | slice 1 | | | 2585 * | 1 | ------------------------- | | 2586 * |________|_______________|___________|_____________| 2587 */ 2588static bool add_plane_to_opp_head_pipes(struct pipe_ctx *otg_master_pipe, 2589 struct dc_plane_state *plane_state, 2590 struct dc_state *context) 2591{ 2592 struct pipe_ctx *opp_head_pipe = otg_master_pipe; 2593 2594 while (opp_head_pipe) { 2595 if (opp_head_pipe->plane_state) { 2596 ASSERT(0); 2597 return false; 2598 } 2599 opp_head_pipe->plane_state = plane_state; 2600 opp_head_pipe = opp_head_pipe->next_odm_pipe; 2601 } 2602 2603 return true; 2604} 2605 2606/* For each OPP head of an OTG master, acquire a secondary DPP pipe and add 2607 * the plane. So the plane is added to all ODM slices associated with the OTG 2608 * master pipe in the bottom layer. 2609 * 2610 * In the following example, the stream has 2 ODM slices and a top plane 0. 2611 * By acquiring secondary DPP pipes and adding a plane 1, we are configuring our 2612 * hardware to render the plane 1 by acquiring a new pipe for each ODM slice and 2613 * render plane 1 using new pipes' DPP in the Z axis below plane 0. 2614 * 2615 * Inter-pipe Relation (Before Adding Plane) 2616 * __________________________________________________ 2617 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER | 2618 * | | plane 0 | slice 0 | | 2619 * | 0 | -------------------------ODM----------- | 2620 * | | plane 0 | slice 1 | | | 2621 * | 1 | ------------------------- | | 2622 * |________|_______________|___________|_____________| 2623 * 2624 * Inter-pipe Relation (After Acquiring and Adding Plane) 2625 * __________________________________________________ 2626 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER | 2627 * | | plane 0 | slice 0 | | 2628 * | 0 | -------------MPC---------ODM----------- | 2629 * | | plane 1 | | | | | 2630 * | 2 | ------------- | | | | 2631 * | | plane 0 | slice 1 | | | 2632 * | 1 | -------------MPC--------- | | 2633 * | | plane 1 | | | | 2634 * | 3 | ------------- | | | 2635 * |________|_______________|___________|_____________| 2636 */ 2637static bool acquire_secondary_dpp_pipes_and_add_plane( 2638 struct pipe_ctx *otg_master_pipe, 2639 struct dc_plane_state *plane_state, 2640 struct dc_state *new_ctx, 2641 struct dc_state *cur_ctx, 2642 struct resource_pool *pool) 2643{ 2644 struct pipe_ctx *opp_head_pipe, *sec_pipe, *tail_pipe; 2645 2646 if (!pool->funcs->acquire_free_pipe_as_secondary_dpp_pipe) { 2647 ASSERT(0); 2648 return false; 2649 } 2650 2651 opp_head_pipe = otg_master_pipe; 2652 while (opp_head_pipe) { 2653 sec_pipe = pool->funcs->acquire_free_pipe_as_secondary_dpp_pipe( 2654 cur_ctx, 2655 new_ctx, 2656 pool, 2657 opp_head_pipe); 2658 if (!sec_pipe) { 2659 /* try tearing down MPCC combine */ 2660 int pipe_idx = acquire_first_split_pipe( 2661 &new_ctx->res_ctx, pool, 2662 otg_master_pipe->stream); 2663 2664 if (pipe_idx >= 0) 2665 sec_pipe = &new_ctx->res_ctx.pipe_ctx[pipe_idx]; 2666 } 2667 2668 if (!sec_pipe) 2669 return false; 2670 2671 sec_pipe->plane_state = plane_state; 2672 2673 /* establish pipe relationship */ 2674 tail_pipe = get_tail_pipe(opp_head_pipe); 2675 tail_pipe->bottom_pipe = sec_pipe; 2676 sec_pipe->top_pipe = tail_pipe; 2677 sec_pipe->bottom_pipe = NULL; 2678 if (tail_pipe->prev_odm_pipe) { 2679 ASSERT(tail_pipe->prev_odm_pipe->bottom_pipe); 2680 sec_pipe->prev_odm_pipe = tail_pipe->prev_odm_pipe->bottom_pipe; 2681 tail_pipe->prev_odm_pipe->bottom_pipe->next_odm_pipe = sec_pipe; 2682 } else { 2683 sec_pipe->prev_odm_pipe = NULL; 2684 } 2685 2686 opp_head_pipe = opp_head_pipe->next_odm_pipe; 2687 } 2688 return true; 2689} 2690 2691bool resource_append_dpp_pipes_for_plane_composition( 2692 struct dc_state *new_ctx, 2693 struct dc_state *cur_ctx, 2694 struct resource_pool *pool, 2695 struct pipe_ctx *otg_master_pipe, 2696 struct dc_plane_state *plane_state) 2697{ 2698 bool success; 2699 if (otg_master_pipe->plane_state == NULL) 2700 success = add_plane_to_opp_head_pipes(otg_master_pipe, 2701 plane_state, new_ctx); 2702 else 2703 success = acquire_secondary_dpp_pipes_and_add_plane( 2704 otg_master_pipe, plane_state, new_ctx, 2705 cur_ctx, pool); 2706 if (success) 2707 /* when appending a plane mpc slice count changes from 0 to 1 */ 2708 success = update_pipe_params_after_mpc_slice_count_change( 2709 plane_state, new_ctx, pool); 2710 return success; 2711} 2712 2713void resource_remove_dpp_pipes_for_plane_composition( 2714 struct dc_state *context, 2715 const struct resource_pool *pool, 2716 const struct dc_plane_state *plane_state) 2717{ 2718 int i; 2719 for (i = pool->pipe_count - 1; i >= 0; i--) { 2720 struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; 2721 2722 if (pipe_ctx->plane_state == plane_state) { 2723 if (pipe_ctx->top_pipe) 2724 pipe_ctx->top_pipe->bottom_pipe = pipe_ctx->bottom_pipe; 2725 2726 /* Second condition is to avoid setting NULL to top pipe 2727 * of tail pipe making it look like head pipe in subsequent 2728 * deletes 2729 */ 2730 if (pipe_ctx->bottom_pipe && pipe_ctx->top_pipe) 2731 pipe_ctx->bottom_pipe->top_pipe = pipe_ctx->top_pipe; 2732 2733 /* 2734 * For head pipe detach surfaces from pipe for tail 2735 * pipe just zero it out 2736 */ 2737 if (!pipe_ctx->top_pipe) 2738 pipe_ctx->plane_state = NULL; 2739 else 2740 memset(pipe_ctx, 0, sizeof(*pipe_ctx)); 2741 } 2742 } 2743} 2744 2745/* 2746 * Increase ODM slice count by 1 by acquiring pipes and adding a new ODM slice 2747 * at the last index. 2748 * return - true if a new ODM slice is added and required pipes are acquired. 2749 * false if new_ctx is no longer a valid state after new ODM slice is added. 2750 * 2751 * This is achieved by duplicating MPC blending tree from previous ODM slice. 2752 * In the following example, we have a single MPC tree and 1 ODM slice 0. We 2753 * want to add a new odm slice by duplicating the MPC blending tree and add 2754 * ODM slice 1. 2755 * 2756 * Inter-pipe Relation (Before Acquiring and Adding ODM Slice) 2757 * __________________________________________________ 2758 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER | 2759 * | | plane 0 | slice 0 | | 2760 * | 0 | -------------MPC---------ODM----------- | 2761 * | | plane 1 | | | | 2762 * | 1 | ------------- | | | 2763 * |________|_______________|___________|_____________| 2764 * 2765 * Inter-pipe Relation (After Acquiring and Adding ODM Slice) 2766 * __________________________________________________ 2767 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER | 2768 * | | plane 0 | slice 0 | | 2769 * | 0 | -------------MPC---------ODM----------- | 2770 * | | plane 1 | | | | | 2771 * | 1 | ------------- | | | | 2772 * | | plane 0 | slice 1 | | | 2773 * | 2 | -------------MPC--------- | | 2774 * | | plane 1 | | | | 2775 * | 3 | ------------- | | | 2776 * |________|_______________|___________|_____________| 2777 */ 2778static bool acquire_pipes_and_add_odm_slice( 2779 struct pipe_ctx *otg_master_pipe, 2780 struct dc_state *new_ctx, 2781 const struct dc_state *cur_ctx, 2782 const struct resource_pool *pool) 2783{ 2784 struct pipe_ctx *last_opp_head = get_last_opp_head(otg_master_pipe); 2785 struct pipe_ctx *new_opp_head; 2786 struct pipe_ctx *last_top_dpp_pipe, *last_bottom_dpp_pipe, 2787 *new_top_dpp_pipe, *new_bottom_dpp_pipe; 2788 2789 if (!pool->funcs->acquire_free_pipe_as_secondary_opp_head) { 2790 ASSERT(0); 2791 return false; 2792 } 2793 new_opp_head = pool->funcs->acquire_free_pipe_as_secondary_opp_head( 2794 cur_ctx, new_ctx, pool, 2795 otg_master_pipe); 2796 if (!new_opp_head) 2797 return false; 2798 2799 last_opp_head->next_odm_pipe = new_opp_head; 2800 new_opp_head->prev_odm_pipe = last_opp_head; 2801 new_opp_head->next_odm_pipe = NULL; 2802 new_opp_head->plane_state = last_opp_head->plane_state; 2803 last_top_dpp_pipe = last_opp_head; 2804 new_top_dpp_pipe = new_opp_head; 2805 2806 while (last_top_dpp_pipe->bottom_pipe) { 2807 last_bottom_dpp_pipe = last_top_dpp_pipe->bottom_pipe; 2808 new_bottom_dpp_pipe = pool->funcs->acquire_free_pipe_as_secondary_dpp_pipe( 2809 cur_ctx, new_ctx, pool, 2810 new_opp_head); 2811 if (!new_bottom_dpp_pipe) 2812 return false; 2813 2814 new_bottom_dpp_pipe->plane_state = last_bottom_dpp_pipe->plane_state; 2815 new_top_dpp_pipe->bottom_pipe = new_bottom_dpp_pipe; 2816 new_bottom_dpp_pipe->top_pipe = new_top_dpp_pipe; 2817 last_bottom_dpp_pipe->next_odm_pipe = new_bottom_dpp_pipe; 2818 new_bottom_dpp_pipe->prev_odm_pipe = last_bottom_dpp_pipe; 2819 new_bottom_dpp_pipe->next_odm_pipe = NULL; 2820 last_top_dpp_pipe = last_bottom_dpp_pipe; 2821 } 2822 2823 return true; 2824} 2825 2826/* 2827 * Decrease ODM slice count by 1 by releasing pipes and removing the ODM slice 2828 * at the last index. 2829 * return - true if the last ODM slice is removed and related pipes are 2830 * released. false if there is no removable ODM slice. 2831 * 2832 * In the following example, we have 2 MPC trees and ODM slice 0 and slice 1. 2833 * We want to remove the last ODM i.e slice 1. We are releasing secondary DPP 2834 * pipe 3 and OPP head pipe 2. 2835 * 2836 * Inter-pipe Relation (Before Releasing and Removing ODM Slice) 2837 * __________________________________________________ 2838 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER | 2839 * | | plane 0 | slice 0 | | 2840 * | 0 | -------------MPC---------ODM----------- | 2841 * | | plane 1 | | | | | 2842 * | 1 | ------------- | | | | 2843 * | | plane 0 | slice 1 | | | 2844 * | 2 | -------------MPC--------- | | 2845 * | | plane 1 | | | | 2846 * | 3 | ------------- | | | 2847 * |________|_______________|___________|_____________| 2848 * 2849 * Inter-pipe Relation (After Releasing and Removing ODM Slice) 2850 * __________________________________________________ 2851 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER | 2852 * | | plane 0 | slice 0 | | 2853 * | 0 | -------------MPC---------ODM----------- | 2854 * | | plane 1 | | | | 2855 * | 1 | ------------- | | | 2856 * |________|_______________|___________|_____________| 2857 */ 2858static bool release_pipes_and_remove_odm_slice( 2859 struct pipe_ctx *otg_master_pipe, 2860 struct dc_state *context, 2861 const struct resource_pool *pool) 2862{ 2863 struct pipe_ctx *last_opp_head = get_last_opp_head(otg_master_pipe); 2864 struct pipe_ctx *tail_pipe = get_tail_pipe(last_opp_head); 2865 2866 if (!pool->funcs->release_pipe) { 2867 ASSERT(0); 2868 return false; 2869 } 2870 2871 if (resource_is_pipe_type(last_opp_head, OTG_MASTER)) 2872 return false; 2873 2874 while (tail_pipe->top_pipe) { 2875 tail_pipe->prev_odm_pipe->next_odm_pipe = NULL; 2876 tail_pipe = tail_pipe->top_pipe; 2877 pool->funcs->release_pipe(context, tail_pipe->bottom_pipe, pool); 2878 tail_pipe->bottom_pipe = NULL; 2879 } 2880 last_opp_head->prev_odm_pipe->next_odm_pipe = NULL; 2881 pool->funcs->release_pipe(context, last_opp_head, pool); 2882 2883 return true; 2884} 2885 2886/* 2887 * Increase MPC slice count by 1 by acquiring a new DPP pipe and add it as the 2888 * last MPC slice of the plane associated with dpp_pipe. 2889 * 2890 * return - true if a new MPC slice is added and required pipes are acquired. 2891 * false if new_ctx is no longer a valid state after new MPC slice is added. 2892 * 2893 * In the following example, we add a new MPC slice for plane 0 into the 2894 * new_ctx. To do so we pass pipe 0 as dpp_pipe. The function acquires a new DPP 2895 * pipe 2 for plane 0 as the bottom most pipe for plane 0. 2896 * 2897 * Inter-pipe Relation (Before Acquiring and Adding MPC Slice) 2898 * __________________________________________________ 2899 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER | 2900 * | | plane 0 | | | 2901 * | 0 | -------------MPC----------------------- | 2902 * | | plane 1 | | | | 2903 * | 1 | ------------- | | | 2904 * |________|_______________|___________|_____________| 2905 * 2906 * Inter-pipe Relation (After Acquiring and Adding MPC Slice) 2907 * __________________________________________________ 2908 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER | 2909 * | | plane 0 | | | 2910 * | 0 | -------------MPC----------------------- | 2911 * | | plane 0 | | | | 2912 * | 2 | ------------- | | | 2913 * | | plane 1 | | | | 2914 * | 1 | ------------- | | | 2915 * |________|_______________|___________|_____________| 2916 */ 2917static bool acquire_dpp_pipe_and_add_mpc_slice( 2918 struct pipe_ctx *dpp_pipe, 2919 struct dc_state *new_ctx, 2920 const struct dc_state *cur_ctx, 2921 const struct resource_pool *pool) 2922{ 2923 struct pipe_ctx *last_dpp_pipe = 2924 get_last_dpp_pipe_in_mpcc_combine(dpp_pipe); 2925 struct pipe_ctx *opp_head = resource_get_opp_head(dpp_pipe); 2926 struct pipe_ctx *new_dpp_pipe; 2927 2928 if (!pool->funcs->acquire_free_pipe_as_secondary_dpp_pipe) { 2929 ASSERT(0); 2930 return false; 2931 } 2932 new_dpp_pipe = pool->funcs->acquire_free_pipe_as_secondary_dpp_pipe( 2933 cur_ctx, new_ctx, pool, opp_head); 2934 if (!new_dpp_pipe || resource_get_odm_slice_count(dpp_pipe) > 1) 2935 return false; 2936 2937 new_dpp_pipe->bottom_pipe = last_dpp_pipe->bottom_pipe; 2938 if (new_dpp_pipe->bottom_pipe) 2939 new_dpp_pipe->bottom_pipe->top_pipe = new_dpp_pipe; 2940 new_dpp_pipe->top_pipe = last_dpp_pipe; 2941 last_dpp_pipe->bottom_pipe = new_dpp_pipe; 2942 new_dpp_pipe->plane_state = last_dpp_pipe->plane_state; 2943 2944 return true; 2945} 2946 2947/* 2948 * Reduce MPC slice count by 1 by releasing the bottom DPP pipe in MPCC combine 2949 * with dpp_pipe and removing last MPC slice of the plane associated with 2950 * dpp_pipe. 2951 * 2952 * return - true if the last MPC slice of the plane associated with dpp_pipe is 2953 * removed and last DPP pipe in MPCC combine with dpp_pipe is released. 2954 * false if there is no removable MPC slice. 2955 * 2956 * In the following example, we remove an MPC slice for plane 0 from the 2957 * context. To do so we pass pipe 0 as dpp_pipe. The function releases pipe 1 as 2958 * it is the last pipe for plane 0. 2959 * 2960 * Inter-pipe Relation (Before Releasing and Removing MPC Slice) 2961 * __________________________________________________ 2962 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER | 2963 * | | plane 0 | | | 2964 * | 0 | -------------MPC----------------------- | 2965 * | | plane 0 | | | | 2966 * | 1 | ------------- | | | 2967 * | | plane 1 | | | | 2968 * | 2 | ------------- | | | 2969 * |________|_______________|___________|_____________| 2970 * 2971 * Inter-pipe Relation (After Releasing and Removing MPC Slice) 2972 * __________________________________________________ 2973 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER | 2974 * | | plane 0 | | | 2975 * | 0 | -------------MPC----------------------- | 2976 * | | plane 1 | | | | 2977 * | 2 | ------------- | | | 2978 * |________|_______________|___________|_____________| 2979 */ 2980static bool release_dpp_pipe_and_remove_mpc_slice( 2981 struct pipe_ctx *dpp_pipe, 2982 struct dc_state *context, 2983 const struct resource_pool *pool) 2984{ 2985 struct pipe_ctx *last_dpp_pipe = 2986 get_last_dpp_pipe_in_mpcc_combine(dpp_pipe); 2987 2988 if (!pool->funcs->release_pipe) { 2989 ASSERT(0); 2990 return false; 2991 } 2992 2993 if (resource_is_pipe_type(last_dpp_pipe, OPP_HEAD) || 2994 resource_get_odm_slice_count(dpp_pipe) > 1) 2995 return false; 2996 2997 last_dpp_pipe->top_pipe->bottom_pipe = last_dpp_pipe->bottom_pipe; 2998 if (last_dpp_pipe->bottom_pipe) 2999 last_dpp_pipe->bottom_pipe->top_pipe = last_dpp_pipe->top_pipe; 3000 pool->funcs->release_pipe(context, last_dpp_pipe, pool); 3001 3002 return true; 3003} 3004 3005bool resource_update_pipes_for_stream_with_slice_count( 3006 struct dc_state *new_ctx, 3007 const struct dc_state *cur_ctx, 3008 const struct resource_pool *pool, 3009 const struct dc_stream_state *stream, 3010 int new_slice_count) 3011{ 3012 int i; 3013 struct pipe_ctx *otg_master = resource_get_otg_master_for_stream( 3014 &new_ctx->res_ctx, stream); 3015 int cur_slice_count = resource_get_odm_slice_count(otg_master); 3016 bool result = true; 3017 3018 if (new_slice_count == cur_slice_count) 3019 return result; 3020 3021 if (new_slice_count > cur_slice_count) 3022 for (i = 0; i < new_slice_count - cur_slice_count && result; i++) 3023 result = acquire_pipes_and_add_odm_slice( 3024 otg_master, new_ctx, cur_ctx, pool); 3025 else 3026 for (i = 0; i < cur_slice_count - new_slice_count && result; i++) 3027 result = release_pipes_and_remove_odm_slice( 3028 otg_master, new_ctx, pool); 3029 if (result) 3030 result = update_pipe_params_after_odm_slice_count_change( 3031 otg_master, new_ctx, pool); 3032 return result; 3033} 3034 3035bool resource_update_pipes_for_plane_with_slice_count( 3036 struct dc_state *new_ctx, 3037 const struct dc_state *cur_ctx, 3038 const struct resource_pool *pool, 3039 const struct dc_plane_state *plane, 3040 int new_slice_count) 3041{ 3042 int i; 3043 int dpp_pipe_count; 3044 int cur_slice_count; 3045 struct pipe_ctx *dpp_pipes[MAX_PIPES] = {0}; 3046 bool result = true; 3047 3048 dpp_pipe_count = resource_get_dpp_pipes_for_plane(plane, 3049 &new_ctx->res_ctx, dpp_pipes); 3050 ASSERT(dpp_pipe_count > 0); 3051 cur_slice_count = resource_get_mpc_slice_count(dpp_pipes[0]); 3052 3053 if (new_slice_count == cur_slice_count) 3054 return result; 3055 3056 if (new_slice_count > cur_slice_count) 3057 for (i = 0; i < new_slice_count - cur_slice_count && result; i++) 3058 result = acquire_dpp_pipe_and_add_mpc_slice( 3059 dpp_pipes[0], new_ctx, cur_ctx, pool); 3060 else 3061 for (i = 0; i < cur_slice_count - new_slice_count && result; i++) 3062 result = release_dpp_pipe_and_remove_mpc_slice( 3063 dpp_pipes[0], new_ctx, pool); 3064 if (result) 3065 result = update_pipe_params_after_mpc_slice_count_change( 3066 dpp_pipes[0]->plane_state, new_ctx, pool); 3067 return result; 3068} 3069 3070bool dc_is_timing_changed(struct dc_stream_state *cur_stream, 3071 struct dc_stream_state *new_stream) 3072{ 3073 if (cur_stream == NULL) 3074 return true; 3075 3076 /* If output color space is changed, need to reprogram info frames */ 3077 if (cur_stream->output_color_space != new_stream->output_color_space) 3078 return true; 3079 3080 return memcmp( 3081 &cur_stream->timing, 3082 &new_stream->timing, 3083 sizeof(struct dc_crtc_timing)) != 0; 3084} 3085 3086static bool are_stream_backends_same( 3087 struct dc_stream_state *stream_a, struct dc_stream_state *stream_b) 3088{ 3089 if (stream_a == stream_b) 3090 return true; 3091 3092 if (stream_a == NULL || stream_b == NULL) 3093 return false; 3094 3095 if (dc_is_timing_changed(stream_a, stream_b)) 3096 return false; 3097 3098 if (stream_a->signal != stream_b->signal) 3099 return false; 3100 3101 if (stream_a->dpms_off != stream_b->dpms_off) 3102 return false; 3103 3104 return true; 3105} 3106 3107/* 3108 * dc_is_stream_unchanged() - Compare two stream states for equivalence. 3109 * 3110 * Checks if there a difference between the two states 3111 * that would require a mode change. 3112 * 3113 * Does not compare cursor position or attributes. 3114 */ 3115bool dc_is_stream_unchanged( 3116 struct dc_stream_state *old_stream, struct dc_stream_state *stream) 3117{ 3118 3119 if (!are_stream_backends_same(old_stream, stream)) 3120 return false; 3121 3122 if (old_stream->ignore_msa_timing_param != stream->ignore_msa_timing_param) 3123 return false; 3124 3125 /*compare audio info*/ 3126 if (memcmp(&old_stream->audio_info, &stream->audio_info, sizeof(stream->audio_info)) != 0) 3127 return false; 3128 3129 return true; 3130} 3131 3132/* 3133 * dc_is_stream_scaling_unchanged() - Compare scaling rectangles of two streams. 3134 */ 3135bool dc_is_stream_scaling_unchanged(struct dc_stream_state *old_stream, 3136 struct dc_stream_state *stream) 3137{ 3138 if (old_stream == stream) 3139 return true; 3140 3141 if (old_stream == NULL || stream == NULL) 3142 return false; 3143 3144 if (memcmp(&old_stream->src, 3145 &stream->src, 3146 sizeof(struct rect)) != 0) 3147 return false; 3148 3149 if (memcmp(&old_stream->dst, 3150 &stream->dst, 3151 sizeof(struct rect)) != 0) 3152 return false; 3153 3154 return true; 3155} 3156 3157/* TODO: release audio object */ 3158void update_audio_usage( 3159 struct resource_context *res_ctx, 3160 const struct resource_pool *pool, 3161 struct audio *audio, 3162 bool acquired) 3163{ 3164 int i; 3165 for (i = 0; i < pool->audio_count; i++) { 3166 if (pool->audios[i] == audio) 3167 res_ctx->is_audio_acquired[i] = acquired; 3168 } 3169} 3170 3171static struct hpo_dp_stream_encoder *find_first_free_match_hpo_dp_stream_enc_for_link( 3172 struct resource_context *res_ctx, 3173 const struct resource_pool *pool, 3174 struct dc_stream_state *stream) 3175{ 3176 int i; 3177 3178 for (i = 0; i < pool->hpo_dp_stream_enc_count; i++) { 3179 if (!res_ctx->is_hpo_dp_stream_enc_acquired[i] && 3180 pool->hpo_dp_stream_enc[i]) { 3181 3182 return pool->hpo_dp_stream_enc[i]; 3183 } 3184 } 3185 3186 return NULL; 3187} 3188 3189static struct audio *find_first_free_audio( 3190 struct resource_context *res_ctx, 3191 const struct resource_pool *pool, 3192 enum engine_id id, 3193 enum dce_version dc_version) 3194{ 3195 int i, available_audio_count; 3196 3197 if (id == ENGINE_ID_UNKNOWN) 3198 return NULL; 3199 3200 available_audio_count = pool->audio_count; 3201 3202 for (i = 0; i < available_audio_count; i++) { 3203 if ((res_ctx->is_audio_acquired[i] == false) && (res_ctx->is_stream_enc_acquired[i] == true)) { 3204 /*we have enough audio endpoint, find the matching inst*/ 3205 if (id != i) 3206 continue; 3207 return pool->audios[i]; 3208 } 3209 } 3210 3211 /* use engine id to find free audio */ 3212 if ((id < available_audio_count) && (res_ctx->is_audio_acquired[id] == false)) { 3213 return pool->audios[id]; 3214 } 3215 /*not found the matching one, first come first serve*/ 3216 for (i = 0; i < available_audio_count; i++) { 3217 if (res_ctx->is_audio_acquired[i] == false) { 3218 return pool->audios[i]; 3219 } 3220 } 3221 return NULL; 3222} 3223 3224static struct dc_stream_state *find_pll_sharable_stream( 3225 struct dc_stream_state *stream_needs_pll, 3226 struct dc_state *context) 3227{ 3228 int i; 3229 3230 for (i = 0; i < context->stream_count; i++) { 3231 struct dc_stream_state *stream_has_pll = context->streams[i]; 3232 3233 /* We are looking for non dp, non virtual stream */ 3234 if (resource_are_streams_timing_synchronizable( 3235 stream_needs_pll, stream_has_pll) 3236 && !dc_is_dp_signal(stream_has_pll->signal) 3237 && stream_has_pll->link->connector_signal 3238 != SIGNAL_TYPE_VIRTUAL) 3239 return stream_has_pll; 3240 3241 } 3242 3243 return NULL; 3244} 3245 3246static int get_norm_pix_clk(const struct dc_crtc_timing *timing) 3247{ 3248 uint32_t pix_clk = timing->pix_clk_100hz; 3249 uint32_t normalized_pix_clk = pix_clk; 3250 3251 if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) 3252 pix_clk /= 2; 3253 if (timing->pixel_encoding != PIXEL_ENCODING_YCBCR422) { 3254 switch (timing->display_color_depth) { 3255 case COLOR_DEPTH_666: 3256 case COLOR_DEPTH_888: 3257 normalized_pix_clk = pix_clk; 3258 break; 3259 case COLOR_DEPTH_101010: 3260 normalized_pix_clk = (pix_clk * 30) / 24; 3261 break; 3262 case COLOR_DEPTH_121212: 3263 normalized_pix_clk = (pix_clk * 36) / 24; 3264 break; 3265 case COLOR_DEPTH_161616: 3266 normalized_pix_clk = (pix_clk * 48) / 24; 3267 break; 3268 default: 3269 ASSERT(0); 3270 break; 3271 } 3272 } 3273 return normalized_pix_clk; 3274} 3275 3276static void calculate_phy_pix_clks(struct dc_stream_state *stream) 3277{ 3278 /* update actual pixel clock on all streams */ 3279 if (dc_is_hdmi_signal(stream->signal)) 3280 stream->phy_pix_clk = get_norm_pix_clk( 3281 &stream->timing) / 10; 3282 else 3283 stream->phy_pix_clk = 3284 stream->timing.pix_clk_100hz / 10; 3285 3286 if (stream->timing.timing_3d_format == TIMING_3D_FORMAT_HW_FRAME_PACKING) 3287 stream->phy_pix_clk *= 2; 3288} 3289 3290static int acquire_resource_from_hw_enabled_state( 3291 struct resource_context *res_ctx, 3292 const struct resource_pool *pool, 3293 struct dc_stream_state *stream) 3294{ 3295 struct dc_link *link = stream->link; 3296 unsigned int i, inst, tg_inst = 0; 3297 uint32_t numPipes = 1; 3298 uint32_t id_src[4] = {0}; 3299 3300 /* Check for enabled DIG to identify enabled display */ 3301 if (!link->link_enc->funcs->is_dig_enabled(link->link_enc)) 3302 return -1; 3303 3304 inst = link->link_enc->funcs->get_dig_frontend(link->link_enc); 3305 3306 if (inst == ENGINE_ID_UNKNOWN) 3307 return -1; 3308 3309 for (i = 0; i < pool->stream_enc_count; i++) { 3310 if (pool->stream_enc[i]->id == inst) { 3311 tg_inst = pool->stream_enc[i]->funcs->dig_source_otg( 3312 pool->stream_enc[i]); 3313 break; 3314 } 3315 } 3316 3317 // tg_inst not found 3318 if (i == pool->stream_enc_count) 3319 return -1; 3320 3321 if (tg_inst >= pool->timing_generator_count) 3322 return -1; 3323 3324 if (!res_ctx->pipe_ctx[tg_inst].stream) { 3325 struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[tg_inst]; 3326 3327 pipe_ctx->stream_res.tg = pool->timing_generators[tg_inst]; 3328 id_src[0] = tg_inst; 3329 3330 if (pipe_ctx->stream_res.tg->funcs->get_optc_source) 3331 pipe_ctx->stream_res.tg->funcs->get_optc_source(pipe_ctx->stream_res.tg, 3332 &numPipes, &id_src[0], &id_src[1]); 3333 3334 if (id_src[0] == 0xf && id_src[1] == 0xf) { 3335 id_src[0] = tg_inst; 3336 numPipes = 1; 3337 } 3338 3339 for (i = 0; i < numPipes; i++) { 3340 //Check if src id invalid 3341 if (id_src[i] == 0xf) 3342 return -1; 3343 3344 pipe_ctx = &res_ctx->pipe_ctx[id_src[i]]; 3345 3346 pipe_ctx->stream_res.tg = pool->timing_generators[tg_inst]; 3347 pipe_ctx->plane_res.mi = pool->mis[id_src[i]]; 3348 pipe_ctx->plane_res.hubp = pool->hubps[id_src[i]]; 3349 pipe_ctx->plane_res.ipp = pool->ipps[id_src[i]]; 3350 pipe_ctx->plane_res.xfm = pool->transforms[id_src[i]]; 3351 pipe_ctx->plane_res.dpp = pool->dpps[id_src[i]]; 3352 pipe_ctx->stream_res.opp = pool->opps[id_src[i]]; 3353 3354 if (pool->dpps[id_src[i]]) { 3355 pipe_ctx->plane_res.mpcc_inst = pool->dpps[id_src[i]]->inst; 3356 3357 if (pool->mpc->funcs->read_mpcc_state) { 3358 struct mpcc_state s = {0}; 3359 3360 pool->mpc->funcs->read_mpcc_state(pool->mpc, pipe_ctx->plane_res.mpcc_inst, &s); 3361 3362 if (s.dpp_id < MAX_MPCC) 3363 pool->mpc->mpcc_array[pipe_ctx->plane_res.mpcc_inst].dpp_id = 3364 s.dpp_id; 3365 3366 if (s.bot_mpcc_id < MAX_MPCC) 3367 pool->mpc->mpcc_array[pipe_ctx->plane_res.mpcc_inst].mpcc_bot = 3368 &pool->mpc->mpcc_array[s.bot_mpcc_id]; 3369 3370 if (s.opp_id < MAX_OPP) 3371 pipe_ctx->stream_res.opp->mpc_tree_params.opp_id = s.opp_id; 3372 } 3373 } 3374 pipe_ctx->pipe_idx = id_src[i]; 3375 3376 if (id_src[i] >= pool->timing_generator_count) { 3377 id_src[i] = pool->timing_generator_count - 1; 3378 3379 pipe_ctx->stream_res.tg = pool->timing_generators[id_src[i]]; 3380 pipe_ctx->stream_res.opp = pool->opps[id_src[i]]; 3381 } 3382 3383 pipe_ctx->stream = stream; 3384 } 3385 3386 if (numPipes == 2) { 3387 stream->apply_boot_odm_mode = dm_odm_combine_policy_2to1; 3388 res_ctx->pipe_ctx[id_src[0]].next_odm_pipe = &res_ctx->pipe_ctx[id_src[1]]; 3389 res_ctx->pipe_ctx[id_src[0]].prev_odm_pipe = NULL; 3390 res_ctx->pipe_ctx[id_src[1]].next_odm_pipe = NULL; 3391 res_ctx->pipe_ctx[id_src[1]].prev_odm_pipe = &res_ctx->pipe_ctx[id_src[0]]; 3392 } else 3393 stream->apply_boot_odm_mode = dm_odm_combine_mode_disabled; 3394 3395 return id_src[0]; 3396 } 3397 3398 return -1; 3399} 3400 3401static void mark_seamless_boot_stream( 3402 const struct dc *dc, 3403 struct dc_stream_state *stream) 3404{ 3405 struct dc_bios *dcb = dc->ctx->dc_bios; 3406 3407 if (dc->config.allow_seamless_boot_optimization && 3408 !dcb->funcs->is_accelerated_mode(dcb)) { 3409 if (dc_validate_boot_timing(dc, stream->sink, &stream->timing)) 3410 stream->apply_seamless_boot_optimization = true; 3411 } 3412} 3413 3414/* 3415 * Acquire a pipe as OTG master and assign to the stream in new dc context. 3416 * return - true if OTG master pipe is acquired and new dc context is updated. 3417 * false if it fails to acquire an OTG master pipe for this stream. 3418 * 3419 * In the example below, we acquired pipe 0 as OTG master pipe for the stream. 3420 * After the function its Inter-pipe Relation is represented by the diagram 3421 * below. 3422 * 3423 * Inter-pipe Relation 3424 * __________________________________________________ 3425 * |PIPE IDX| DPP PIPES | OPP HEADS | OTG MASTER | 3426 * | | | | | 3427 * | 0 | |blank ------------------ | 3428 * |________|_______________|___________|_____________| 3429 */ 3430static bool acquire_otg_master_pipe_for_stream( 3431 const struct dc_state *cur_ctx, 3432 struct dc_state *new_ctx, 3433 const struct resource_pool *pool, 3434 struct dc_stream_state *stream) 3435{ 3436 /* TODO: Move this function to DCN specific resource file and acquire 3437 * DSC resource here. The reason is that the function should have the 3438 * same level of responsibility as when we acquire secondary OPP head. 3439 * We acquire DSC when we acquire secondary OPP head, so we should 3440 * acquire DSC when we acquire OTG master. 3441 */ 3442 int pipe_idx; 3443 struct pipe_ctx *pipe_ctx = NULL; 3444 3445 /* 3446 * Upper level code is responsible to optimize unnecessary addition and 3447 * removal for unchanged streams. So unchanged stream will keep the same 3448 * OTG master instance allocated. When current stream is removed and a 3449 * new stream is added, we want to reuse the OTG instance made available 3450 * by the removed stream first. If not found, we try to avoid of using 3451 * any free pipes already used in current context as this could tear 3452 * down exiting ODM/MPC/MPO configuration unnecessarily. 3453 */ 3454 3455 /* 3456 * Try to acquire the same OTG master already in use. This is not 3457 * optimal because resetting an enabled OTG master pipe for a new stream 3458 * requires an extra frame of wait. However there are test automation 3459 * and eDP assumptions that rely on reusing the same OTG master pipe 3460 * during mode change. We have to keep this logic as is for now. 3461 */ 3462 pipe_idx = recource_find_free_pipe_used_as_otg_master_in_cur_res_ctx( 3463 &cur_ctx->res_ctx, &new_ctx->res_ctx, pool); 3464 /* 3465 * Try to acquire a pipe not used in current resource context to avoid 3466 * pipe swapping. 3467 */ 3468 if (pipe_idx == FREE_PIPE_INDEX_NOT_FOUND) 3469 pipe_idx = recource_find_free_pipe_not_used_in_cur_res_ctx( 3470 &cur_ctx->res_ctx, &new_ctx->res_ctx, pool); 3471 /* 3472 * If pipe swapping is unavoidable, try to acquire pipe used as 3473 * secondary DPP pipe in current state as we prioritize to support more 3474 * streams over supporting MPO planes. 3475 */ 3476 if (pipe_idx == FREE_PIPE_INDEX_NOT_FOUND) 3477 pipe_idx = resource_find_free_pipe_used_as_cur_sec_dpp( 3478 &cur_ctx->res_ctx, &new_ctx->res_ctx, pool); 3479 if (pipe_idx == FREE_PIPE_INDEX_NOT_FOUND) 3480 pipe_idx = resource_find_any_free_pipe(&new_ctx->res_ctx, pool); 3481 if (pipe_idx != FREE_PIPE_INDEX_NOT_FOUND) { 3482 pipe_ctx = &new_ctx->res_ctx.pipe_ctx[pipe_idx]; 3483 memset(pipe_ctx, 0, sizeof(*pipe_ctx)); 3484 pipe_ctx->pipe_idx = pipe_idx; 3485 pipe_ctx->stream_res.tg = pool->timing_generators[pipe_idx]; 3486 pipe_ctx->plane_res.mi = pool->mis[pipe_idx]; 3487 pipe_ctx->plane_res.hubp = pool->hubps[pipe_idx]; 3488 pipe_ctx->plane_res.ipp = pool->ipps[pipe_idx]; 3489 pipe_ctx->plane_res.xfm = pool->transforms[pipe_idx]; 3490 pipe_ctx->plane_res.dpp = pool->dpps[pipe_idx]; 3491 pipe_ctx->stream_res.opp = pool->opps[pipe_idx]; 3492 if (pool->dpps[pipe_idx]) 3493 pipe_ctx->plane_res.mpcc_inst = pool->dpps[pipe_idx]->inst; 3494 3495 if (pipe_idx >= pool->timing_generator_count) { 3496 int tg_inst = pool->timing_generator_count - 1; 3497 3498 pipe_ctx->stream_res.tg = pool->timing_generators[tg_inst]; 3499 pipe_ctx->stream_res.opp = pool->opps[tg_inst]; 3500 } 3501 3502 pipe_ctx->stream = stream; 3503 } else { 3504 pipe_idx = acquire_first_split_pipe(&new_ctx->res_ctx, pool, stream); 3505 } 3506 3507 return pipe_idx != FREE_PIPE_INDEX_NOT_FOUND; 3508} 3509 3510enum dc_status resource_map_pool_resources( 3511 const struct dc *dc, 3512 struct dc_state *context, 3513 struct dc_stream_state *stream) 3514{ 3515 const struct resource_pool *pool = dc->res_pool; 3516 int i; 3517 struct dc_context *dc_ctx = dc->ctx; 3518 struct pipe_ctx *pipe_ctx = NULL; 3519 int pipe_idx = -1; 3520 bool acquired = false; 3521 3522 calculate_phy_pix_clks(stream); 3523 3524 mark_seamless_boot_stream(dc, stream); 3525 3526 if (stream->apply_seamless_boot_optimization) { 3527 pipe_idx = acquire_resource_from_hw_enabled_state( 3528 &context->res_ctx, 3529 pool, 3530 stream); 3531 if (pipe_idx < 0) 3532 /* hw resource was assigned to other stream */ 3533 stream->apply_seamless_boot_optimization = false; 3534 else 3535 acquired = true; 3536 } 3537 3538 if (!acquired) 3539 /* acquire new resources */ 3540 acquired = acquire_otg_master_pipe_for_stream(dc->current_state, 3541 context, pool, stream); 3542 3543 pipe_ctx = resource_get_otg_master_for_stream(&context->res_ctx, stream); 3544 3545 if (!pipe_ctx || pipe_ctx->stream_res.tg == NULL) 3546 return DC_NO_CONTROLLER_RESOURCE; 3547 3548 pipe_ctx->stream_res.stream_enc = 3549 dc->res_pool->funcs->find_first_free_match_stream_enc_for_link( 3550 &context->res_ctx, pool, stream); 3551 3552 if (!pipe_ctx->stream_res.stream_enc) 3553 return DC_NO_STREAM_ENC_RESOURCE; 3554 3555 update_stream_engine_usage( 3556 &context->res_ctx, pool, 3557 pipe_ctx->stream_res.stream_enc, 3558 true); 3559 3560 /* Allocate DP HPO Stream Encoder based on signal, hw capabilities 3561 * and link settings 3562 */ 3563 if (dc_is_dp_signal(stream->signal)) { 3564 if (!dc->link_srv->dp_decide_link_settings(stream, &pipe_ctx->link_config.dp_link_settings)) 3565 return DC_FAIL_DP_LINK_BANDWIDTH; 3566 if (dc->link_srv->dp_get_encoding_format( 3567 &pipe_ctx->link_config.dp_link_settings) == DP_128b_132b_ENCODING) { 3568 pipe_ctx->stream_res.hpo_dp_stream_enc = 3569 find_first_free_match_hpo_dp_stream_enc_for_link( 3570 &context->res_ctx, pool, stream); 3571 3572 if (!pipe_ctx->stream_res.hpo_dp_stream_enc) 3573 return DC_NO_STREAM_ENC_RESOURCE; 3574 3575 update_hpo_dp_stream_engine_usage( 3576 &context->res_ctx, pool, 3577 pipe_ctx->stream_res.hpo_dp_stream_enc, 3578 true); 3579 if (!add_hpo_dp_link_enc_to_ctx(&context->res_ctx, pool, pipe_ctx, stream)) 3580 return DC_NO_LINK_ENC_RESOURCE; 3581 } 3582 } 3583 3584 /* TODO: Add check if ASIC support and EDID audio */ 3585 if (!stream->converter_disable_audio && 3586 dc_is_audio_capable_signal(pipe_ctx->stream->signal) && 3587 stream->audio_info.mode_count && stream->audio_info.flags.all) { 3588 pipe_ctx->stream_res.audio = find_first_free_audio( 3589 &context->res_ctx, pool, pipe_ctx->stream_res.stream_enc->id, dc_ctx->dce_version); 3590 3591 /* 3592 * Audio assigned in order first come first get. 3593 * There are asics which has number of audio 3594 * resources less then number of pipes 3595 */ 3596 if (pipe_ctx->stream_res.audio) 3597 update_audio_usage(&context->res_ctx, pool, 3598 pipe_ctx->stream_res.audio, true); 3599 } 3600 3601 /* Add ABM to the resource if on EDP */ 3602 if (pipe_ctx->stream && dc_is_embedded_signal(pipe_ctx->stream->signal)) { 3603 if (pool->abm) 3604 pipe_ctx->stream_res.abm = pool->abm; 3605 else 3606 pipe_ctx->stream_res.abm = pool->multiple_abms[pipe_ctx->stream_res.tg->inst]; 3607 } 3608 3609 for (i = 0; i < context->stream_count; i++) 3610 if (context->streams[i] == stream) { 3611 context->stream_status[i].primary_otg_inst = pipe_ctx->stream_res.tg->inst; 3612 context->stream_status[i].stream_enc_inst = pipe_ctx->stream_res.stream_enc->stream_enc_inst; 3613 context->stream_status[i].audio_inst = 3614 pipe_ctx->stream_res.audio ? pipe_ctx->stream_res.audio->inst : -1; 3615 3616 return DC_OK; 3617 } 3618 3619 DC_ERROR("Stream %p not found in new ctx!\n", stream); 3620 return DC_ERROR_UNEXPECTED; 3621} 3622 3623bool dc_resource_is_dsc_encoding_supported(const struct dc *dc) 3624{ 3625 if (dc->res_pool == NULL) 3626 return false; 3627 3628 return dc->res_pool->res_cap->num_dsc > 0; 3629} 3630 3631static bool planes_changed_for_existing_stream(struct dc_state *context, 3632 struct dc_stream_state *stream, 3633 const struct dc_validation_set set[], 3634 int set_count) 3635{ 3636 int i, j; 3637 struct dc_stream_status *stream_status = NULL; 3638 3639 for (i = 0; i < context->stream_count; i++) { 3640 if (context->streams[i] == stream) { 3641 stream_status = &context->stream_status[i]; 3642 break; 3643 } 3644 } 3645 3646 if (!stream_status) 3647 ASSERT(0); 3648 3649 for (i = 0; i < set_count; i++) 3650 if (set[i].stream == stream) 3651 break; 3652 3653 if (i == set_count) 3654 ASSERT(0); 3655 3656 if (set[i].plane_count != stream_status->plane_count) 3657 return true; 3658 3659 for (j = 0; j < set[i].plane_count; j++) 3660 if (set[i].plane_states[j] != stream_status->plane_states[j]) 3661 return true; 3662 3663 return false; 3664} 3665 3666static bool add_all_planes_for_stream( 3667 const struct dc *dc, 3668 struct dc_stream_state *stream, 3669 const struct dc_validation_set set[], 3670 int set_count, 3671 struct dc_state *state) 3672{ 3673 int i, j; 3674 3675 for (i = 0; i < set_count; i++) 3676 if (set[i].stream == stream) 3677 break; 3678 3679 if (i == set_count) { 3680 dm_error("Stream %p not found in set!\n", stream); 3681 return false; 3682 } 3683 3684 for (j = 0; j < set[i].plane_count; j++) 3685 if (!dc_state_add_plane(dc, stream, set[i].plane_states[j], state)) 3686 return false; 3687 3688 return true; 3689} 3690 3691/** 3692 * dc_validate_with_context - Validate and update the potential new stream in the context object 3693 * 3694 * @dc: Used to get the current state status 3695 * @set: An array of dc_validation_set with all the current streams reference 3696 * @set_count: Total of streams 3697 * @context: New context 3698 * @fast_validate: Enable or disable fast validation 3699 * 3700 * This function updates the potential new stream in the context object. It 3701 * creates multiple lists for the add, remove, and unchanged streams. In 3702 * particular, if the unchanged streams have a plane that changed, it is 3703 * necessary to remove all planes from the unchanged streams. In summary, this 3704 * function is responsible for validating the new context. 3705 * 3706 * Return: 3707 * In case of success, return DC_OK (1), otherwise, return a DC error. 3708 */ 3709enum dc_status dc_validate_with_context(struct dc *dc, 3710 const struct dc_validation_set set[], 3711 int set_count, 3712 struct dc_state *context, 3713 bool fast_validate) 3714{ 3715 struct dc_stream_state *unchanged_streams[MAX_PIPES] = { 0 }; 3716 struct dc_stream_state *del_streams[MAX_PIPES] = { 0 }; 3717 struct dc_stream_state *add_streams[MAX_PIPES] = { 0 }; 3718 int old_stream_count = context->stream_count; 3719 enum dc_status res = DC_ERROR_UNEXPECTED; 3720 int unchanged_streams_count = 0; 3721 int del_streams_count = 0; 3722 int add_streams_count = 0; 3723 bool found = false; 3724 int i, j, k; 3725 3726 DC_LOGGER_INIT(dc->ctx->logger); 3727 3728 /* First build a list of streams to be remove from current context */ 3729 for (i = 0; i < old_stream_count; i++) { 3730 struct dc_stream_state *stream = context->streams[i]; 3731 3732 for (j = 0; j < set_count; j++) { 3733 if (stream == set[j].stream) { 3734 found = true; 3735 break; 3736 } 3737 } 3738 3739 if (!found) 3740 del_streams[del_streams_count++] = stream; 3741 3742 found = false; 3743 } 3744 3745 /* Second, build a list of new streams */ 3746 for (i = 0; i < set_count; i++) { 3747 struct dc_stream_state *stream = set[i].stream; 3748 3749 for (j = 0; j < old_stream_count; j++) { 3750 if (stream == context->streams[j]) { 3751 found = true; 3752 break; 3753 } 3754 } 3755 3756 if (!found) 3757 add_streams[add_streams_count++] = stream; 3758 3759 found = false; 3760 } 3761 3762 /* Build a list of unchanged streams which is necessary for handling 3763 * planes change such as added, removed, and updated. 3764 */ 3765 for (i = 0; i < set_count; i++) { 3766 /* Check if stream is part of the delete list */ 3767 for (j = 0; j < del_streams_count; j++) { 3768 if (set[i].stream == del_streams[j]) { 3769 found = true; 3770 break; 3771 } 3772 } 3773 3774 if (!found) { 3775 /* Check if stream is part of the add list */ 3776 for (j = 0; j < add_streams_count; j++) { 3777 if (set[i].stream == add_streams[j]) { 3778 found = true; 3779 break; 3780 } 3781 } 3782 } 3783 3784 if (!found) 3785 unchanged_streams[unchanged_streams_count++] = set[i].stream; 3786 3787 found = false; 3788 } 3789 3790 /* Remove all planes for unchanged streams if planes changed */ 3791 for (i = 0; i < unchanged_streams_count; i++) { 3792 if (planes_changed_for_existing_stream(context, 3793 unchanged_streams[i], 3794 set, 3795 set_count)) { 3796 3797 if (!dc_state_rem_all_planes_for_stream(dc, 3798 unchanged_streams[i], 3799 context)) { 3800 res = DC_FAIL_DETACH_SURFACES; 3801 goto fail; 3802 } 3803 } 3804 } 3805 3806 /* Remove all planes for removed streams and then remove the streams */ 3807 for (i = 0; i < del_streams_count; i++) { 3808 /* Need to cpy the dwb data from the old stream in order to efc to work */ 3809 if (del_streams[i]->num_wb_info > 0) { 3810 for (j = 0; j < add_streams_count; j++) { 3811 if (del_streams[i]->sink == add_streams[j]->sink) { 3812 add_streams[j]->num_wb_info = del_streams[i]->num_wb_info; 3813 for (k = 0; k < del_streams[i]->num_wb_info; k++) 3814 add_streams[j]->writeback_info[k] = del_streams[i]->writeback_info[k]; 3815 } 3816 } 3817 } 3818 3819 if (dc_state_get_stream_subvp_type(context, del_streams[i]) == SUBVP_PHANTOM) { 3820 /* remove phantoms specifically */ 3821 if (!dc_state_rem_all_phantom_planes_for_stream(dc, del_streams[i], context, true)) { 3822 res = DC_FAIL_DETACH_SURFACES; 3823 goto fail; 3824 } 3825 3826 res = dc_state_remove_phantom_stream(dc, context, del_streams[i]); 3827 dc_state_release_phantom_stream(dc, context, del_streams[i]); 3828 } else { 3829 if (!dc_state_rem_all_planes_for_stream(dc, del_streams[i], context)) { 3830 res = DC_FAIL_DETACH_SURFACES; 3831 goto fail; 3832 } 3833 3834 res = dc_state_remove_stream(dc, context, del_streams[i]); 3835 } 3836 3837 if (res != DC_OK) 3838 goto fail; 3839 } 3840 3841 /* Swap seamless boot stream to pipe 0 (if needed) to ensure pipe_ctx 3842 * matches. This may change in the future if seamless_boot_stream can be 3843 * multiple. 3844 */ 3845 for (i = 0; i < add_streams_count; i++) { 3846 mark_seamless_boot_stream(dc, add_streams[i]); 3847 if (add_streams[i]->apply_seamless_boot_optimization && i != 0) { 3848 struct dc_stream_state *temp = add_streams[0]; 3849 3850 add_streams[0] = add_streams[i]; 3851 add_streams[i] = temp; 3852 break; 3853 } 3854 } 3855 3856 /* Add new streams and then add all planes for the new stream */ 3857 for (i = 0; i < add_streams_count; i++) { 3858 calculate_phy_pix_clks(add_streams[i]); 3859 res = dc_state_add_stream(dc, context, add_streams[i]); 3860 if (res != DC_OK) 3861 goto fail; 3862 3863 if (!add_all_planes_for_stream(dc, add_streams[i], set, set_count, context)) { 3864 res = DC_FAIL_ATTACH_SURFACES; 3865 goto fail; 3866 } 3867 } 3868 3869 /* Add all planes for unchanged streams if planes changed */ 3870 for (i = 0; i < unchanged_streams_count; i++) { 3871 if (planes_changed_for_existing_stream(context, 3872 unchanged_streams[i], 3873 set, 3874 set_count)) { 3875 if (!add_all_planes_for_stream(dc, unchanged_streams[i], set, set_count, context)) { 3876 res = DC_FAIL_ATTACH_SURFACES; 3877 goto fail; 3878 } 3879 } 3880 } 3881 3882 res = dc_validate_global_state(dc, context, fast_validate); 3883 3884fail: 3885 if (res != DC_OK) 3886 DC_LOG_WARNING("%s:resource validation failed, dc_status:%d\n", 3887 __func__, 3888 res); 3889 3890 return res; 3891} 3892 3893/** 3894 * dc_validate_global_state() - Determine if hardware can support a given state 3895 * 3896 * @dc: dc struct for this driver 3897 * @new_ctx: state to be validated 3898 * @fast_validate: set to true if only yes/no to support matters 3899 * 3900 * Checks hardware resource availability and bandwidth requirement. 3901 * 3902 * Return: 3903 * DC_OK if the result can be programmed. Otherwise, an error code. 3904 */ 3905enum dc_status dc_validate_global_state( 3906 struct dc *dc, 3907 struct dc_state *new_ctx, 3908 bool fast_validate) 3909{ 3910 enum dc_status result = DC_ERROR_UNEXPECTED; 3911 int i, j; 3912 3913 if (!new_ctx) 3914 return DC_ERROR_UNEXPECTED; 3915 3916 if (dc->res_pool->funcs->validate_global) { 3917 result = dc->res_pool->funcs->validate_global(dc, new_ctx); 3918 if (result != DC_OK) 3919 return result; 3920 } 3921 3922 for (i = 0; i < new_ctx->stream_count; i++) { 3923 struct dc_stream_state *stream = new_ctx->streams[i]; 3924 3925 for (j = 0; j < dc->res_pool->pipe_count; j++) { 3926 struct pipe_ctx *pipe_ctx = &new_ctx->res_ctx.pipe_ctx[j]; 3927 3928 if (pipe_ctx->stream != stream) 3929 continue; 3930 3931 if (dc->res_pool->funcs->patch_unknown_plane_state && 3932 pipe_ctx->plane_state && 3933 pipe_ctx->plane_state->tiling_info.gfx9.swizzle == DC_SW_UNKNOWN) { 3934 result = dc->res_pool->funcs->patch_unknown_plane_state(pipe_ctx->plane_state); 3935 if (result != DC_OK) 3936 return result; 3937 } 3938 3939 /* Switch to dp clock source only if there is 3940 * no non dp stream that shares the same timing 3941 * with the dp stream. 3942 */ 3943 if (dc_is_dp_signal(pipe_ctx->stream->signal) && 3944 !find_pll_sharable_stream(stream, new_ctx)) { 3945 3946 resource_unreference_clock_source( 3947 &new_ctx->res_ctx, 3948 dc->res_pool, 3949 pipe_ctx->clock_source); 3950 3951 pipe_ctx->clock_source = dc->res_pool->dp_clock_source; 3952 resource_reference_clock_source( 3953 &new_ctx->res_ctx, 3954 dc->res_pool, 3955 pipe_ctx->clock_source); 3956 } 3957 } 3958 } 3959 3960 result = resource_build_scaling_params_for_context(dc, new_ctx); 3961 3962 if (result == DC_OK) 3963 if (!dc->res_pool->funcs->validate_bandwidth(dc, new_ctx, fast_validate)) 3964 result = DC_FAIL_BANDWIDTH_VALIDATE; 3965 3966 /* 3967 * Only update link encoder to stream assignment after bandwidth validation passed. 3968 * TODO: Split out assignment and validation. 3969 */ 3970 if (result == DC_OK && dc->res_pool->funcs->link_encs_assign && fast_validate == false) 3971 dc->res_pool->funcs->link_encs_assign( 3972 dc, new_ctx, new_ctx->streams, new_ctx->stream_count); 3973 3974 return result; 3975} 3976 3977static void patch_gamut_packet_checksum( 3978 struct dc_info_packet *gamut_packet) 3979{ 3980 /* For gamut we recalc checksum */ 3981 if (gamut_packet->valid) { 3982 uint8_t chk_sum = 0; 3983 uint8_t *ptr; 3984 uint8_t i; 3985 3986 /*start of the Gamut data. */ 3987 ptr = &gamut_packet->sb[3]; 3988 3989 for (i = 0; i <= gamut_packet->sb[1]; i++) 3990 chk_sum += ptr[i]; 3991 3992 gamut_packet->sb[2] = (uint8_t) (0x100 - chk_sum); 3993 } 3994} 3995 3996static void set_avi_info_frame( 3997 struct dc_info_packet *info_packet, 3998 struct pipe_ctx *pipe_ctx) 3999{ 4000 struct dc_stream_state *stream = pipe_ctx->stream; 4001 enum dc_color_space color_space = COLOR_SPACE_UNKNOWN; 4002 uint32_t pixel_encoding = 0; 4003 enum scanning_type scan_type = SCANNING_TYPE_NODATA; 4004 enum dc_aspect_ratio aspect = ASPECT_RATIO_NO_DATA; 4005 uint8_t *check_sum = NULL; 4006 uint8_t byte_index = 0; 4007 union hdmi_info_packet hdmi_info; 4008 unsigned int vic = pipe_ctx->stream->timing.vic; 4009 unsigned int rid = pipe_ctx->stream->timing.rid; 4010 unsigned int fr_ind = pipe_ctx->stream->timing.fr_index; 4011 enum dc_timing_3d_format format; 4012 4013 memset(&hdmi_info, 0, sizeof(union hdmi_info_packet)); 4014 4015 color_space = pipe_ctx->stream->output_color_space; 4016 if (color_space == COLOR_SPACE_UNKNOWN) 4017 color_space = (stream->timing.pixel_encoding == PIXEL_ENCODING_RGB) ? 4018 COLOR_SPACE_SRGB:COLOR_SPACE_YCBCR709; 4019 4020 /* Initialize header */ 4021 hdmi_info.bits.header.info_frame_type = HDMI_INFOFRAME_TYPE_AVI; 4022 /* InfoFrameVersion_3 is defined by CEA861F (Section 6.4), but shall 4023 * not be used in HDMI 2.0 (Section 10.1) */ 4024 hdmi_info.bits.header.version = 2; 4025 hdmi_info.bits.header.length = HDMI_AVI_INFOFRAME_SIZE; 4026 4027 /* 4028 * IDO-defined (Y2,Y1,Y0 = 1,1,1) shall not be used by devices built 4029 * according to HDMI 2.0 spec (Section 10.1) 4030 */ 4031 4032 switch (stream->timing.pixel_encoding) { 4033 case PIXEL_ENCODING_YCBCR422: 4034 pixel_encoding = 1; 4035 break; 4036 4037 case PIXEL_ENCODING_YCBCR444: 4038 pixel_encoding = 2; 4039 break; 4040 case PIXEL_ENCODING_YCBCR420: 4041 pixel_encoding = 3; 4042 break; 4043 4044 case PIXEL_ENCODING_RGB: 4045 default: 4046 pixel_encoding = 0; 4047 } 4048 4049 /* Y0_Y1_Y2 : The pixel encoding */ 4050 /* H14b AVI InfoFrame has extension on Y-field from 2 bits to 3 bits */ 4051 hdmi_info.bits.Y0_Y1_Y2 = pixel_encoding; 4052 4053 /* A0 = 1 Active Format Information valid */ 4054 hdmi_info.bits.A0 = ACTIVE_FORMAT_VALID; 4055 4056 /* B0, B1 = 3; Bar info data is valid */ 4057 hdmi_info.bits.B0_B1 = BAR_INFO_BOTH_VALID; 4058 4059 hdmi_info.bits.SC0_SC1 = PICTURE_SCALING_UNIFORM; 4060 4061 /* S0, S1 : Underscan / Overscan */ 4062 /* TODO: un-hardcode scan type */ 4063 scan_type = SCANNING_TYPE_UNDERSCAN; 4064 hdmi_info.bits.S0_S1 = scan_type; 4065 4066 /* C0, C1 : Colorimetry */ 4067 switch (color_space) { 4068 case COLOR_SPACE_YCBCR709: 4069 case COLOR_SPACE_YCBCR709_LIMITED: 4070 hdmi_info.bits.C0_C1 = COLORIMETRY_ITU709; 4071 break; 4072 case COLOR_SPACE_YCBCR601: 4073 case COLOR_SPACE_YCBCR601_LIMITED: 4074 hdmi_info.bits.C0_C1 = COLORIMETRY_ITU601; 4075 break; 4076 case COLOR_SPACE_2020_RGB_FULLRANGE: 4077 case COLOR_SPACE_2020_RGB_LIMITEDRANGE: 4078 case COLOR_SPACE_2020_YCBCR: 4079 hdmi_info.bits.EC0_EC2 = COLORIMETRYEX_BT2020RGBYCBCR; 4080 hdmi_info.bits.C0_C1 = COLORIMETRY_EXTENDED; 4081 break; 4082 case COLOR_SPACE_ADOBERGB: 4083 hdmi_info.bits.EC0_EC2 = COLORIMETRYEX_ADOBERGB; 4084 hdmi_info.bits.C0_C1 = COLORIMETRY_EXTENDED; 4085 break; 4086 case COLOR_SPACE_SRGB: 4087 default: 4088 hdmi_info.bits.C0_C1 = COLORIMETRY_NO_DATA; 4089 break; 4090 } 4091 4092 if (pixel_encoding && color_space == COLOR_SPACE_2020_YCBCR && 4093 stream->out_transfer_func.tf == TRANSFER_FUNCTION_GAMMA22) { 4094 hdmi_info.bits.EC0_EC2 = 0; 4095 hdmi_info.bits.C0_C1 = COLORIMETRY_ITU709; 4096 } 4097 4098 /* TODO: un-hardcode aspect ratio */ 4099 aspect = stream->timing.aspect_ratio; 4100 4101 switch (aspect) { 4102 case ASPECT_RATIO_4_3: 4103 case ASPECT_RATIO_16_9: 4104 hdmi_info.bits.M0_M1 = aspect; 4105 break; 4106 4107 case ASPECT_RATIO_NO_DATA: 4108 case ASPECT_RATIO_64_27: 4109 case ASPECT_RATIO_256_135: 4110 default: 4111 hdmi_info.bits.M0_M1 = 0; 4112 } 4113 4114 /* Active Format Aspect ratio - same as Picture Aspect Ratio. */ 4115 hdmi_info.bits.R0_R3 = ACTIVE_FORMAT_ASPECT_RATIO_SAME_AS_PICTURE; 4116 4117 switch (stream->content_type) { 4118 case DISPLAY_CONTENT_TYPE_NO_DATA: 4119 hdmi_info.bits.CN0_CN1 = 0; 4120 hdmi_info.bits.ITC = 1; 4121 break; 4122 case DISPLAY_CONTENT_TYPE_GRAPHICS: 4123 hdmi_info.bits.CN0_CN1 = 0; 4124 hdmi_info.bits.ITC = 1; 4125 break; 4126 case DISPLAY_CONTENT_TYPE_PHOTO: 4127 hdmi_info.bits.CN0_CN1 = 1; 4128 hdmi_info.bits.ITC = 1; 4129 break; 4130 case DISPLAY_CONTENT_TYPE_CINEMA: 4131 hdmi_info.bits.CN0_CN1 = 2; 4132 hdmi_info.bits.ITC = 1; 4133 break; 4134 case DISPLAY_CONTENT_TYPE_GAME: 4135 hdmi_info.bits.CN0_CN1 = 3; 4136 hdmi_info.bits.ITC = 1; 4137 break; 4138 } 4139 4140 if (stream->qs_bit == 1) { 4141 if (color_space == COLOR_SPACE_SRGB || 4142 color_space == COLOR_SPACE_2020_RGB_FULLRANGE) 4143 hdmi_info.bits.Q0_Q1 = RGB_QUANTIZATION_FULL_RANGE; 4144 else if (color_space == COLOR_SPACE_SRGB_LIMITED || 4145 color_space == COLOR_SPACE_2020_RGB_LIMITEDRANGE) 4146 hdmi_info.bits.Q0_Q1 = RGB_QUANTIZATION_LIMITED_RANGE; 4147 else 4148 hdmi_info.bits.Q0_Q1 = RGB_QUANTIZATION_DEFAULT_RANGE; 4149 } else 4150 hdmi_info.bits.Q0_Q1 = RGB_QUANTIZATION_DEFAULT_RANGE; 4151 4152 /* TODO : We should handle YCC quantization */ 4153 /* but we do not have matrix calculation */ 4154 hdmi_info.bits.YQ0_YQ1 = YYC_QUANTIZATION_LIMITED_RANGE; 4155 4156 ///VIC 4157 if (pipe_ctx->stream->timing.hdmi_vic != 0) 4158 vic = 0; 4159 format = stream->timing.timing_3d_format; 4160 /*todo, add 3DStereo support*/ 4161 if (format != TIMING_3D_FORMAT_NONE) { 4162 // Based on HDMI specs hdmi vic needs to be converted to cea vic when 3D is enabled 4163 switch (pipe_ctx->stream->timing.hdmi_vic) { 4164 case 1: 4165 vic = 95; 4166 break; 4167 case 2: 4168 vic = 94; 4169 break; 4170 case 3: 4171 vic = 93; 4172 break; 4173 case 4: 4174 vic = 98; 4175 break; 4176 default: 4177 break; 4178 } 4179 } 4180 /* If VIC >= 128, the Source shall use AVI InfoFrame Version 3*/ 4181 hdmi_info.bits.VIC0_VIC7 = vic; 4182 if (vic >= 128) 4183 hdmi_info.bits.header.version = 3; 4184 /* If (C1, C0)=(1, 1) and (EC2, EC1, EC0)=(1, 1, 1), 4185 * the Source shall use 20 AVI InfoFrame Version 4 4186 */ 4187 if (hdmi_info.bits.C0_C1 == COLORIMETRY_EXTENDED && 4188 hdmi_info.bits.EC0_EC2 == COLORIMETRYEX_RESERVED) { 4189 hdmi_info.bits.header.version = 4; 4190 hdmi_info.bits.header.length = 14; 4191 } 4192 4193 if (rid != 0 && fr_ind != 0) { 4194 hdmi_info.bits.header.version = 5; 4195 hdmi_info.bits.header.length = 15; 4196 4197 hdmi_info.bits.FR0_FR3 = fr_ind & 0xF; 4198 hdmi_info.bits.FR4 = (fr_ind >> 4) & 0x1; 4199 hdmi_info.bits.RID0_RID5 = rid; 4200 } 4201 4202 /* pixel repetition 4203 * PR0 - PR3 start from 0 whereas pHwPathMode->mode.timing.flags.pixel 4204 * repetition start from 1 */ 4205 hdmi_info.bits.PR0_PR3 = 0; 4206 4207 /* Bar Info 4208 * barTop: Line Number of End of Top Bar. 4209 * barBottom: Line Number of Start of Bottom Bar. 4210 * barLeft: Pixel Number of End of Left Bar. 4211 * barRight: Pixel Number of Start of Right Bar. */ 4212 hdmi_info.bits.bar_top = stream->timing.v_border_top; 4213 hdmi_info.bits.bar_bottom = (stream->timing.v_total 4214 - stream->timing.v_border_bottom + 1); 4215 hdmi_info.bits.bar_left = stream->timing.h_border_left; 4216 hdmi_info.bits.bar_right = (stream->timing.h_total 4217 - stream->timing.h_border_right + 1); 4218 4219 /* Additional Colorimetry Extension 4220 * Used in conduction with C0-C1 and EC0-EC2 4221 * 0 = DCI-P3 RGB (D65) 4222 * 1 = DCI-P3 RGB (theater) 4223 */ 4224 hdmi_info.bits.ACE0_ACE3 = 0; 4225 4226 /* check_sum - Calculate AFMT_AVI_INFO0 ~ AFMT_AVI_INFO3 */ 4227 check_sum = &hdmi_info.packet_raw_data.sb[0]; 4228 4229 *check_sum = HDMI_INFOFRAME_TYPE_AVI + hdmi_info.bits.header.length + hdmi_info.bits.header.version; 4230 4231 for (byte_index = 1; byte_index <= hdmi_info.bits.header.length; byte_index++) 4232 *check_sum += hdmi_info.packet_raw_data.sb[byte_index]; 4233 4234 /* one byte complement */ 4235 *check_sum = (uint8_t) (0x100 - *check_sum); 4236 4237 /* Store in hw_path_mode */ 4238 info_packet->hb0 = hdmi_info.packet_raw_data.hb0; 4239 info_packet->hb1 = hdmi_info.packet_raw_data.hb1; 4240 info_packet->hb2 = hdmi_info.packet_raw_data.hb2; 4241 4242 for (byte_index = 0; byte_index < sizeof(hdmi_info.packet_raw_data.sb); byte_index++) 4243 info_packet->sb[byte_index] = hdmi_info.packet_raw_data.sb[byte_index]; 4244 4245 info_packet->valid = true; 4246} 4247 4248static void set_vendor_info_packet( 4249 struct dc_info_packet *info_packet, 4250 struct dc_stream_state *stream) 4251{ 4252 /* SPD info packet for FreeSync */ 4253 4254 /* Check if Freesync is supported. Return if false. If true, 4255 * set the corresponding bit in the info packet 4256 */ 4257 if (!stream->vsp_infopacket.valid) 4258 return; 4259 4260 *info_packet = stream->vsp_infopacket; 4261} 4262 4263static void set_spd_info_packet( 4264 struct dc_info_packet *info_packet, 4265 struct dc_stream_state *stream) 4266{ 4267 /* SPD info packet for FreeSync */ 4268 4269 /* Check if Freesync is supported. Return if false. If true, 4270 * set the corresponding bit in the info packet 4271 */ 4272 if (!stream->vrr_infopacket.valid) 4273 return; 4274 4275 *info_packet = stream->vrr_infopacket; 4276} 4277 4278static void set_hdr_static_info_packet( 4279 struct dc_info_packet *info_packet, 4280 struct dc_stream_state *stream) 4281{ 4282 /* HDR Static Metadata info packet for HDR10 */ 4283 4284 if (!stream->hdr_static_metadata.valid || 4285 stream->use_dynamic_meta) 4286 return; 4287 4288 *info_packet = stream->hdr_static_metadata; 4289} 4290 4291static void set_vsc_info_packet( 4292 struct dc_info_packet *info_packet, 4293 struct dc_stream_state *stream) 4294{ 4295 if (!stream->vsc_infopacket.valid) 4296 return; 4297 4298 *info_packet = stream->vsc_infopacket; 4299} 4300static void set_hfvs_info_packet( 4301 struct dc_info_packet *info_packet, 4302 struct dc_stream_state *stream) 4303{ 4304 if (!stream->hfvsif_infopacket.valid) 4305 return; 4306 4307 *info_packet = stream->hfvsif_infopacket; 4308} 4309 4310static void adaptive_sync_override_dp_info_packets_sdp_line_num( 4311 const struct dc_crtc_timing *timing, 4312 struct enc_sdp_line_num *sdp_line_num, 4313 struct _vcs_dpi_display_pipe_dest_params_st *pipe_dlg_param) 4314{ 4315 uint32_t asic_blank_start = 0; 4316 uint32_t asic_blank_end = 0; 4317 uint32_t v_update = 0; 4318 4319 const struct dc_crtc_timing *tg = timing; 4320 4321 /* blank_start = frame end - front porch */ 4322 asic_blank_start = tg->v_total - tg->v_front_porch; 4323 4324 /* blank_end = blank_start - active */ 4325 asic_blank_end = (asic_blank_start - tg->v_border_bottom - 4326 tg->v_addressable - tg->v_border_top); 4327 4328 if (pipe_dlg_param->vstartup_start > asic_blank_end) { 4329 v_update = (tg->v_total - (pipe_dlg_param->vstartup_start - asic_blank_end)); 4330 sdp_line_num->adaptive_sync_line_num_valid = true; 4331 sdp_line_num->adaptive_sync_line_num = (tg->v_total - v_update - 1); 4332 } else { 4333 sdp_line_num->adaptive_sync_line_num_valid = false; 4334 sdp_line_num->adaptive_sync_line_num = 0; 4335 } 4336} 4337 4338static void set_adaptive_sync_info_packet( 4339 struct dc_info_packet *info_packet, 4340 const struct dc_stream_state *stream, 4341 struct encoder_info_frame *info_frame, 4342 struct _vcs_dpi_display_pipe_dest_params_st *pipe_dlg_param) 4343{ 4344 if (!stream->adaptive_sync_infopacket.valid) 4345 return; 4346 4347 adaptive_sync_override_dp_info_packets_sdp_line_num( 4348 &stream->timing, 4349 &info_frame->sdp_line_num, 4350 pipe_dlg_param); 4351 4352 *info_packet = stream->adaptive_sync_infopacket; 4353} 4354 4355static void set_vtem_info_packet( 4356 struct dc_info_packet *info_packet, 4357 struct dc_stream_state *stream) 4358{ 4359 if (!stream->vtem_infopacket.valid) 4360 return; 4361 4362 *info_packet = stream->vtem_infopacket; 4363} 4364 4365struct clock_source *dc_resource_find_first_free_pll( 4366 struct resource_context *res_ctx, 4367 const struct resource_pool *pool) 4368{ 4369 int i; 4370 4371 for (i = 0; i < pool->clk_src_count; ++i) { 4372 if (res_ctx->clock_source_ref_count[i] == 0) 4373 return pool->clock_sources[i]; 4374 } 4375 4376 return NULL; 4377} 4378 4379void resource_build_info_frame(struct pipe_ctx *pipe_ctx) 4380{ 4381 enum signal_type signal = SIGNAL_TYPE_NONE; 4382 struct encoder_info_frame *info = &pipe_ctx->stream_res.encoder_info_frame; 4383 4384 /* default all packets to invalid */ 4385 info->avi.valid = false; 4386 info->gamut.valid = false; 4387 info->vendor.valid = false; 4388 info->spd.valid = false; 4389 info->hdrsmd.valid = false; 4390 info->vsc.valid = false; 4391 info->hfvsif.valid = false; 4392 info->vtem.valid = false; 4393 info->adaptive_sync.valid = false; 4394 signal = pipe_ctx->stream->signal; 4395 4396 /* HDMi and DP have different info packets*/ 4397 if (dc_is_hdmi_signal(signal)) { 4398 set_avi_info_frame(&info->avi, pipe_ctx); 4399 4400 set_vendor_info_packet(&info->vendor, pipe_ctx->stream); 4401 set_hfvs_info_packet(&info->hfvsif, pipe_ctx->stream); 4402 set_vtem_info_packet(&info->vtem, pipe_ctx->stream); 4403 4404 set_spd_info_packet(&info->spd, pipe_ctx->stream); 4405 4406 set_hdr_static_info_packet(&info->hdrsmd, pipe_ctx->stream); 4407 4408 } else if (dc_is_dp_signal(signal)) { 4409 set_vsc_info_packet(&info->vsc, pipe_ctx->stream); 4410 4411 set_spd_info_packet(&info->spd, pipe_ctx->stream); 4412 4413 set_hdr_static_info_packet(&info->hdrsmd, pipe_ctx->stream); 4414 set_adaptive_sync_info_packet(&info->adaptive_sync, 4415 pipe_ctx->stream, 4416 info, 4417 &pipe_ctx->pipe_dlg_param); 4418 } 4419 4420 patch_gamut_packet_checksum(&info->gamut); 4421} 4422 4423enum dc_status resource_map_clock_resources( 4424 const struct dc *dc, 4425 struct dc_state *context, 4426 struct dc_stream_state *stream) 4427{ 4428 /* acquire new resources */ 4429 const struct resource_pool *pool = dc->res_pool; 4430 struct pipe_ctx *pipe_ctx = resource_get_otg_master_for_stream( 4431 &context->res_ctx, stream); 4432 4433 if (!pipe_ctx) 4434 return DC_ERROR_UNEXPECTED; 4435 4436 if (dc_is_dp_signal(pipe_ctx->stream->signal) 4437 || pipe_ctx->stream->signal == SIGNAL_TYPE_VIRTUAL) 4438 pipe_ctx->clock_source = pool->dp_clock_source; 4439 else { 4440 pipe_ctx->clock_source = NULL; 4441 4442 if (!dc->config.disable_disp_pll_sharing) 4443 pipe_ctx->clock_source = resource_find_used_clk_src_for_sharing( 4444 &context->res_ctx, 4445 pipe_ctx); 4446 4447 if (pipe_ctx->clock_source == NULL) 4448 pipe_ctx->clock_source = 4449 dc_resource_find_first_free_pll( 4450 &context->res_ctx, 4451 pool); 4452 } 4453 4454 if (pipe_ctx->clock_source == NULL) 4455 return DC_NO_CLOCK_SOURCE_RESOURCE; 4456 4457 resource_reference_clock_source( 4458 &context->res_ctx, pool, 4459 pipe_ctx->clock_source); 4460 4461 return DC_OK; 4462} 4463 4464/* 4465 * Note: We need to disable output if clock sources change, 4466 * since bios does optimization and doesn't apply if changing 4467 * PHY when not already disabled. 4468 */ 4469bool pipe_need_reprogram( 4470 struct pipe_ctx *pipe_ctx_old, 4471 struct pipe_ctx *pipe_ctx) 4472{ 4473 if (!pipe_ctx_old->stream) 4474 return false; 4475 4476 if (pipe_ctx_old->stream->sink != pipe_ctx->stream->sink) 4477 return true; 4478 4479 if (pipe_ctx_old->stream->signal != pipe_ctx->stream->signal) 4480 return true; 4481 4482 if (pipe_ctx_old->stream_res.audio != pipe_ctx->stream_res.audio) 4483 return true; 4484 4485 if (pipe_ctx_old->clock_source != pipe_ctx->clock_source 4486 && pipe_ctx_old->stream != pipe_ctx->stream) 4487 return true; 4488 4489 if (pipe_ctx_old->stream_res.stream_enc != pipe_ctx->stream_res.stream_enc) 4490 return true; 4491 4492 if (dc_is_timing_changed(pipe_ctx_old->stream, pipe_ctx->stream)) 4493 return true; 4494 4495 if (pipe_ctx_old->stream->dpms_off != pipe_ctx->stream->dpms_off) 4496 return true; 4497 4498 if (false == pipe_ctx_old->stream->link->link_state_valid && 4499 false == pipe_ctx_old->stream->dpms_off) 4500 return true; 4501 4502 if (pipe_ctx_old->stream_res.dsc != pipe_ctx->stream_res.dsc) 4503 return true; 4504 4505 if (pipe_ctx_old->stream_res.hpo_dp_stream_enc != pipe_ctx->stream_res.hpo_dp_stream_enc) 4506 return true; 4507 if (pipe_ctx_old->link_res.hpo_dp_link_enc != pipe_ctx->link_res.hpo_dp_link_enc) 4508 return true; 4509 4510 /* DIG link encoder resource assignment for stream changed. */ 4511 if (pipe_ctx_old->stream->ctx->dc->res_pool->funcs->link_encs_assign) { 4512 bool need_reprogram = false; 4513 struct dc *dc = pipe_ctx_old->stream->ctx->dc; 4514 struct link_encoder *link_enc_prev = 4515 link_enc_cfg_get_link_enc_used_by_stream_current(dc, pipe_ctx_old->stream); 4516 4517 if (link_enc_prev != pipe_ctx->stream->link_enc) 4518 need_reprogram = true; 4519 4520 return need_reprogram; 4521 } 4522 4523 return false; 4524} 4525 4526void resource_build_bit_depth_reduction_params(struct dc_stream_state *stream, 4527 struct bit_depth_reduction_params *fmt_bit_depth) 4528{ 4529 enum dc_dither_option option = stream->dither_option; 4530 enum dc_pixel_encoding pixel_encoding = 4531 stream->timing.pixel_encoding; 4532 4533 memset(fmt_bit_depth, 0, sizeof(*fmt_bit_depth)); 4534 4535 if (option == DITHER_OPTION_DEFAULT) { 4536 switch (stream->timing.display_color_depth) { 4537 case COLOR_DEPTH_666: 4538 option = DITHER_OPTION_SPATIAL6; 4539 break; 4540 case COLOR_DEPTH_888: 4541 option = DITHER_OPTION_SPATIAL8; 4542 break; 4543 case COLOR_DEPTH_101010: 4544 option = DITHER_OPTION_TRUN10; 4545 break; 4546 default: 4547 option = DITHER_OPTION_DISABLE; 4548 } 4549 } 4550 4551 if (option == DITHER_OPTION_DISABLE) 4552 return; 4553 4554 if (option == DITHER_OPTION_TRUN6) { 4555 fmt_bit_depth->flags.TRUNCATE_ENABLED = 1; 4556 fmt_bit_depth->flags.TRUNCATE_DEPTH = 0; 4557 } else if (option == DITHER_OPTION_TRUN8 || 4558 option == DITHER_OPTION_TRUN8_SPATIAL6 || 4559 option == DITHER_OPTION_TRUN8_FM6) { 4560 fmt_bit_depth->flags.TRUNCATE_ENABLED = 1; 4561 fmt_bit_depth->flags.TRUNCATE_DEPTH = 1; 4562 } else if (option == DITHER_OPTION_TRUN10 || 4563 option == DITHER_OPTION_TRUN10_SPATIAL6 || 4564 option == DITHER_OPTION_TRUN10_SPATIAL8 || 4565 option == DITHER_OPTION_TRUN10_FM8 || 4566 option == DITHER_OPTION_TRUN10_FM6 || 4567 option == DITHER_OPTION_TRUN10_SPATIAL8_FM6) { 4568 fmt_bit_depth->flags.TRUNCATE_ENABLED = 1; 4569 fmt_bit_depth->flags.TRUNCATE_DEPTH = 2; 4570 if (option == DITHER_OPTION_TRUN10) 4571 fmt_bit_depth->flags.TRUNCATE_MODE = 1; 4572 } 4573 4574 /* special case - Formatter can only reduce by 4 bits at most. 4575 * When reducing from 12 to 6 bits, 4576 * HW recommends we use trunc with round mode 4577 * (if we did nothing, trunc to 10 bits would be used) 4578 * note that any 12->10 bit reduction is ignored prior to DCE8, 4579 * as the input was 10 bits. 4580 */ 4581 if (option == DITHER_OPTION_SPATIAL6_FRAME_RANDOM || 4582 option == DITHER_OPTION_SPATIAL6 || 4583 option == DITHER_OPTION_FM6) { 4584 fmt_bit_depth->flags.TRUNCATE_ENABLED = 1; 4585 fmt_bit_depth->flags.TRUNCATE_DEPTH = 2; 4586 fmt_bit_depth->flags.TRUNCATE_MODE = 1; 4587 } 4588 4589 /* spatial dither 4590 * note that spatial modes 1-3 are never used 4591 */ 4592 if (option == DITHER_OPTION_SPATIAL6_FRAME_RANDOM || 4593 option == DITHER_OPTION_SPATIAL6 || 4594 option == DITHER_OPTION_TRUN10_SPATIAL6 || 4595 option == DITHER_OPTION_TRUN8_SPATIAL6) { 4596 fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 1; 4597 fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH = 0; 4598 fmt_bit_depth->flags.HIGHPASS_RANDOM = 1; 4599 fmt_bit_depth->flags.RGB_RANDOM = 4600 (pixel_encoding == PIXEL_ENCODING_RGB) ? 1 : 0; 4601 } else if (option == DITHER_OPTION_SPATIAL8_FRAME_RANDOM || 4602 option == DITHER_OPTION_SPATIAL8 || 4603 option == DITHER_OPTION_SPATIAL8_FM6 || 4604 option == DITHER_OPTION_TRUN10_SPATIAL8 || 4605 option == DITHER_OPTION_TRUN10_SPATIAL8_FM6) { 4606 fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 1; 4607 fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH = 1; 4608 fmt_bit_depth->flags.HIGHPASS_RANDOM = 1; 4609 fmt_bit_depth->flags.RGB_RANDOM = 4610 (pixel_encoding == PIXEL_ENCODING_RGB) ? 1 : 0; 4611 } else if (option == DITHER_OPTION_SPATIAL10_FRAME_RANDOM || 4612 option == DITHER_OPTION_SPATIAL10 || 4613 option == DITHER_OPTION_SPATIAL10_FM8 || 4614 option == DITHER_OPTION_SPATIAL10_FM6) { 4615 fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 1; 4616 fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH = 2; 4617 fmt_bit_depth->flags.HIGHPASS_RANDOM = 1; 4618 fmt_bit_depth->flags.RGB_RANDOM = 4619 (pixel_encoding == PIXEL_ENCODING_RGB) ? 1 : 0; 4620 } 4621 4622 if (option == DITHER_OPTION_SPATIAL6 || 4623 option == DITHER_OPTION_SPATIAL8 || 4624 option == DITHER_OPTION_SPATIAL10) { 4625 fmt_bit_depth->flags.FRAME_RANDOM = 0; 4626 } else { 4627 fmt_bit_depth->flags.FRAME_RANDOM = 1; 4628 } 4629 4630 ////////////////////// 4631 //// temporal dither 4632 ////////////////////// 4633 if (option == DITHER_OPTION_FM6 || 4634 option == DITHER_OPTION_SPATIAL8_FM6 || 4635 option == DITHER_OPTION_SPATIAL10_FM6 || 4636 option == DITHER_OPTION_TRUN10_FM6 || 4637 option == DITHER_OPTION_TRUN8_FM6 || 4638 option == DITHER_OPTION_TRUN10_SPATIAL8_FM6) { 4639 fmt_bit_depth->flags.FRAME_MODULATION_ENABLED = 1; 4640 fmt_bit_depth->flags.FRAME_MODULATION_DEPTH = 0; 4641 } else if (option == DITHER_OPTION_FM8 || 4642 option == DITHER_OPTION_SPATIAL10_FM8 || 4643 option == DITHER_OPTION_TRUN10_FM8) { 4644 fmt_bit_depth->flags.FRAME_MODULATION_ENABLED = 1; 4645 fmt_bit_depth->flags.FRAME_MODULATION_DEPTH = 1; 4646 } else if (option == DITHER_OPTION_FM10) { 4647 fmt_bit_depth->flags.FRAME_MODULATION_ENABLED = 1; 4648 fmt_bit_depth->flags.FRAME_MODULATION_DEPTH = 2; 4649 } 4650 4651 fmt_bit_depth->pixel_encoding = pixel_encoding; 4652} 4653 4654enum dc_status dc_validate_stream(struct dc *dc, struct dc_stream_state *stream) 4655{ 4656 struct dc_link *link = stream->link; 4657 struct timing_generator *tg = dc->res_pool->timing_generators[0]; 4658 enum dc_status res = DC_OK; 4659 4660 calculate_phy_pix_clks(stream); 4661 4662 if (!tg->funcs->validate_timing(tg, &stream->timing)) 4663 res = DC_FAIL_CONTROLLER_VALIDATE; 4664 4665 if (res == DC_OK) { 4666 if (link->ep_type == DISPLAY_ENDPOINT_PHY && 4667 !link->link_enc->funcs->validate_output_with_stream( 4668 link->link_enc, stream)) 4669 res = DC_FAIL_ENC_VALIDATE; 4670 } 4671 4672 /* TODO: validate audio ASIC caps, encoder */ 4673 4674 if (res == DC_OK) 4675 res = dc->link_srv->validate_mode_timing(stream, 4676 link, 4677 &stream->timing); 4678 4679 return res; 4680} 4681 4682enum dc_status dc_validate_plane(struct dc *dc, const struct dc_plane_state *plane_state) 4683{ 4684 enum dc_status res = DC_OK; 4685 4686 /* check if surface has invalid dimensions */ 4687 if (plane_state->src_rect.width == 0 || plane_state->src_rect.height == 0 || 4688 plane_state->dst_rect.width == 0 || plane_state->dst_rect.height == 0) 4689 return DC_FAIL_SURFACE_VALIDATE; 4690 4691 /* TODO For now validates pixel format only */ 4692 if (dc->res_pool->funcs->validate_plane) 4693 return dc->res_pool->funcs->validate_plane(plane_state, &dc->caps); 4694 4695 return res; 4696} 4697 4698unsigned int resource_pixel_format_to_bpp(enum surface_pixel_format format) 4699{ 4700 switch (format) { 4701 case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS: 4702 return 8; 4703 case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: 4704 case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: 4705 return 12; 4706 case SURFACE_PIXEL_FORMAT_GRPH_ARGB1555: 4707 case SURFACE_PIXEL_FORMAT_GRPH_RGB565: 4708 case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr: 4709 case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb: 4710 return 16; 4711 case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: 4712 case SURFACE_PIXEL_FORMAT_GRPH_ABGR8888: 4713 case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: 4714 case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: 4715 case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: 4716 case SURFACE_PIXEL_FORMAT_GRPH_RGBE: 4717 case SURFACE_PIXEL_FORMAT_GRPH_RGBE_ALPHA: 4718 return 32; 4719 case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: 4720 case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616: 4721 case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: 4722 case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: 4723 return 64; 4724 default: 4725 ASSERT_CRITICAL(false); 4726 return -1; 4727 } 4728} 4729static unsigned int get_max_audio_sample_rate(struct audio_mode *modes) 4730{ 4731 if (modes) { 4732 if (modes->sample_rates.rate.RATE_192) 4733 return 192000; 4734 if (modes->sample_rates.rate.RATE_176_4) 4735 return 176400; 4736 if (modes->sample_rates.rate.RATE_96) 4737 return 96000; 4738 if (modes->sample_rates.rate.RATE_88_2) 4739 return 88200; 4740 if (modes->sample_rates.rate.RATE_48) 4741 return 48000; 4742 if (modes->sample_rates.rate.RATE_44_1) 4743 return 44100; 4744 if (modes->sample_rates.rate.RATE_32) 4745 return 32000; 4746 } 4747 /*original logic when no audio info*/ 4748 return 441000; 4749} 4750 4751void get_audio_check(struct audio_info *aud_modes, 4752 struct audio_check *audio_chk) 4753{ 4754 unsigned int i; 4755 unsigned int max_sample_rate = 0; 4756 4757 if (aud_modes) { 4758 audio_chk->audio_packet_type = 0x2;/*audio sample packet AP = .25 for layout0, 1 for layout1*/ 4759 4760 audio_chk->max_audiosample_rate = 0; 4761 for (i = 0; i < aud_modes->mode_count; i++) { 4762 max_sample_rate = get_max_audio_sample_rate(&aud_modes->modes[i]); 4763 if (audio_chk->max_audiosample_rate < max_sample_rate) 4764 audio_chk->max_audiosample_rate = max_sample_rate; 4765 /*dts takes the same as type 2: AP = 0.25*/ 4766 } 4767 /*check which one take more bandwidth*/ 4768 if (audio_chk->max_audiosample_rate > 192000) 4769 audio_chk->audio_packet_type = 0x9;/*AP =1*/ 4770 audio_chk->acat = 0;/*not support*/ 4771 } 4772} 4773 4774static struct hpo_dp_link_encoder *get_temp_hpo_dp_link_enc( 4775 const struct resource_context *res_ctx, 4776 const struct resource_pool *const pool, 4777 const struct dc_link *link) 4778{ 4779 struct hpo_dp_link_encoder *hpo_dp_link_enc = NULL; 4780 int enc_index; 4781 4782 enc_index = find_acquired_hpo_dp_link_enc_for_link(res_ctx, link); 4783 4784 if (enc_index < 0) 4785 enc_index = find_free_hpo_dp_link_enc(res_ctx, pool); 4786 4787 if (enc_index >= 0) 4788 hpo_dp_link_enc = pool->hpo_dp_link_enc[enc_index]; 4789 4790 return hpo_dp_link_enc; 4791} 4792 4793bool get_temp_dp_link_res(struct dc_link *link, 4794 struct link_resource *link_res, 4795 struct dc_link_settings *link_settings) 4796{ 4797 const struct dc *dc = link->dc; 4798 const struct resource_context *res_ctx = &dc->current_state->res_ctx; 4799 4800 memset(link_res, 0, sizeof(*link_res)); 4801 4802 if (dc->link_srv->dp_get_encoding_format(link_settings) == DP_128b_132b_ENCODING) { 4803 link_res->hpo_dp_link_enc = get_temp_hpo_dp_link_enc(res_ctx, 4804 dc->res_pool, link); 4805 if (!link_res->hpo_dp_link_enc) 4806 return false; 4807 } 4808 return true; 4809} 4810 4811void reset_syncd_pipes_from_disabled_pipes(struct dc *dc, 4812 struct dc_state *context) 4813{ 4814 int i, j; 4815 struct pipe_ctx *pipe_ctx_old, *pipe_ctx, *pipe_ctx_syncd; 4816 4817 /* If pipe backend is reset, need to reset pipe syncd status */ 4818 for (i = 0; i < dc->res_pool->pipe_count; i++) { 4819 pipe_ctx_old = &dc->current_state->res_ctx.pipe_ctx[i]; 4820 pipe_ctx = &context->res_ctx.pipe_ctx[i]; 4821 4822 if (!resource_is_pipe_type(pipe_ctx_old, OTG_MASTER)) 4823 continue; 4824 4825 if (!pipe_ctx->stream || 4826 pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) { 4827 4828 /* Reset all the syncd pipes from the disabled pipe */ 4829 for (j = 0; j < dc->res_pool->pipe_count; j++) { 4830 pipe_ctx_syncd = &context->res_ctx.pipe_ctx[j]; 4831 if ((GET_PIPE_SYNCD_FROM_PIPE(pipe_ctx_syncd) == pipe_ctx_old->pipe_idx) || 4832 !IS_PIPE_SYNCD_VALID(pipe_ctx_syncd)) 4833 SET_PIPE_SYNCD_TO_PIPE(pipe_ctx_syncd, j); 4834 } 4835 } 4836 } 4837} 4838 4839void check_syncd_pipes_for_disabled_master_pipe(struct dc *dc, 4840 struct dc_state *context, 4841 uint8_t disabled_master_pipe_idx) 4842{ 4843 int i; 4844 struct pipe_ctx *pipe_ctx, *pipe_ctx_check; 4845 4846 pipe_ctx = &context->res_ctx.pipe_ctx[disabled_master_pipe_idx]; 4847 if ((GET_PIPE_SYNCD_FROM_PIPE(pipe_ctx) != disabled_master_pipe_idx) || 4848 !IS_PIPE_SYNCD_VALID(pipe_ctx)) 4849 SET_PIPE_SYNCD_TO_PIPE(pipe_ctx, disabled_master_pipe_idx); 4850 4851 /* for the pipe disabled, check if any slave pipe exists and assert */ 4852 for (i = 0; i < dc->res_pool->pipe_count; i++) { 4853 pipe_ctx_check = &context->res_ctx.pipe_ctx[i]; 4854 4855 if ((GET_PIPE_SYNCD_FROM_PIPE(pipe_ctx_check) == disabled_master_pipe_idx) && 4856 IS_PIPE_SYNCD_VALID(pipe_ctx_check) && (i != disabled_master_pipe_idx)) { 4857 struct pipe_ctx *first_pipe = pipe_ctx_check; 4858 4859 while (first_pipe->prev_odm_pipe) 4860 first_pipe = first_pipe->prev_odm_pipe; 4861 /* When ODM combine is enabled, this case is expected. If the disabled pipe 4862 * is part of the ODM tree, then we should not print an error. 4863 * */ 4864 if (first_pipe->pipe_idx == disabled_master_pipe_idx) 4865 continue; 4866 4867 DC_ERR("DC: Failure: pipe_idx[%d] syncd with disabled master pipe_idx[%d]\n", 4868 i, disabled_master_pipe_idx); 4869 } 4870 } 4871} 4872 4873void reset_sync_context_for_pipe(const struct dc *dc, 4874 struct dc_state *context, 4875 uint8_t pipe_idx) 4876{ 4877 int i; 4878 struct pipe_ctx *pipe_ctx_reset; 4879 4880 /* reset the otg sync context for the pipe and its slave pipes if any */ 4881 for (i = 0; i < dc->res_pool->pipe_count; i++) { 4882 pipe_ctx_reset = &context->res_ctx.pipe_ctx[i]; 4883 4884 if (((GET_PIPE_SYNCD_FROM_PIPE(pipe_ctx_reset) == pipe_idx) && 4885 IS_PIPE_SYNCD_VALID(pipe_ctx_reset)) || (i == pipe_idx)) 4886 SET_PIPE_SYNCD_TO_PIPE(pipe_ctx_reset, i); 4887 } 4888} 4889 4890uint8_t resource_transmitter_to_phy_idx(const struct dc *dc, enum transmitter transmitter) 4891{ 4892 /* TODO - get transmitter to phy idx mapping from DMUB */ 4893 uint8_t phy_idx = transmitter - TRANSMITTER_UNIPHY_A; 4894 4895 if (dc->ctx->dce_version == DCN_VERSION_3_1 && 4896 dc->ctx->asic_id.hw_internal_rev == YELLOW_CARP_B0) { 4897 switch (transmitter) { 4898 case TRANSMITTER_UNIPHY_A: 4899 phy_idx = 0; 4900 break; 4901 case TRANSMITTER_UNIPHY_B: 4902 phy_idx = 1; 4903 break; 4904 case TRANSMITTER_UNIPHY_C: 4905 phy_idx = 5; 4906 break; 4907 case TRANSMITTER_UNIPHY_D: 4908 phy_idx = 6; 4909 break; 4910 case TRANSMITTER_UNIPHY_E: 4911 phy_idx = 4; 4912 break; 4913 default: 4914 phy_idx = 0; 4915 break; 4916 } 4917 } 4918 4919 return phy_idx; 4920} 4921 4922const struct link_hwss *get_link_hwss(const struct dc_link *link, 4923 const struct link_resource *link_res) 4924{ 4925 /* Link_hwss is only accessible by getter function instead of accessing 4926 * by pointers in dc with the intent to protect against breaking polymorphism. 4927 */ 4928 if (can_use_hpo_dp_link_hwss(link, link_res)) 4929 /* TODO: some assumes that if decided link settings is 128b/132b 4930 * channel coding format hpo_dp_link_enc should be used. 4931 * Others believe that if hpo_dp_link_enc is available in link 4932 * resource then hpo_dp_link_enc must be used. This bound between 4933 * hpo_dp_link_enc != NULL and decided link settings is loosely coupled 4934 * with a premise that both hpo_dp_link_enc pointer and decided link 4935 * settings are determined based on single policy function like 4936 * "decide_link_settings" from upper layer. This "convention" 4937 * cannot be maintained and enforced at current level. 4938 * Therefore a refactor is due so we can enforce a strong bound 4939 * between those two parameters at this level. 4940 * 4941 * To put it simple, we want to make enforcement at low level so that 4942 * we will not return link hwss if caller plans to do 8b/10b 4943 * with an hpo encoder. Or we can return a very dummy one that doesn't 4944 * do work for all functions 4945 */ 4946 return (requires_fixed_vs_pe_retimer_hpo_link_hwss(link) ? 4947 get_hpo_fixed_vs_pe_retimer_dp_link_hwss() : get_hpo_dp_link_hwss()); 4948 else if (can_use_dpia_link_hwss(link, link_res)) 4949 return get_dpia_link_hwss(); 4950 else if (can_use_dio_link_hwss(link, link_res)) 4951 return (requires_fixed_vs_pe_retimer_dio_link_hwss(link)) ? 4952 get_dio_fixed_vs_pe_retimer_link_hwss() : get_dio_link_hwss(); 4953 else 4954 return get_virtual_link_hwss(); 4955} 4956 4957bool is_h_timing_divisible_by_2(struct dc_stream_state *stream) 4958{ 4959 bool divisible = false; 4960 uint16_t h_blank_start = 0; 4961 uint16_t h_blank_end = 0; 4962 4963 if (stream) { 4964 h_blank_start = stream->timing.h_total - stream->timing.h_front_porch; 4965 h_blank_end = h_blank_start - stream->timing.h_addressable; 4966 4967 /* HTOTAL, Hblank start/end, and Hsync start/end all must be 4968 * divisible by 2 in order for the horizontal timing params 4969 * to be considered divisible by 2. Hsync start is always 0. 4970 */ 4971 divisible = (stream->timing.h_total % 2 == 0) && 4972 (h_blank_start % 2 == 0) && 4973 (h_blank_end % 2 == 0) && 4974 (stream->timing.h_sync_width % 2 == 0); 4975 } 4976 return divisible; 4977} 4978 4979/* This interface is deprecated for new DCNs. It is replaced by the following 4980 * new interfaces. These two interfaces encapsulate pipe selection priority 4981 * with DCN specific minimum hardware transition optimization algorithm. With 4982 * the new interfaces caller no longer needs to know the implementation detail 4983 * of a pipe topology. 4984 * 4985 * resource_update_pipes_with_odm_slice_count 4986 * resource_update_pipes_with_mpc_slice_count 4987 * 4988 */ 4989bool dc_resource_acquire_secondary_pipe_for_mpc_odm_legacy( 4990 const struct dc *dc, 4991 struct dc_state *state, 4992 struct pipe_ctx *pri_pipe, 4993 struct pipe_ctx *sec_pipe, 4994 bool odm) 4995{ 4996 int pipe_idx = sec_pipe->pipe_idx; 4997 struct pipe_ctx *sec_top, *sec_bottom, *sec_next, *sec_prev; 4998 const struct resource_pool *pool = dc->res_pool; 4999 5000 sec_top = sec_pipe->top_pipe; 5001 sec_bottom = sec_pipe->bottom_pipe; 5002 sec_next = sec_pipe->next_odm_pipe; 5003 sec_prev = sec_pipe->prev_odm_pipe; 5004 5005 if (pri_pipe == NULL) 5006 return false; 5007 5008 *sec_pipe = *pri_pipe; 5009 5010 sec_pipe->top_pipe = sec_top; 5011 sec_pipe->bottom_pipe = sec_bottom; 5012 sec_pipe->next_odm_pipe = sec_next; 5013 sec_pipe->prev_odm_pipe = sec_prev; 5014 5015 sec_pipe->pipe_idx = pipe_idx; 5016 sec_pipe->plane_res.mi = pool->mis[pipe_idx]; 5017 sec_pipe->plane_res.hubp = pool->hubps[pipe_idx]; 5018 sec_pipe->plane_res.ipp = pool->ipps[pipe_idx]; 5019 sec_pipe->plane_res.xfm = pool->transforms[pipe_idx]; 5020 sec_pipe->plane_res.dpp = pool->dpps[pipe_idx]; 5021 sec_pipe->plane_res.mpcc_inst = pool->dpps[pipe_idx]->inst; 5022 sec_pipe->stream_res.dsc = NULL; 5023 if (odm) { 5024 if (!sec_pipe->top_pipe) 5025 sec_pipe->stream_res.opp = pool->opps[pipe_idx]; 5026 else 5027 sec_pipe->stream_res.opp = sec_pipe->top_pipe->stream_res.opp; 5028 if (sec_pipe->stream->timing.flags.DSC == 1) { 5029#if defined(CONFIG_DRM_AMD_DC_FP) 5030 dcn20_acquire_dsc(dc, &state->res_ctx, &sec_pipe->stream_res.dsc, pipe_idx); 5031#endif 5032 ASSERT(sec_pipe->stream_res.dsc); 5033 if (sec_pipe->stream_res.dsc == NULL) 5034 return false; 5035 } 5036#if defined(CONFIG_DRM_AMD_DC_FP) 5037 dcn20_build_mapped_resource(dc, state, sec_pipe->stream); 5038#endif 5039 } 5040 5041 return true; 5042} 5043 5044enum dc_status update_dp_encoder_resources_for_test_harness(const struct dc *dc, 5045 struct dc_state *context, 5046 struct pipe_ctx *pipe_ctx) 5047{ 5048 if (dc->link_srv->dp_get_encoding_format(&pipe_ctx->link_config.dp_link_settings) == DP_128b_132b_ENCODING) { 5049 if (pipe_ctx->stream_res.hpo_dp_stream_enc == NULL) { 5050 pipe_ctx->stream_res.hpo_dp_stream_enc = 5051 find_first_free_match_hpo_dp_stream_enc_for_link( 5052 &context->res_ctx, dc->res_pool, pipe_ctx->stream); 5053 5054 if (!pipe_ctx->stream_res.hpo_dp_stream_enc) 5055 return DC_NO_STREAM_ENC_RESOURCE; 5056 5057 update_hpo_dp_stream_engine_usage( 5058 &context->res_ctx, dc->res_pool, 5059 pipe_ctx->stream_res.hpo_dp_stream_enc, 5060 true); 5061 } 5062 5063 if (pipe_ctx->link_res.hpo_dp_link_enc == NULL) { 5064 if (!add_hpo_dp_link_enc_to_ctx(&context->res_ctx, dc->res_pool, pipe_ctx, pipe_ctx->stream)) 5065 return DC_NO_LINK_ENC_RESOURCE; 5066 } 5067 } else { 5068 if (pipe_ctx->stream_res.hpo_dp_stream_enc) { 5069 update_hpo_dp_stream_engine_usage( 5070 &context->res_ctx, dc->res_pool, 5071 pipe_ctx->stream_res.hpo_dp_stream_enc, 5072 false); 5073 pipe_ctx->stream_res.hpo_dp_stream_enc = NULL; 5074 } 5075 if (pipe_ctx->link_res.hpo_dp_link_enc) 5076 remove_hpo_dp_link_enc_from_ctx(&context->res_ctx, pipe_ctx, pipe_ctx->stream); 5077 } 5078 5079 return DC_OK; 5080} 5081 5082bool check_subvp_sw_cursor_fallback_req(const struct dc *dc, struct dc_stream_state *stream) 5083{ 5084 if (!dc->debug.disable_subvp_high_refresh && is_subvp_high_refresh_candidate(stream)) 5085 return true; 5086 if (dc->current_state->stream_count == 1 && stream->timing.v_addressable >= 2880 && 5087 ((stream->timing.pix_clk_100hz * 100) / stream->timing.v_total / stream->timing.h_total) < 120) 5088 return true; 5089 else if (dc->current_state->stream_count > 1 && stream->timing.v_addressable >= 1080 && 5090 ((stream->timing.pix_clk_100hz * 100) / stream->timing.v_total / stream->timing.h_total) < 120) 5091 return true; 5092 5093 return false; 5094} 5095 5096void resource_init_common_dml2_callbacks(struct dc *dc, struct dml2_configuration_options *dml2_options) 5097{ 5098 dml2_options->callbacks.dc = dc; 5099 dml2_options->callbacks.build_scaling_params = &resource_build_scaling_params; 5100 dml2_options->callbacks.build_test_pattern_params = &resource_build_test_pattern_params; 5101 dml2_options->callbacks.acquire_secondary_pipe_for_mpc_odm = &dc_resource_acquire_secondary_pipe_for_mpc_odm_legacy; 5102 dml2_options->callbacks.update_pipes_for_stream_with_slice_count = &resource_update_pipes_for_stream_with_slice_count; 5103 dml2_options->callbacks.update_pipes_for_plane_with_slice_count = &resource_update_pipes_for_plane_with_slice_count; 5104 dml2_options->callbacks.get_mpc_slice_index = &resource_get_mpc_slice_index; 5105 dml2_options->callbacks.get_mpc_slice_count = &resource_get_mpc_slice_count; 5106 dml2_options->callbacks.get_odm_slice_index = &resource_get_odm_slice_index; 5107 dml2_options->callbacks.get_odm_slice_count = &resource_get_odm_slice_count; 5108 dml2_options->callbacks.get_opp_head = &resource_get_opp_head; 5109 dml2_options->callbacks.get_otg_master_for_stream = &resource_get_otg_master_for_stream; 5110 dml2_options->callbacks.get_opp_heads_for_otg_master = &resource_get_opp_heads_for_otg_master; 5111 dml2_options->callbacks.get_dpp_pipes_for_plane = &resource_get_dpp_pipes_for_plane; 5112 dml2_options->callbacks.get_stream_status = &dc_state_get_stream_status; 5113 dml2_options->callbacks.get_stream_from_id = &dc_state_get_stream_from_id; 5114 5115 dml2_options->svp_pstate.callbacks.dc = dc; 5116 dml2_options->svp_pstate.callbacks.add_phantom_plane = &dc_state_add_phantom_plane; 5117 dml2_options->svp_pstate.callbacks.add_phantom_stream = &dc_state_add_phantom_stream; 5118 dml2_options->svp_pstate.callbacks.build_scaling_params = &resource_build_scaling_params; 5119 dml2_options->svp_pstate.callbacks.create_phantom_plane = &dc_state_create_phantom_plane; 5120 dml2_options->svp_pstate.callbacks.remove_phantom_plane = &dc_state_remove_phantom_plane; 5121 dml2_options->svp_pstate.callbacks.remove_phantom_stream = &dc_state_remove_phantom_stream; 5122 dml2_options->svp_pstate.callbacks.create_phantom_stream = &dc_state_create_phantom_stream; 5123 dml2_options->svp_pstate.callbacks.release_phantom_plane = &dc_state_release_phantom_plane; 5124 dml2_options->svp_pstate.callbacks.release_phantom_stream = &dc_state_release_phantom_stream; 5125 dml2_options->svp_pstate.callbacks.get_pipe_subvp_type = &dc_state_get_pipe_subvp_type; 5126 dml2_options->svp_pstate.callbacks.get_stream_subvp_type = &dc_state_get_stream_subvp_type; 5127 dml2_options->svp_pstate.callbacks.get_paired_subvp_stream = &dc_state_get_paired_subvp_stream; 5128 dml2_options->svp_pstate.callbacks.remove_phantom_streams_and_planes = &dc_state_remove_phantom_streams_and_planes; 5129 dml2_options->svp_pstate.callbacks.release_phantom_streams_and_planes = &dc_state_release_phantom_streams_and_planes; 5130} 5131