FILE
dc_stream.c
File preview with syntax highlighting.
linux-6.19.14/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
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#include "basics/dc_common.h" 28#include "dc.h" 29#include "core_types.h" 30#include "resource.h" 31#include "ipp.h" 32#include "timing_generator.h" 33#include "dc_dmub_srv.h" 34#include "dc_state_priv.h" 35#include "dc_stream_priv.h" 36 37#define DC_LOGGER dc->ctx->logger 38#ifndef MIN 39#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) 40#endif 41#ifndef MAX 42#define MAX(x, y) ((x > y) ? x : y) 43#endif 44 45/******************************************************************************* 46 * Private functions 47 ******************************************************************************/ 48void update_stream_signal(struct dc_stream_state *stream, struct dc_sink *sink) 49{ 50 if (sink->sink_signal == SIGNAL_TYPE_NONE) 51 stream->signal = stream->link->connector_signal; 52 else 53 stream->signal = sink->sink_signal; 54 55 if (dc_is_dvi_signal(stream->signal)) { 56 if (stream->ctx->dc->caps.dual_link_dvi && 57 (stream->timing.pix_clk_100hz / 10) > TMDS_MAX_PIXEL_CLOCK && 58 sink->sink_signal != SIGNAL_TYPE_DVI_SINGLE_LINK) 59 stream->signal = SIGNAL_TYPE_DVI_DUAL_LINK; 60 else 61 stream->signal = SIGNAL_TYPE_DVI_SINGLE_LINK; 62 } 63} 64 65bool dc_stream_construct(struct dc_stream_state *stream, 66 struct dc_sink *dc_sink_data) 67{ 68 uint32_t i = 0; 69 70 stream->sink = dc_sink_data; 71 dc_sink_retain(dc_sink_data); 72 73 stream->ctx = dc_sink_data->ctx; 74 stream->link = dc_sink_data->link; 75 stream->sink_patches = dc_sink_data->edid_caps.panel_patch; 76 stream->converter_disable_audio = dc_sink_data->converter_disable_audio; 77 stream->qs_bit = dc_sink_data->edid_caps.qs_bit; 78 stream->qy_bit = dc_sink_data->edid_caps.qy_bit; 79 80 /* Copy audio modes */ 81 /* TODO - Remove this translation */ 82 for (i = 0; i < (dc_sink_data->edid_caps.audio_mode_count); i++) { 83 stream->audio_info.modes[i].channel_count = dc_sink_data->edid_caps.audio_modes[i].channel_count; 84 stream->audio_info.modes[i].format_code = dc_sink_data->edid_caps.audio_modes[i].format_code; 85 stream->audio_info.modes[i].sample_rates.all = dc_sink_data->edid_caps.audio_modes[i].sample_rate; 86 stream->audio_info.modes[i].sample_size = dc_sink_data->edid_caps.audio_modes[i].sample_size; 87 } 88 stream->audio_info.mode_count = dc_sink_data->edid_caps.audio_mode_count; 89 stream->audio_info.audio_latency = dc_sink_data->edid_caps.audio_latency; 90 stream->audio_info.video_latency = dc_sink_data->edid_caps.video_latency; 91 memmove( 92 stream->audio_info.display_name, 93 dc_sink_data->edid_caps.display_name, 94 AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS); 95 stream->audio_info.manufacture_id = dc_sink_data->edid_caps.manufacturer_id; 96 stream->audio_info.product_id = dc_sink_data->edid_caps.product_id; 97 stream->audio_info.flags.all = dc_sink_data->edid_caps.speaker_flags; 98 99 if (dc_sink_data->dc_container_id != NULL) { 100 struct dc_container_id *dc_container_id = dc_sink_data->dc_container_id; 101 102 stream->audio_info.port_id[0] = dc_container_id->portId[0]; 103 stream->audio_info.port_id[1] = dc_container_id->portId[1]; 104 } else { 105 /* TODO - WindowDM has implemented, 106 other DMs need Unhardcode port_id */ 107 stream->audio_info.port_id[0] = 0x5558859e; 108 stream->audio_info.port_id[1] = 0xd989449; 109 } 110 111 /* EDID CAP translation for HDMI 2.0 */ 112 stream->timing.flags.LTE_340MCSC_SCRAMBLE = dc_sink_data->edid_caps.lte_340mcsc_scramble; 113 114 memset(&stream->timing.dsc_cfg, 0, sizeof(stream->timing.dsc_cfg)); 115 stream->timing.dsc_cfg.num_slices_h = 0; 116 stream->timing.dsc_cfg.num_slices_v = 0; 117 stream->timing.dsc_cfg.bits_per_pixel = 128; 118 stream->timing.dsc_cfg.block_pred_enable = 1; 119 stream->timing.dsc_cfg.linebuf_depth = 9; 120 stream->timing.dsc_cfg.version_minor = 2; 121 stream->timing.dsc_cfg.ycbcr422_simple = 0; 122 123 update_stream_signal(stream, dc_sink_data); 124 125 stream->out_transfer_func.type = TF_TYPE_BYPASS; 126 127 dc_stream_assign_stream_id(stream); 128 129 return true; 130} 131 132void dc_stream_destruct(struct dc_stream_state *stream) 133{ 134 dc_sink_release(stream->sink); 135} 136 137void dc_stream_assign_stream_id(struct dc_stream_state *stream) 138{ 139 /* MSB is reserved to indicate phantoms */ 140 stream->stream_id = stream->ctx->dc_stream_id_count; 141 stream->ctx->dc_stream_id_count++; 142} 143 144void dc_stream_retain(struct dc_stream_state *stream) 145{ 146 kref_get(&stream->refcount); 147} 148 149static void dc_stream_free(struct kref *kref) 150{ 151 struct dc_stream_state *stream = container_of(kref, struct dc_stream_state, refcount); 152 153 dc_stream_destruct(stream); 154 kfree(stream); 155} 156 157void dc_stream_release(struct dc_stream_state *stream) 158{ 159 if (stream != NULL) { 160 kref_put(&stream->refcount, dc_stream_free); 161 } 162} 163 164struct dc_stream_state *dc_create_stream_for_sink( 165 struct dc_sink *sink) 166{ 167 struct dc_stream_state *stream; 168 169 if (sink == NULL) 170 return NULL; 171 172 stream = kzalloc(sizeof(struct dc_stream_state), GFP_ATOMIC); 173 if (stream == NULL) 174 goto alloc_fail; 175 176 if (dc_stream_construct(stream, sink) == false) 177 goto construct_fail; 178 179 kref_init(&stream->refcount); 180 181 return stream; 182 183construct_fail: 184 kfree(stream); 185 186alloc_fail: 187 return NULL; 188} 189 190struct dc_stream_state *dc_copy_stream(const struct dc_stream_state *stream) 191{ 192 struct dc_stream_state *new_stream; 193 194 new_stream = kmemdup(stream, sizeof(struct dc_stream_state), GFP_KERNEL); 195 if (!new_stream) 196 return NULL; 197 198 if (new_stream->sink) 199 dc_sink_retain(new_stream->sink); 200 201 dc_stream_assign_stream_id(new_stream); 202 203 /* If using dynamic encoder assignment, wait till stream committed to assign encoder. */ 204 if (new_stream->ctx->dc->res_pool->funcs->link_encs_assign && 205 !new_stream->ctx->dc->config.unify_link_enc_assignment) 206 new_stream->link_enc = NULL; 207 208 kref_init(&new_stream->refcount); 209 210 return new_stream; 211} 212 213/** 214 * dc_stream_get_status() - Get current stream status of the given stream state 215 * @stream: The stream to get the stream status for. 216 * 217 * The given stream is expected to exist in dc->current_state. Otherwise, NULL 218 * will be returned. 219 */ 220struct dc_stream_status *dc_stream_get_status( 221 struct dc_stream_state *stream) 222{ 223 struct dc *dc = stream->ctx->dc; 224 return dc_state_get_stream_status(dc->current_state, stream); 225} 226 227const struct dc_stream_status *dc_stream_get_status_const( 228 const struct dc_stream_state *stream) 229{ 230 struct dc *dc = stream->ctx->dc; 231 232 return dc_state_get_stream_status(dc->current_state, stream); 233} 234 235void program_cursor_attributes( 236 struct dc *dc, 237 struct dc_stream_state *stream) 238{ 239 int i; 240 struct resource_context *res_ctx; 241 struct pipe_ctx *pipe_to_program = NULL; 242 bool enable_cursor_offload = dc_dmub_srv_is_cursor_offload_enabled(dc); 243 244 if (!stream) 245 return; 246 247 res_ctx = &dc->current_state->res_ctx; 248 249 for (i = 0; i < MAX_PIPES; i++) { 250 struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i]; 251 252 if (pipe_ctx->stream != stream) 253 continue; 254 255 if (!pipe_to_program) { 256 pipe_to_program = pipe_ctx; 257 258 if (enable_cursor_offload && dc->hwss.begin_cursor_offload_update) { 259 dc->hwss.begin_cursor_offload_update(dc, pipe_ctx); 260 } else { 261 dc->hwss.cursor_lock(dc, pipe_to_program, true); 262 if (pipe_to_program->next_odm_pipe) 263 dc->hwss.cursor_lock(dc, pipe_to_program->next_odm_pipe, true); 264 } 265 } 266 267 dc->hwss.set_cursor_attribute(pipe_ctx); 268 if (dc->ctx->dmub_srv) 269 dc_send_update_cursor_info_to_dmu(pipe_ctx, i); 270 if (dc->hwss.set_cursor_sdr_white_level) 271 dc->hwss.set_cursor_sdr_white_level(pipe_ctx); 272 if (enable_cursor_offload && dc->hwss.update_cursor_offload_pipe) 273 dc->hwss.update_cursor_offload_pipe(dc, pipe_ctx); 274 } 275 276 if (pipe_to_program) { 277 if (enable_cursor_offload && dc->hwss.commit_cursor_offload_update) { 278 dc->hwss.commit_cursor_offload_update(dc, pipe_to_program); 279 } else { 280 dc->hwss.cursor_lock(dc, pipe_to_program, false); 281 if (pipe_to_program->next_odm_pipe) 282 dc->hwss.cursor_lock(dc, pipe_to_program->next_odm_pipe, false); 283 } 284 } 285} 286 287/* 288 * dc_stream_check_cursor_attributes() - Check validitity of cursor attributes and surface address 289 */ 290bool dc_stream_check_cursor_attributes( 291 const struct dc_stream_state *stream, 292 struct dc_state *state, 293 const struct dc_cursor_attributes *attributes) 294{ 295 const struct dc *dc; 296 297 unsigned int max_cursor_size; 298 299 if (NULL == stream) { 300 dm_error("DC: dc_stream is NULL!\n"); 301 return false; 302 } 303 if (NULL == attributes) { 304 dm_error("DC: attributes is NULL!\n"); 305 return false; 306 } 307 308 if (attributes->address.quad_part == 0) { 309 dm_output_to_console("DC: Cursor address is 0!\n"); 310 return false; 311 } 312 313 dc = stream->ctx->dc; 314 315 /* SubVP is not compatible with HW cursor larger than what can fit in cursor SRAM. 316 * Therefore, if cursor is greater than this, fallback to SW cursor. 317 */ 318 if (dc->debug.allow_sw_cursor_fallback && dc->res_pool->funcs->get_max_hw_cursor_size) { 319 max_cursor_size = dc->res_pool->funcs->get_max_hw_cursor_size(dc, state, stream); 320 max_cursor_size = max_cursor_size * max_cursor_size * 4; 321 322 if (attributes->height * attributes->width * 4 > max_cursor_size) { 323 return false; 324 } 325 } 326 327 return true; 328} 329 330/* 331 * dc_stream_set_cursor_attributes() - Update cursor attributes and set cursor surface address 332 */ 333bool dc_stream_set_cursor_attributes( 334 struct dc_stream_state *stream, 335 const struct dc_cursor_attributes *attributes) 336{ 337 bool result = false; 338 339 if (!stream) 340 return false; 341 342 if (dc_stream_check_cursor_attributes(stream, stream->ctx->dc->current_state, attributes)) { 343 stream->cursor_attributes = *attributes; 344 result = true; 345 } 346 347 return result; 348} 349 350bool dc_stream_program_cursor_attributes( 351 struct dc_stream_state *stream, 352 const struct dc_cursor_attributes *attributes) 353{ 354 struct dc *dc; 355 bool reset_idle_optimizations = false; 356 357 if (!stream) 358 return false; 359 360 dc = stream->ctx->dc; 361 362 if (dc_stream_set_cursor_attributes(stream, attributes)) { 363 dc_z10_restore(dc); 364 /* disable idle optimizations while updating cursor */ 365 if (dc->idle_optimizations_allowed) { 366 dc_allow_idle_optimizations(dc, false); 367 reset_idle_optimizations = true; 368 } 369 370 program_cursor_attributes(dc, stream); 371 372 /* re-enable idle optimizations if necessary */ 373 if (reset_idle_optimizations && !dc->debug.disable_dmub_reallow_idle) 374 dc_allow_idle_optimizations(dc, true); 375 376 return true; 377 } 378 379 return false; 380} 381 382void program_cursor_position( 383 struct dc *dc, 384 struct dc_stream_state *stream) 385{ 386 int i; 387 struct resource_context *res_ctx; 388 struct pipe_ctx *pipe_to_program = NULL; 389 bool enable_cursor_offload = dc_dmub_srv_is_cursor_offload_enabled(dc); 390 391 if (!stream) 392 return; 393 394 res_ctx = &dc->current_state->res_ctx; 395 396 for (i = 0; i < MAX_PIPES; i++) { 397 struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i]; 398 399 if (pipe_ctx->stream != stream || 400 (!pipe_ctx->plane_res.mi && !pipe_ctx->plane_res.hubp) || 401 !pipe_ctx->plane_state || 402 (!pipe_ctx->plane_res.xfm && !pipe_ctx->plane_res.dpp) || 403 (!pipe_ctx->plane_res.ipp && !pipe_ctx->plane_res.dpp)) 404 continue; 405 406 if (!pipe_to_program) { 407 pipe_to_program = pipe_ctx; 408 409 if (enable_cursor_offload && dc->hwss.begin_cursor_offload_update) 410 dc->hwss.begin_cursor_offload_update(dc, pipe_ctx); 411 else 412 dc->hwss.cursor_lock(dc, pipe_to_program, true); 413 } 414 415 dc->hwss.set_cursor_position(pipe_ctx); 416 if (enable_cursor_offload && dc->hwss.update_cursor_offload_pipe) 417 dc->hwss.update_cursor_offload_pipe(dc, pipe_ctx); 418 419 if (dc->ctx->dmub_srv) 420 dc_send_update_cursor_info_to_dmu(pipe_ctx, i); 421 } 422 423 if (pipe_to_program) { 424 if (enable_cursor_offload && dc->hwss.commit_cursor_offload_update) 425 dc->hwss.commit_cursor_offload_update(dc, pipe_to_program); 426 else 427 dc->hwss.cursor_lock(dc, pipe_to_program, false); 428 } 429} 430 431bool dc_stream_set_cursor_position( 432 struct dc_stream_state *stream, 433 const struct dc_cursor_position *position) 434{ 435 if (NULL == stream) { 436 dm_error("DC: dc_stream is NULL!\n"); 437 return false; 438 } 439 440 if (NULL == position) { 441 dm_error("DC: cursor position is NULL!\n"); 442 return false; 443 } 444 445 stream->cursor_position = *position; 446 447 448 return true; 449} 450 451bool dc_stream_program_cursor_position( 452 struct dc_stream_state *stream, 453 const struct dc_cursor_position *position) 454{ 455 struct dc *dc; 456 bool reset_idle_optimizations = false; 457 const struct dc_cursor_position *old_position; 458 459 if (!stream) 460 return false; 461 462 old_position = &stream->cursor_position; 463 dc = stream->ctx->dc; 464 465 if (dc_stream_set_cursor_position(stream, position)) { 466 dc_z10_restore(dc); 467 468 /* disable idle optimizations if enabling cursor */ 469 if (dc->idle_optimizations_allowed && 470 (!old_position->enable || dc->debug.exit_idle_opt_for_cursor_updates) && 471 position->enable) { 472 dc_allow_idle_optimizations(dc, false); 473 reset_idle_optimizations = true; 474 } 475 476 program_cursor_position(dc, stream); 477 /* re-enable idle optimizations if necessary */ 478 if (reset_idle_optimizations && !dc->debug.disable_dmub_reallow_idle) 479 dc_allow_idle_optimizations(dc, true); 480 481 /* apply/update visual confirm */ 482 if (dc->debug.visual_confirm == VISUAL_CONFIRM_HW_CURSOR) { 483 /* update software state */ 484 int i; 485 486 for (i = 0; i < dc->res_pool->pipe_count; i++) { 487 struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; 488 489 /* adjust visual confirm color for all pipes with current stream */ 490 if (stream == pipe_ctx->stream) { 491 get_cursor_visual_confirm_color(pipe_ctx, &(pipe_ctx->visual_confirm_color)); 492 493 /* programming hardware */ 494 if (pipe_ctx->plane_state) 495 dc->hwss.update_visual_confirm_color(dc, pipe_ctx, 496 pipe_ctx->plane_res.hubp->mpcc_id); 497 } 498 } 499 } 500 501 return true; 502 } 503 504 return false; 505} 506 507bool dc_stream_add_writeback(struct dc *dc, 508 struct dc_stream_state *stream, 509 struct dc_writeback_info *wb_info) 510{ 511 bool isDrc = false; 512 int i = 0; 513 struct dwbc *dwb; 514 515 if (stream == NULL) { 516 dm_error("DC: dc_stream is NULL!\n"); 517 return false; 518 } 519 520 if (wb_info == NULL) { 521 dm_error("DC: dc_writeback_info is NULL!\n"); 522 return false; 523 } 524 525 if (wb_info->dwb_pipe_inst >= MAX_DWB_PIPES) { 526 dm_error("DC: writeback pipe is invalid!\n"); 527 return false; 528 } 529 530 dc_exit_ips_for_hw_access(dc); 531 532 wb_info->dwb_params.out_transfer_func = &stream->out_transfer_func; 533 534 dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst]; 535 dwb->dwb_is_drc = false; 536 537 /* recalculate and apply DML parameters */ 538 539 for (i = 0; i < stream->num_wb_info; i++) { 540 /*dynamic update*/ 541 if (stream->writeback_info[i].wb_enabled && 542 stream->writeback_info[i].dwb_pipe_inst == wb_info->dwb_pipe_inst) { 543 stream->writeback_info[i] = *wb_info; 544 isDrc = true; 545 } 546 } 547 548 if (!isDrc) { 549 ASSERT(stream->num_wb_info + 1 <= MAX_DWB_PIPES); 550 stream->writeback_info[stream->num_wb_info++] = *wb_info; 551 } 552 553 if (dc->hwss.enable_writeback) { 554 struct dc_stream_status *stream_status = dc_stream_get_status(stream); 555 struct dwbc *dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst]; 556 if (stream_status) 557 dwb->otg_inst = stream_status->primary_otg_inst; 558 } 559 560 if (!dc->hwss.update_bandwidth(dc, dc->current_state)) { 561 dm_error("DC: update_bandwidth failed!\n"); 562 return false; 563 } 564 565 /* enable writeback */ 566 if (dc->hwss.enable_writeback) { 567 struct dwbc *dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst]; 568 569 if (dwb->funcs->is_enabled(dwb)) { 570 /* writeback pipe already enabled, only need to update */ 571 dc->hwss.update_writeback(dc, wb_info, dc->current_state); 572 } else { 573 /* Enable writeback pipe from scratch*/ 574 dc->hwss.enable_writeback(dc, wb_info, dc->current_state); 575 } 576 } 577 578 return true; 579} 580 581bool dc_stream_fc_disable_writeback(struct dc *dc, 582 struct dc_stream_state *stream, 583 uint32_t dwb_pipe_inst) 584{ 585 struct dwbc *dwb = dc->res_pool->dwbc[dwb_pipe_inst]; 586 587 if (stream == NULL) { 588 dm_error("DC: dc_stream is NULL!\n"); 589 return false; 590 } 591 592 if (dwb_pipe_inst >= MAX_DWB_PIPES) { 593 dm_error("DC: writeback pipe is invalid!\n"); 594 return false; 595 } 596 597 if (stream->num_wb_info > MAX_DWB_PIPES) { 598 dm_error("DC: num_wb_info is invalid!\n"); 599 return false; 600 } 601 602 dc_exit_ips_for_hw_access(dc); 603 604 if (dwb->funcs->set_fc_enable) 605 dwb->funcs->set_fc_enable(dwb, DWB_FRAME_CAPTURE_DISABLE); 606 607 return true; 608} 609 610/** 611 * dc_stream_remove_writeback() - Disables writeback and removes writeback info. 612 * @dc: Display core control structure. 613 * @stream: Display core stream state. 614 * @dwb_pipe_inst: Display writeback pipe. 615 * 616 * Return: returns true on success, false otherwise. 617 */ 618bool dc_stream_remove_writeback(struct dc *dc, 619 struct dc_stream_state *stream, 620 uint32_t dwb_pipe_inst) 621{ 622 unsigned int i, j; 623 if (stream == NULL) { 624 dm_error("DC: dc_stream is NULL!\n"); 625 return false; 626 } 627 628 if (dwb_pipe_inst >= MAX_DWB_PIPES) { 629 dm_error("DC: writeback pipe is invalid!\n"); 630 return false; 631 } 632 633 if (stream->num_wb_info > MAX_DWB_PIPES) { 634 dm_error("DC: num_wb_info is invalid!\n"); 635 return false; 636 } 637 638 /* remove writeback info for disabled writeback pipes from stream */ 639 for (i = 0, j = 0; i < stream->num_wb_info; i++) { 640 if (stream->writeback_info[i].wb_enabled) { 641 642 if (stream->writeback_info[i].dwb_pipe_inst == dwb_pipe_inst) 643 stream->writeback_info[i].wb_enabled = false; 644 645 /* trim the array */ 646 if (j < i) { 647 memcpy(&stream->writeback_info[j], &stream->writeback_info[i], 648 sizeof(struct dc_writeback_info)); 649 j++; 650 } 651 } 652 } 653 stream->num_wb_info = j; 654 655 /* recalculate and apply DML parameters */ 656 if (!dc->hwss.update_bandwidth(dc, dc->current_state)) { 657 dm_error("DC: update_bandwidth failed!\n"); 658 return false; 659 } 660 661 dc_exit_ips_for_hw_access(dc); 662 663 /* disable writeback */ 664 if (dc->hwss.disable_writeback) { 665 struct dwbc *dwb = dc->res_pool->dwbc[dwb_pipe_inst]; 666 667 if (dwb->funcs->is_enabled(dwb)) 668 dc->hwss.disable_writeback(dc, dwb_pipe_inst); 669 } 670 671 return true; 672} 673 674uint32_t dc_stream_get_vblank_counter(const struct dc_stream_state *stream) 675{ 676 uint8_t i; 677 struct dc *dc = stream->ctx->dc; 678 struct resource_context *res_ctx = 679 &dc->current_state->res_ctx; 680 681 dc_exit_ips_for_hw_access(dc); 682 683 for (i = 0; i < MAX_PIPES; i++) { 684 struct timing_generator *tg = res_ctx->pipe_ctx[i].stream_res.tg; 685 686 if (res_ctx->pipe_ctx[i].stream != stream || !tg) 687 continue; 688 689 return tg->funcs->get_frame_count(tg); 690 } 691 692 return 0; 693} 694 695bool dc_stream_send_dp_sdp(const struct dc_stream_state *stream, 696 const uint8_t *custom_sdp_message, 697 unsigned int sdp_message_size) 698{ 699 int i; 700 struct dc *dc; 701 struct resource_context *res_ctx; 702 703 if (stream == NULL) { 704 dm_error("DC: dc_stream is NULL!\n"); 705 return false; 706 } 707 708 dc = stream->ctx->dc; 709 res_ctx = &dc->current_state->res_ctx; 710 711 dc_exit_ips_for_hw_access(dc); 712 713 for (i = 0; i < MAX_PIPES; i++) { 714 struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i]; 715 716 if (pipe_ctx->stream != stream) 717 continue; 718 719 if (dc->hwss.send_immediate_sdp_message != NULL) 720 dc->hwss.send_immediate_sdp_message(pipe_ctx, 721 custom_sdp_message, 722 sdp_message_size); 723 else 724 DC_LOG_WARNING("%s:send_immediate_sdp_message not implemented on this ASIC\n", 725 __func__); 726 727 } 728 729 return true; 730} 731 732bool dc_stream_get_scanoutpos(const struct dc_stream_state *stream, 733 uint32_t *v_blank_start, 734 uint32_t *v_blank_end, 735 uint32_t *h_position, 736 uint32_t *v_position) 737{ 738 uint8_t i; 739 bool ret = false; 740 struct dc *dc; 741 struct resource_context *res_ctx; 742 743 if (!stream->ctx) 744 return false; 745 746 dc = stream->ctx->dc; 747 res_ctx = &dc->current_state->res_ctx; 748 749 dc_exit_ips_for_hw_access(dc); 750 751 for (i = 0; i < MAX_PIPES; i++) { 752 struct timing_generator *tg = res_ctx->pipe_ctx[i].stream_res.tg; 753 754 if (res_ctx->pipe_ctx[i].stream != stream || !tg) 755 continue; 756 757 tg->funcs->get_scanoutpos(tg, 758 v_blank_start, 759 v_blank_end, 760 h_position, 761 v_position); 762 763 ret = true; 764 break; 765 } 766 767 return ret; 768} 769 770bool dc_stream_dmdata_status_done(struct dc *dc, struct dc_stream_state *stream) 771{ 772 struct pipe_ctx *pipe = NULL; 773 int i; 774 775 if (!dc->hwss.dmdata_status_done) 776 return false; 777 778 for (i = 0; i < MAX_PIPES; i++) { 779 pipe = &dc->current_state->res_ctx.pipe_ctx[i]; 780 if (pipe->stream == stream) 781 break; 782 } 783 /* Stream not found, by default we'll assume HUBP fetched dm data */ 784 if (i == MAX_PIPES) 785 return true; 786 787 dc_exit_ips_for_hw_access(dc); 788 789 return dc->hwss.dmdata_status_done(pipe); 790} 791 792bool dc_stream_set_dynamic_metadata(struct dc *dc, 793 struct dc_stream_state *stream, 794 struct dc_dmdata_attributes *attr) 795{ 796 struct pipe_ctx *pipe_ctx = NULL; 797 struct hubp *hubp; 798 int i; 799 800 /* Dynamic metadata is only supported on HDMI or DP */ 801 if (!dc_is_hdmi_signal(stream->signal) && !dc_is_dp_signal(stream->signal)) 802 return false; 803 804 /* Check hardware support */ 805 if (!dc->hwss.program_dmdata_engine) 806 return false; 807 808 for (i = 0; i < MAX_PIPES; i++) { 809 pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; 810 if (pipe_ctx->stream == stream) 811 break; 812 } 813 814 if (i == MAX_PIPES) 815 return false; 816 817 hubp = pipe_ctx->plane_res.hubp; 818 if (hubp == NULL) 819 return false; 820 821 pipe_ctx->stream->dmdata_address = attr->address; 822 823 dc_exit_ips_for_hw_access(dc); 824 825 dc->hwss.program_dmdata_engine(pipe_ctx); 826 827 if (hubp->funcs->dmdata_set_attributes != NULL && 828 pipe_ctx->stream->dmdata_address.quad_part != 0) { 829 hubp->funcs->dmdata_set_attributes(hubp, attr); 830 } 831 832 return true; 833} 834 835enum dc_status dc_stream_add_dsc_to_resource(struct dc *dc, 836 struct dc_state *state, 837 struct dc_stream_state *stream) 838{ 839 if (dc->res_pool->funcs->add_dsc_to_stream_resource) { 840 return dc->res_pool->funcs->add_dsc_to_stream_resource(dc, state, stream); 841 } else { 842 return DC_NO_DSC_RESOURCE; 843 } 844} 845 846struct pipe_ctx *dc_stream_get_pipe_ctx(struct dc_stream_state *stream) 847{ 848 int i = 0; 849 850 for (i = 0; i < MAX_PIPES; i++) { 851 struct pipe_ctx *pipe = &stream->ctx->dc->current_state->res_ctx.pipe_ctx[i]; 852 853 if (pipe->stream == stream) 854 return pipe; 855 } 856 857 return NULL; 858} 859 860void dc_stream_log(const struct dc *dc, const struct dc_stream_state *stream) 861{ 862 DC_LOG_DC( 863 "core_stream 0x%p: src: %d, %d, %d, %d; dst: %d, %d, %d, %d, colorSpace:%d\n", 864 stream, 865 stream->src.x, 866 stream->src.y, 867 stream->src.width, 868 stream->src.height, 869 stream->dst.x, 870 stream->dst.y, 871 stream->dst.width, 872 stream->dst.height, 873 stream->output_color_space); 874 DC_LOG_DC( 875 "\tpix_clk_khz: %d, h_total: %d, v_total: %d, pixel_encoding:%s, color_depth:%s\n", 876 stream->timing.pix_clk_100hz / 10, 877 stream->timing.h_total, 878 stream->timing.v_total, 879 dc_pixel_encoding_to_str(stream->timing.pixel_encoding), 880 dc_color_depth_to_str(stream->timing.display_color_depth)); 881 DC_LOG_DC( 882 "\tlink: %d\n", 883 stream->link->link_index); 884 885 DC_LOG_DC( 886 "\tdsc: %d, mst_pbn: %d\n", 887 stream->timing.flags.DSC, 888 stream->timing.dsc_cfg.mst_pbn); 889 890 if (stream->sink) { 891 if (stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL && 892 stream->sink->sink_signal != SIGNAL_TYPE_NONE) { 893 894 DC_LOG_DC( 895 "\tsignal: %x dispname: %s manufacturer_id: 0x%x product_id: 0x%x\n", 896 stream->signal, 897 stream->sink->edid_caps.display_name, 898 stream->sink->edid_caps.manufacturer_id, 899 stream->sink->edid_caps.product_id); 900 } 901 } 902} 903 904/* 905* dc_stream_get_3dlut() 906* Requirements: 907* 1. Is stream already owns an RMCM instance, return it. 908* 2. If it doesn't and we don't need to allocate, return NULL. 909* 3. If there's a free RMCM instance, assign to stream and return it. 910* 4. If no free RMCM instances, return NULL. 911*/ 912 913struct dc_rmcm_3dlut *dc_stream_get_3dlut_for_stream( 914 const struct dc *dc, 915 const struct dc_stream_state *stream, 916 bool allocate_one) 917{ 918 unsigned int num_rmcm = dc->caps.color.mpc.num_rmcm_3dluts; 919 920 // see if one is allocated for this stream 921 for (int i = 0; i < num_rmcm; i++) { 922 if (dc->res_pool->rmcm_3dlut[i].isInUse && 923 dc->res_pool->rmcm_3dlut[i].stream == stream) 924 return &dc->res_pool->rmcm_3dlut[i]; 925 } 926 927 //case: not found one, and dont need to allocate 928 if (!allocate_one) 929 return NULL; 930 931 //see if there is an unused 3dlut, allocate 932 for (int i = 0; i < num_rmcm; i++) { 933 if (!dc->res_pool->rmcm_3dlut[i].isInUse) { 934 dc->res_pool->rmcm_3dlut[i].isInUse = true; 935 dc->res_pool->rmcm_3dlut[i].stream = stream; 936 return &dc->res_pool->rmcm_3dlut[i]; 937 } 938 } 939 940 //dont have a 3dlut 941 return NULL; 942} 943 944 945void dc_stream_release_3dlut_for_stream( 946 const struct dc *dc, 947 const struct dc_stream_state *stream) 948{ 949 struct dc_rmcm_3dlut *rmcm_3dlut = 950 dc_stream_get_3dlut_for_stream(dc, stream, false); 951 952 if (rmcm_3dlut) { 953 rmcm_3dlut->isInUse = false; 954 rmcm_3dlut->stream = NULL; 955 rmcm_3dlut->protection_bits = 0; 956 } 957} 958 959 960void dc_stream_init_rmcm_3dlut(struct dc *dc) 961{ 962 unsigned int num_rmcm = dc->caps.color.mpc.num_rmcm_3dluts; 963 964 for (int i = 0; i < num_rmcm; i++) { 965 dc->res_pool->rmcm_3dlut[i].isInUse = false; 966 dc->res_pool->rmcm_3dlut[i].stream = NULL; 967 dc->res_pool->rmcm_3dlut[i].protection_bits = 0; 968 } 969} 970 971/* 972 * Finds the greatest index in refresh_rate_hz that contains a value <= refresh 973 */ 974static int dc_stream_get_nearest_smallest_index(struct dc_stream_state *stream, int refresh) 975{ 976 for (int i = 0; i < (LUMINANCE_DATA_TABLE_SIZE - 1); ++i) { 977 if ((stream->lumin_data.refresh_rate_hz[i] <= refresh) && (refresh < stream->lumin_data.refresh_rate_hz[i + 1])) { 978 return i; 979 } 980 } 981 return 9; 982} 983 984/* 985 * Finds a corresponding brightness for a given refresh rate between 2 given indices, where index1 < index2 986 */ 987static int dc_stream_get_brightness_millinits_linear_interpolation (struct dc_stream_state *stream, 988 int index1, 989 int index2, 990 int refresh_hz) 991{ 992 long long slope = 0; 993 if (stream->lumin_data.refresh_rate_hz[index2] != stream->lumin_data.refresh_rate_hz[index1]) { 994 slope = (stream->lumin_data.luminance_millinits[index2] - stream->lumin_data.luminance_millinits[index1]) / 995 (stream->lumin_data.refresh_rate_hz[index2] - stream->lumin_data.refresh_rate_hz[index1]); 996 } 997 998 int y_intercept = stream->lumin_data.luminance_millinits[index2] - slope * stream->lumin_data.refresh_rate_hz[index2]; 999 1000 return (y_intercept + refresh_hz * slope); 1001} 1002 1003/* 1004 * Finds a corresponding refresh rate for a given brightness between 2 given indices, where index1 < index2 1005 */ 1006static int dc_stream_get_refresh_hz_linear_interpolation (struct dc_stream_state *stream, 1007 int index1, 1008 int index2, 1009 int brightness_millinits) 1010{ 1011 long long slope = 1; 1012 if (stream->lumin_data.refresh_rate_hz[index2] != stream->lumin_data.refresh_rate_hz[index1]) { 1013 slope = (stream->lumin_data.luminance_millinits[index2] - stream->lumin_data.luminance_millinits[index1]) / 1014 (stream->lumin_data.refresh_rate_hz[index2] - stream->lumin_data.refresh_rate_hz[index1]); 1015 } 1016 1017 int y_intercept = stream->lumin_data.luminance_millinits[index2] - slope * stream->lumin_data.refresh_rate_hz[index2]; 1018 1019 return ((int)div64_s64((brightness_millinits - y_intercept), slope)); 1020} 1021 1022/* 1023 * Finds the current brightness in millinits given a refresh rate 1024 */ 1025static int dc_stream_get_brightness_millinits_from_refresh (struct dc_stream_state *stream, int refresh_hz) 1026{ 1027 int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, refresh_hz); 1028 int nearest_smallest_value = stream->lumin_data.refresh_rate_hz[nearest_smallest_index]; 1029 1030 if (nearest_smallest_value == refresh_hz) 1031 return stream->lumin_data.luminance_millinits[nearest_smallest_index]; 1032 1033 if (nearest_smallest_index >= 9) 1034 return dc_stream_get_brightness_millinits_linear_interpolation(stream, nearest_smallest_index - 1, nearest_smallest_index, refresh_hz); 1035 1036 if (nearest_smallest_value == stream->lumin_data.refresh_rate_hz[nearest_smallest_index + 1]) 1037 return stream->lumin_data.luminance_millinits[nearest_smallest_index]; 1038 1039 return dc_stream_get_brightness_millinits_linear_interpolation(stream, nearest_smallest_index, nearest_smallest_index + 1, refresh_hz); 1040} 1041 1042/* 1043 * Finds the lowest/highest refresh rate (depending on search_for_max_increase) 1044 * that can be achieved from starting_refresh_hz while staying 1045 * within flicker criteria 1046 */ 1047static int dc_stream_calculate_flickerless_refresh_rate(struct dc_stream_state *stream, 1048 int current_brightness, 1049 int starting_refresh_hz, 1050 bool is_gaming, 1051 bool search_for_max_increase) 1052{ 1053 int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, starting_refresh_hz); 1054 1055 int flicker_criteria_millinits = is_gaming ? 1056 stream->lumin_data.flicker_criteria_milli_nits_GAMING : 1057 stream->lumin_data.flicker_criteria_milli_nits_STATIC; 1058 1059 int safe_upper_bound = current_brightness + flicker_criteria_millinits; 1060 int safe_lower_bound = current_brightness - flicker_criteria_millinits; 1061 int lumin_millinits_temp = 0; 1062 1063 int offset = -1; 1064 if (search_for_max_increase) { 1065 offset = 1; 1066 } 1067 1068 /* 1069 * Increments up or down by 1 depending on search_for_max_increase 1070 */ 1071 for (int i = nearest_smallest_index; (i > 0 && !search_for_max_increase) || (i < (LUMINANCE_DATA_TABLE_SIZE - 1) && search_for_max_increase); i += offset) { 1072 1073 lumin_millinits_temp = stream->lumin_data.luminance_millinits[i + offset]; 1074 1075 if ((lumin_millinits_temp >= safe_upper_bound) || (lumin_millinits_temp <= safe_lower_bound)) { 1076 1077 if (stream->lumin_data.refresh_rate_hz[i + offset] == stream->lumin_data.refresh_rate_hz[i]) 1078 return stream->lumin_data.refresh_rate_hz[i]; 1079 1080 int target_brightness = (stream->lumin_data.luminance_millinits[i + offset] >= (current_brightness + flicker_criteria_millinits)) ? 1081 current_brightness + flicker_criteria_millinits : 1082 current_brightness - flicker_criteria_millinits; 1083 1084 int refresh = 0; 1085 1086 /* 1087 * Need the second input to be < third input for dc_stream_get_refresh_hz_linear_interpolation 1088 */ 1089 if (search_for_max_increase) 1090 refresh = dc_stream_get_refresh_hz_linear_interpolation(stream, i, i + offset, target_brightness); 1091 else 1092 refresh = dc_stream_get_refresh_hz_linear_interpolation(stream, i + offset, i, target_brightness); 1093 1094 if (refresh == stream->lumin_data.refresh_rate_hz[i + offset]) 1095 return stream->lumin_data.refresh_rate_hz[i + offset]; 1096 1097 return refresh; 1098 } 1099 } 1100 1101 if (search_for_max_increase) 1102 return (int)div64_s64((long long)stream->timing.pix_clk_100hz*100, stream->timing.v_total*(long long)stream->timing.h_total); 1103 else 1104 return stream->lumin_data.refresh_rate_hz[0]; 1105} 1106 1107/* 1108 * Gets the max delta luminance within a specified refresh range 1109 */ 1110static int dc_stream_get_max_delta_lumin_millinits(struct dc_stream_state *stream, int hz1, int hz2, bool isGaming) 1111{ 1112 int lower_refresh_brightness = dc_stream_get_brightness_millinits_from_refresh (stream, hz1); 1113 int higher_refresh_brightness = dc_stream_get_brightness_millinits_from_refresh (stream, hz2); 1114 1115 int min = lower_refresh_brightness; 1116 int max = higher_refresh_brightness; 1117 1118 /* 1119 * Static screen, therefore no need to scan through array 1120 */ 1121 if (!isGaming) { 1122 if (lower_refresh_brightness >= higher_refresh_brightness) { 1123 return lower_refresh_brightness - higher_refresh_brightness; 1124 } 1125 return higher_refresh_brightness - lower_refresh_brightness; 1126 } 1127 1128 min = MIN(lower_refresh_brightness, higher_refresh_brightness); 1129 max = MAX(lower_refresh_brightness, higher_refresh_brightness); 1130 1131 int nearest_smallest_index = dc_stream_get_nearest_smallest_index(stream, hz1); 1132 1133 for (; nearest_smallest_index < (LUMINANCE_DATA_TABLE_SIZE - 1) && 1134 stream->lumin_data.refresh_rate_hz[nearest_smallest_index + 1] <= hz2 ; nearest_smallest_index++) { 1135 min = MIN(min, stream->lumin_data.luminance_millinits[nearest_smallest_index + 1]); 1136 max = MAX(max, stream->lumin_data.luminance_millinits[nearest_smallest_index + 1]); 1137 } 1138 1139 return (max - min); 1140} 1141 1142/* 1143 * Determines the max flickerless instant vtotal delta for a stream. 1144 * Determines vtotal increase/decrease based on the bool "increase" 1145 */ 1146static unsigned int dc_stream_get_max_flickerless_instant_vtotal_delta(struct dc_stream_state *stream, bool is_gaming, bool increase) 1147{ 1148 if (stream->timing.v_total * stream->timing.h_total == 0) 1149 return 0; 1150 1151 int current_refresh_hz = (int)div64_s64((long long)stream->timing.pix_clk_100hz*100, stream->timing.v_total*(long long)stream->timing.h_total); 1152 1153 int safe_refresh_hz = dc_stream_calculate_flickerless_refresh_rate(stream, 1154 dc_stream_get_brightness_millinits_from_refresh(stream, current_refresh_hz), 1155 current_refresh_hz, 1156 is_gaming, 1157 increase); 1158 1159 int safe_refresh_v_total = (int)div64_s64((long long)stream->timing.pix_clk_100hz*100, safe_refresh_hz*(long long)stream->timing.h_total); 1160 1161 if (increase) 1162 return (((int) stream->timing.v_total - safe_refresh_v_total) >= 0) ? (stream->timing.v_total - safe_refresh_v_total) : 0; 1163 1164 return ((safe_refresh_v_total - (int) stream->timing.v_total) >= 0) ? (safe_refresh_v_total - stream->timing.v_total) : 0; 1165} 1166 1167/* 1168 * Finds the highest refresh rate that can be achieved 1169 * from starting_refresh_hz while staying within flicker criteria 1170 */ 1171int dc_stream_calculate_max_flickerless_refresh_rate(struct dc_stream_state *stream, int starting_refresh_hz, bool is_gaming) 1172{ 1173 if (!stream->lumin_data.is_valid) 1174 return 0; 1175 1176 int current_brightness = dc_stream_get_brightness_millinits_from_refresh(stream, starting_refresh_hz); 1177 1178 return dc_stream_calculate_flickerless_refresh_rate(stream, 1179 current_brightness, 1180 starting_refresh_hz, 1181 is_gaming, 1182 true); 1183} 1184 1185/* 1186 * Finds the lowest refresh rate that can be achieved 1187 * from starting_refresh_hz while staying within flicker criteria 1188 */ 1189int dc_stream_calculate_min_flickerless_refresh_rate(struct dc_stream_state *stream, int starting_refresh_hz, bool is_gaming) 1190{ 1191 if (!stream->lumin_data.is_valid) 1192 return 0; 1193 1194 int current_brightness = dc_stream_get_brightness_millinits_from_refresh(stream, starting_refresh_hz); 1195 1196 return dc_stream_calculate_flickerless_refresh_rate(stream, 1197 current_brightness, 1198 starting_refresh_hz, 1199 is_gaming, 1200 false); 1201} 1202 1203/* 1204 * Determines if there will be a flicker when moving between 2 refresh rates 1205 */ 1206bool dc_stream_is_refresh_rate_range_flickerless(struct dc_stream_state *stream, int hz1, int hz2, bool is_gaming) 1207{ 1208 1209 /* 1210 * Assume that we wont flicker if there is invalid data 1211 */ 1212 if (!stream->lumin_data.is_valid) 1213 return false; 1214 1215 int dl = dc_stream_get_max_delta_lumin_millinits(stream, hz1, hz2, is_gaming); 1216 1217 int flicker_criteria_millinits = (is_gaming) ? 1218 stream->lumin_data.flicker_criteria_milli_nits_GAMING : 1219 stream->lumin_data.flicker_criteria_milli_nits_STATIC; 1220 1221 return (dl <= flicker_criteria_millinits); 1222} 1223 1224/* 1225 * Determines the max instant vtotal delta increase that can be applied without 1226 * flickering for a given stream 1227 */ 1228unsigned int dc_stream_get_max_flickerless_instant_vtotal_decrease(struct dc_stream_state *stream, 1229 bool is_gaming) 1230{ 1231 if (!stream->lumin_data.is_valid) 1232 return 0; 1233 1234 return dc_stream_get_max_flickerless_instant_vtotal_delta(stream, is_gaming, true); 1235} 1236 1237/* 1238 * Determines the max instant vtotal delta decrease that can be applied without 1239 * flickering for a given stream 1240 */ 1241unsigned int dc_stream_get_max_flickerless_instant_vtotal_increase(struct dc_stream_state *stream, 1242 bool is_gaming) 1243{ 1244 if (!stream->lumin_data.is_valid) 1245 return 0; 1246 1247 return dc_stream_get_max_flickerless_instant_vtotal_delta(stream, is_gaming, false); 1248} 1249 1250bool dc_stream_is_cursor_limit_pending(struct dc *dc, struct dc_stream_state *stream) 1251{ 1252 bool is_limit_pending = false; 1253 1254 if (dc->current_state) 1255 is_limit_pending = dc_state_get_stream_cursor_subvp_limit(stream, dc->current_state); 1256 1257 return is_limit_pending; 1258} 1259 1260bool dc_stream_can_clear_cursor_limit(struct dc *dc, struct dc_stream_state *stream) 1261{ 1262 bool can_clear_limit = false; 1263 1264 if (dc->current_state) 1265 can_clear_limit = dc_state_get_stream_cursor_subvp_limit(stream, dc->current_state) && 1266 (stream->hw_cursor_req || 1267 !stream->cursor_position.enable || 1268 dc_stream_check_cursor_attributes(stream, dc->current_state, &stream->cursor_attributes)); 1269 1270 return can_clear_limit; 1271}
~/
Read-only file preview.