3608 lines · c
1// SPDX-License-Identifier: MIT2/*3 * Copyright 2022 Advanced Micro Devices, Inc.4 *5 * Permission is hereby granted, free of charge, to any person obtaining a6 * copy of this software and associated documentation files (the "Software"),7 * to deal in the Software without restriction, including without limitation8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,9 * and/or sell copies of the Software, and to permit persons to whom the10 * Software is furnished to do so, subject to the following conditions:11 *12 * The above copyright notice and this permission notice shall be included in13 * all copies or substantial portions of the Software.14 *15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL18 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR21 * OTHER DEALINGS IN THE SOFTWARE.22 *23 * Authors: AMD24 *25 */26#include "dcn32_fpu.h"27#include "dcn32/dcn32_resource.h"28#include "dcn20/dcn20_resource.h"29#include "display_mode_vba_util_32.h"30#include "dml/dcn32/display_mode_vba_32.h"31// We need this includes for WATERMARKS_* defines32#include "clk_mgr/dcn32/dcn32_smu13_driver_if.h"33#include "dcn30/dcn30_resource.h"34#include "link.h"35#include "dc_state_priv.h"36 37#define DC_LOGGER_INIT(logger)38 39static const struct subvp_high_refresh_list subvp_high_refresh_list = {40 .min_refresh = 120,41 .max_refresh = 175,42 .res = {43 {.width = 3840, .height = 2160, },44 {.width = 3440, .height = 1440, },45 {.width = 2560, .height = 1440, },46 {.width = 1920, .height = 1080, }},47};48 49static const struct subvp_active_margin_list subvp_active_margin_list = {50 .min_refresh = 55,51 .max_refresh = 65,52 .res = {53 {.width = 2560, .height = 1440, },54 {.width = 1920, .height = 1080, }},55};56 57struct _vcs_dpi_ip_params_st dcn3_2_ip = {58 .gpuvm_enable = 0,59 .gpuvm_max_page_table_levels = 4,60 .hostvm_enable = 0,61 .rob_buffer_size_kbytes = 128,62 .det_buffer_size_kbytes = DCN3_2_DEFAULT_DET_SIZE,63 .config_return_buffer_size_in_kbytes = 1280,64 .compressed_buffer_segment_size_in_kbytes = 64,65 .meta_fifo_size_in_kentries = 22,66 .zero_size_buffer_entries = 512,67 .compbuf_reserved_space_64b = 256,68 .compbuf_reserved_space_zs = 64,69 .dpp_output_buffer_pixels = 2560,70 .opp_output_buffer_lines = 1,71 .pixel_chunk_size_kbytes = 8,72 .alpha_pixel_chunk_size_kbytes = 4,73 .min_pixel_chunk_size_bytes = 1024,74 .dcc_meta_buffer_size_bytes = 6272,75 .meta_chunk_size_kbytes = 2,76 .min_meta_chunk_size_bytes = 256,77 .writeback_chunk_size_kbytes = 8,78 .ptoi_supported = false,79 .num_dsc = 4,80 .maximum_dsc_bits_per_component = 12,81 .maximum_pixels_per_line_per_dsc_unit = 6016,82 .dsc422_native_support = true,83 .is_line_buffer_bpp_fixed = true,84 .line_buffer_fixed_bpp = 57,85 .line_buffer_size_bits = 1171920,86 .max_line_buffer_lines = 32,87 .writeback_interface_buffer_size_kbytes = 90,88 .max_num_dpp = 4,89 .max_num_otg = 4,90 .max_num_hdmi_frl_outputs = 1,91 .max_num_wb = 1,92 .max_dchub_pscl_bw_pix_per_clk = 4,93 .max_pscl_lb_bw_pix_per_clk = 2,94 .max_lb_vscl_bw_pix_per_clk = 4,95 .max_vscl_hscl_bw_pix_per_clk = 4,96 .max_hscl_ratio = 6,97 .max_vscl_ratio = 6,98 .max_hscl_taps = 8,99 .max_vscl_taps = 8,100 .dpte_buffer_size_in_pte_reqs_luma = 64,101 .dpte_buffer_size_in_pte_reqs_chroma = 34,102 .dispclk_ramp_margin_percent = 1,103 .max_inter_dcn_tile_repeaters = 8,104 .cursor_buffer_size = 16,105 .cursor_chunk_size = 2,106 .writeback_line_buffer_buffer_size = 0,107 .writeback_min_hscl_ratio = 1,108 .writeback_min_vscl_ratio = 1,109 .writeback_max_hscl_ratio = 1,110 .writeback_max_vscl_ratio = 1,111 .writeback_max_hscl_taps = 1,112 .writeback_max_vscl_taps = 1,113 .dppclk_delay_subtotal = 47,114 .dppclk_delay_scl = 50,115 .dppclk_delay_scl_lb_only = 16,116 .dppclk_delay_cnvc_formatter = 28,117 .dppclk_delay_cnvc_cursor = 6,118 .dispclk_delay_subtotal = 125,119 .dynamic_metadata_vm_enabled = false,120 .odm_combine_4to1_supported = false,121 .dcc_supported = true,122 .max_num_dp2p0_outputs = 2,123 .max_num_dp2p0_streams = 4,124};125 126struct _vcs_dpi_soc_bounding_box_st dcn3_2_soc = {127 .clock_limits = {128 {129 .state = 0,130 .dcfclk_mhz = 1564.0,131 .fabricclk_mhz = 2500.0,132 .dispclk_mhz = 2150.0,133 .dppclk_mhz = 2150.0,134 .phyclk_mhz = 810.0,135 .phyclk_d18_mhz = 667.0,136 .phyclk_d32_mhz = 625.0,137 .socclk_mhz = 1200.0,138 .dscclk_mhz = 716.667,139 .dram_speed_mts = 18000.0,140 .dtbclk_mhz = 1564.0,141 },142 },143 .num_states = 1,144 .sr_exit_time_us = 42.97,145 .sr_enter_plus_exit_time_us = 49.94,146 .sr_exit_z8_time_us = 285.0,147 .sr_enter_plus_exit_z8_time_us = 320,148 .writeback_latency_us = 12.0,149 .round_trip_ping_latency_dcfclk_cycles = 263,150 .urgent_latency_pixel_data_only_us = 4.0,151 .urgent_latency_pixel_mixed_with_vm_data_us = 4.0,152 .urgent_latency_vm_data_only_us = 4.0,153 .fclk_change_latency_us = 25,154 .usr_retraining_latency_us = 2,155 .smn_latency_us = 2,156 .mall_allocated_for_dcn_mbytes = 64,157 .urgent_out_of_order_return_per_channel_pixel_only_bytes = 4096,158 .urgent_out_of_order_return_per_channel_pixel_and_vm_bytes = 4096,159 .urgent_out_of_order_return_per_channel_vm_only_bytes = 4096,160 .pct_ideal_sdp_bw_after_urgent = 90.0,161 .pct_ideal_fabric_bw_after_urgent = 67.0,162 .pct_ideal_dram_sdp_bw_after_urgent_pixel_only = 20.0,163 .pct_ideal_dram_sdp_bw_after_urgent_pixel_and_vm = 60.0,164 .pct_ideal_dram_sdp_bw_after_urgent_vm_only = 30.0,165 .pct_ideal_dram_bw_after_urgent_strobe = 67.0,166 .max_avg_sdp_bw_use_normal_percent = 80.0,167 .max_avg_fabric_bw_use_normal_percent = 60.0,168 .max_avg_dram_bw_use_normal_strobe_percent = 50.0,169 .max_avg_dram_bw_use_normal_percent = 15.0,170 .num_chans = 24,171 .dram_channel_width_bytes = 2,172 .fabric_datapath_to_dcn_data_return_bytes = 64,173 .return_bus_width_bytes = 64,174 .downspread_percent = 0.38,175 .dcn_downspread_percent = 0.5,176 .dram_clock_change_latency_us = 400,177 .dispclk_dppclk_vco_speed_mhz = 4300.0,178 .do_urgent_latency_adjustment = true,179 .urgent_latency_adjustment_fabric_clock_component_us = 1.0,180 .urgent_latency_adjustment_fabric_clock_reference_mhz = 3000,181};182 183static bool dcn32_apply_merge_split_flags_helper(struct dc *dc, struct dc_state *context,184 bool *repopulate_pipes, int *split, bool *merge);185 186void dcn32_build_wm_range_table_fpu(struct clk_mgr_internal *clk_mgr)187{188 /* defaults */189 double pstate_latency_us = clk_mgr->base.ctx->dc->dml.soc.dram_clock_change_latency_us;190 double fclk_change_latency_us = clk_mgr->base.ctx->dc->dml.soc.fclk_change_latency_us;191 double sr_exit_time_us = clk_mgr->base.ctx->dc->dml.soc.sr_exit_time_us;192 double sr_enter_plus_exit_time_us = clk_mgr->base.ctx->dc->dml.soc.sr_enter_plus_exit_time_us;193 /* For min clocks use as reported by PM FW and report those as min */194 uint16_t min_uclk_mhz = clk_mgr->base.bw_params->clk_table.entries[0].memclk_mhz;195 uint16_t min_dcfclk_mhz = clk_mgr->base.bw_params->clk_table.entries[0].dcfclk_mhz;196 uint16_t setb_min_uclk_mhz = min_uclk_mhz;197 uint16_t dcfclk_mhz_for_the_second_state = clk_mgr->base.ctx->dc->dml.soc.clock_limits[2].dcfclk_mhz;198 199 dc_assert_fp_enabled();200 201 /* For Set B ranges use min clocks state 2 when available, and report those to PM FW */202 if (dcfclk_mhz_for_the_second_state)203 clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.min_dcfclk = dcfclk_mhz_for_the_second_state;204 else205 clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.min_dcfclk = clk_mgr->base.bw_params->clk_table.entries[0].dcfclk_mhz;206 207 if (clk_mgr->base.bw_params->clk_table.entries[2].memclk_mhz)208 setb_min_uclk_mhz = clk_mgr->base.bw_params->clk_table.entries[2].memclk_mhz;209 210 /* Set A - Normal - default values */211 clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].valid = true;212 clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].dml_input.pstate_latency_us = pstate_latency_us;213 clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].dml_input.fclk_change_latency_us = fclk_change_latency_us;214 clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].dml_input.sr_exit_time_us = sr_exit_time_us;215 clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].dml_input.sr_enter_plus_exit_time_us = sr_enter_plus_exit_time_us;216 clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.wm_type = WATERMARKS_CLOCK_RANGE;217 clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.min_dcfclk = min_dcfclk_mhz;218 clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.max_dcfclk = 0xFFFF;219 clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.min_uclk = min_uclk_mhz;220 clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.max_uclk = 0xFFFF;221 222 /* Set B - Performance - higher clocks, using DPM[2] DCFCLK and UCLK */223 clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].valid = true;224 clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].dml_input.pstate_latency_us = pstate_latency_us;225 clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].dml_input.fclk_change_latency_us = fclk_change_latency_us;226 clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].dml_input.sr_exit_time_us = sr_exit_time_us;227 clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].dml_input.sr_enter_plus_exit_time_us = sr_enter_plus_exit_time_us;228 clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.wm_type = WATERMARKS_CLOCK_RANGE;229 clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.max_dcfclk = 0xFFFF;230 clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.min_uclk = setb_min_uclk_mhz;231 clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.max_uclk = 0xFFFF;232 233 /* Set C - Dummy P-State - P-State latency set to "dummy p-state" value */234 /* 'DalDummyClockChangeLatencyNs' registry key option set to 0x7FFFFFFF can be used to disable Set C for dummy p-state */235 if (clk_mgr->base.ctx->dc->bb_overrides.dummy_clock_change_latency_ns != 0x7FFFFFFF) {236 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].valid = true;237 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].dml_input.pstate_latency_us = 50;238 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].dml_input.fclk_change_latency_us = fclk_change_latency_us;239 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].dml_input.sr_exit_time_us = sr_exit_time_us;240 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].dml_input.sr_enter_plus_exit_time_us = sr_enter_plus_exit_time_us;241 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.wm_type = WATERMARKS_DUMMY_PSTATE;242 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.min_dcfclk = min_dcfclk_mhz;243 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.max_dcfclk = 0xFFFF;244 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.min_uclk = min_uclk_mhz;245 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.max_uclk = 0xFFFF;246 clk_mgr->base.bw_params->dummy_pstate_table[0].dram_speed_mts = clk_mgr->base.bw_params->clk_table.entries[0].memclk_mhz * 16;247 clk_mgr->base.bw_params->dummy_pstate_table[0].dummy_pstate_latency_us = 50;248 clk_mgr->base.bw_params->dummy_pstate_table[1].dram_speed_mts = clk_mgr->base.bw_params->clk_table.entries[1].memclk_mhz * 16;249 clk_mgr->base.bw_params->dummy_pstate_table[1].dummy_pstate_latency_us = 9;250 clk_mgr->base.bw_params->dummy_pstate_table[2].dram_speed_mts = clk_mgr->base.bw_params->clk_table.entries[2].memclk_mhz * 16;251 clk_mgr->base.bw_params->dummy_pstate_table[2].dummy_pstate_latency_us = 8;252 clk_mgr->base.bw_params->dummy_pstate_table[3].dram_speed_mts = clk_mgr->base.bw_params->clk_table.entries[3].memclk_mhz * 16;253 clk_mgr->base.bw_params->dummy_pstate_table[3].dummy_pstate_latency_us = 5;254 }255 /* Set D - MALL - SR enter and exit time specific to MALL, TBD after bringup or later phase for now use DRAM values / 2 */256 /* For MALL DRAM clock change latency is N/A, for watermak calculations use lowest value dummy P state latency */257 clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].valid = true;258 clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].dml_input.pstate_latency_us = clk_mgr->base.bw_params->dummy_pstate_table[3].dummy_pstate_latency_us;259 clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].dml_input.fclk_change_latency_us = fclk_change_latency_us;260 clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].dml_input.sr_exit_time_us = sr_exit_time_us / 2; // TBD261 clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].dml_input.sr_enter_plus_exit_time_us = sr_enter_plus_exit_time_us / 2; // TBD262 clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.wm_type = WATERMARKS_MALL;263 clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.min_dcfclk = min_dcfclk_mhz;264 clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.max_dcfclk = 0xFFFF;265 clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.min_uclk = min_uclk_mhz;266 clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.max_uclk = 0xFFFF;267}268 269/*270 * Finds dummy_latency_index when MCLK switching using firmware based271 * vblank stretch is enabled. This function will iterate through the272 * table of dummy pstate latencies until the lowest value that allows273 * dm_allow_self_refresh_and_mclk_switch to happen is found274 */275int dcn32_find_dummy_latency_index_for_fw_based_mclk_switch(struct dc *dc,276 struct dc_state *context,277 display_e2e_pipe_params_st *pipes,278 int pipe_cnt,279 int vlevel)280{281 const int max_latency_table_entries = 4;282 struct vba_vars_st *vba = &context->bw_ctx.dml.vba;283 int dummy_latency_index = 0;284 enum clock_change_support temp_clock_change_support = vba->DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb];285 286 dc_assert_fp_enabled();287 288 while (dummy_latency_index < max_latency_table_entries) {289 if (temp_clock_change_support != dm_dram_clock_change_unsupported)290 vba->DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] = temp_clock_change_support;291 context->bw_ctx.dml.soc.dram_clock_change_latency_us =292 dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dummy_pstate_latency_us;293 dcn32_internal_validate_bw(dc, context, pipes, &pipe_cnt, &vlevel, false);294 295 /* for subvp + DRR case, if subvp pipes are still present we support pstate */296 if (vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_unsupported &&297 dcn32_subvp_in_use(dc, context))298 vba->DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] = temp_clock_change_support;299 300 if (vlevel < context->bw_ctx.dml.vba.soc.num_states &&301 vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] != dm_dram_clock_change_unsupported)302 break;303 304 dummy_latency_index++;305 }306 307 if (dummy_latency_index == max_latency_table_entries) {308 ASSERT(dummy_latency_index != max_latency_table_entries);309 /* If the execution gets here, it means dummy p_states are310 * not possible. This should never happen and would mean311 * something is severely wrong.312 * Here we reset dummy_latency_index to 3, because it is313 * better to have underflows than system crashes.314 */315 dummy_latency_index = max_latency_table_entries - 1;316 }317 318 return dummy_latency_index;319}320 321/**322 * dcn32_helper_populate_phantom_dlg_params - Get DLG params for phantom pipes323 * and populate pipe_ctx with those params.324 * @dc: [in] current dc state325 * @context: [in] new dc state326 * @pipes: [in] DML pipe params array327 * @pipe_cnt: [in] DML pipe count328 *329 * This function must be called AFTER the phantom pipes are added to context330 * and run through DML (so that the DLG params for the phantom pipes can be331 * populated), and BEFORE we program the timing for the phantom pipes.332 */333void dcn32_helper_populate_phantom_dlg_params(struct dc *dc,334 struct dc_state *context,335 display_e2e_pipe_params_st *pipes,336 int pipe_cnt)337{338 uint32_t i, pipe_idx;339 340 dc_assert_fp_enabled();341 342 for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {343 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];344 345 if (!pipe->stream)346 continue;347 348 if (pipe->plane_state && dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM) {349 pipes[pipe_idx].pipe.dest.vstartup_start =350 get_vstartup(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);351 pipes[pipe_idx].pipe.dest.vupdate_offset =352 get_vupdate_offset(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);353 pipes[pipe_idx].pipe.dest.vupdate_width =354 get_vupdate_width(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);355 pipes[pipe_idx].pipe.dest.vready_offset =356 get_vready_offset(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);357 pipe->pipe_dlg_param = pipes[pipe_idx].pipe.dest;358 }359 pipe_idx++;360 }361}362 363static float calculate_net_bw_in_kbytes_sec(struct _vcs_dpi_voltage_scaling_st *entry)364{365 float memory_bw_kbytes_sec;366 float fabric_bw_kbytes_sec;367 float sdp_bw_kbytes_sec;368 float limiting_bw_kbytes_sec;369 370 memory_bw_kbytes_sec = entry->dram_speed_mts *371 dcn3_2_soc.num_chans *372 dcn3_2_soc.dram_channel_width_bytes *373 ((float)dcn3_2_soc.pct_ideal_dram_sdp_bw_after_urgent_pixel_only / 100);374 375 fabric_bw_kbytes_sec = entry->fabricclk_mhz *376 dcn3_2_soc.return_bus_width_bytes *377 ((float)dcn3_2_soc.pct_ideal_fabric_bw_after_urgent / 100);378 379 sdp_bw_kbytes_sec = entry->dcfclk_mhz *380 dcn3_2_soc.return_bus_width_bytes *381 ((float)dcn3_2_soc.pct_ideal_sdp_bw_after_urgent / 100);382 383 limiting_bw_kbytes_sec = memory_bw_kbytes_sec;384 385 if (fabric_bw_kbytes_sec < limiting_bw_kbytes_sec)386 limiting_bw_kbytes_sec = fabric_bw_kbytes_sec;387 388 if (sdp_bw_kbytes_sec < limiting_bw_kbytes_sec)389 limiting_bw_kbytes_sec = sdp_bw_kbytes_sec;390 391 return limiting_bw_kbytes_sec;392}393 394static void get_optimal_ntuple(struct _vcs_dpi_voltage_scaling_st *entry)395{396 if (entry->dcfclk_mhz > 0) {397 float bw_on_sdp = entry->dcfclk_mhz * dcn3_2_soc.return_bus_width_bytes * ((float)dcn3_2_soc.pct_ideal_sdp_bw_after_urgent / 100);398 399 entry->fabricclk_mhz = bw_on_sdp / (dcn3_2_soc.return_bus_width_bytes * ((float)dcn3_2_soc.pct_ideal_fabric_bw_after_urgent / 100));400 entry->dram_speed_mts = bw_on_sdp / (dcn3_2_soc.num_chans *401 dcn3_2_soc.dram_channel_width_bytes * ((float)dcn3_2_soc.pct_ideal_dram_sdp_bw_after_urgent_pixel_only / 100));402 } else if (entry->fabricclk_mhz > 0) {403 float bw_on_fabric = entry->fabricclk_mhz * dcn3_2_soc.return_bus_width_bytes * ((float)dcn3_2_soc.pct_ideal_fabric_bw_after_urgent / 100);404 405 entry->dcfclk_mhz = bw_on_fabric / (dcn3_2_soc.return_bus_width_bytes * ((float)dcn3_2_soc.pct_ideal_sdp_bw_after_urgent / 100));406 entry->dram_speed_mts = bw_on_fabric / (dcn3_2_soc.num_chans *407 dcn3_2_soc.dram_channel_width_bytes * ((float)dcn3_2_soc.pct_ideal_dram_sdp_bw_after_urgent_pixel_only / 100));408 } else if (entry->dram_speed_mts > 0) {409 float bw_on_dram = entry->dram_speed_mts * dcn3_2_soc.num_chans *410 dcn3_2_soc.dram_channel_width_bytes * ((float)dcn3_2_soc.pct_ideal_dram_sdp_bw_after_urgent_pixel_only / 100);411 412 entry->fabricclk_mhz = bw_on_dram / (dcn3_2_soc.return_bus_width_bytes * ((float)dcn3_2_soc.pct_ideal_fabric_bw_after_urgent / 100));413 entry->dcfclk_mhz = bw_on_dram / (dcn3_2_soc.return_bus_width_bytes * ((float)dcn3_2_soc.pct_ideal_sdp_bw_after_urgent / 100));414 }415}416 417static void insert_entry_into_table_sorted(struct _vcs_dpi_voltage_scaling_st *table,418 unsigned int *num_entries,419 struct _vcs_dpi_voltage_scaling_st *entry)420{421 int i = 0;422 int index = 0;423 424 dc_assert_fp_enabled();425 426 if (*num_entries == 0) {427 table[0] = *entry;428 (*num_entries)++;429 } else {430 while (entry->net_bw_in_kbytes_sec > table[index].net_bw_in_kbytes_sec) {431 index++;432 if (index >= *num_entries)433 break;434 }435 436 for (i = *num_entries; i > index; i--)437 table[i] = table[i - 1];438 439 table[index] = *entry;440 (*num_entries)++;441 }442}443 444/**445 * dcn32_set_phantom_stream_timing - Set timing params for the phantom stream446 * @dc: current dc state447 * @context: new dc state448 * @ref_pipe: Main pipe for the phantom stream449 * @phantom_stream: target phantom stream state450 * @pipes: DML pipe params451 * @pipe_cnt: number of DML pipes452 * @dc_pipe_idx: DC pipe index for the main pipe (i.e. ref_pipe)453 *454 * Set timing params of the phantom stream based on calculated output from DML.455 * This function first gets the DML pipe index using the DC pipe index, then456 * calls into DML (get_subviewport_lines_needed_in_mall) to get the number of457 * lines required for SubVP MCLK switching and assigns to the phantom stream458 * accordingly.459 *460 * - The number of SubVP lines calculated in DML does not take into account461 * FW processing delays and required pstate allow width, so we must include462 * that separately.463 *464 * - Set phantom backporch = vstartup of main pipe465 */466void dcn32_set_phantom_stream_timing(struct dc *dc,467 struct dc_state *context,468 struct pipe_ctx *ref_pipe,469 struct dc_stream_state *phantom_stream,470 display_e2e_pipe_params_st *pipes,471 unsigned int pipe_cnt,472 unsigned int dc_pipe_idx)473{474 unsigned int i, pipe_idx;475 struct pipe_ctx *pipe;476 uint32_t phantom_vactive, phantom_bp, pstate_width_fw_delay_lines;477 unsigned int num_dpp;478 unsigned int vlevel = context->bw_ctx.dml.vba.VoltageLevel;479 unsigned int dcfclk = context->bw_ctx.dml.vba.DCFCLKState[vlevel][context->bw_ctx.dml.vba.maxMpcComb];480 unsigned int socclk = context->bw_ctx.dml.vba.SOCCLKPerState[vlevel];481 struct vba_vars_st *vba = &context->bw_ctx.dml.vba;482 struct dc_stream_state *main_stream = ref_pipe->stream;483 484 dc_assert_fp_enabled();485 486 // Find DML pipe index (pipe_idx) using dc_pipe_idx487 for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {488 pipe = &context->res_ctx.pipe_ctx[i];489 490 if (!pipe->stream)491 continue;492 493 if (i == dc_pipe_idx)494 break;495 496 pipe_idx++;497 }498 499 // Calculate lines required for pstate allow width and FW processing delays500 pstate_width_fw_delay_lines = ((double)(dc->caps.subvp_fw_processing_delay_us +501 dc->caps.subvp_pstate_allow_width_us) / 1000000) *502 (ref_pipe->stream->timing.pix_clk_100hz * 100) /503 (double)ref_pipe->stream->timing.h_total;504 505 // Update clks_cfg for calling into recalculate506 pipes[0].clks_cfg.voltage = vlevel;507 pipes[0].clks_cfg.dcfclk_mhz = dcfclk;508 pipes[0].clks_cfg.socclk_mhz = socclk;509 510 // DML calculation for MALL region doesn't take into account FW delay511 // and required pstate allow width for multi-display cases512 /* Add 16 lines margin to the MALL REGION because SUB_VP_START_LINE must be aligned513 * to 2 swaths (i.e. 16 lines)514 */515 phantom_vactive = get_subviewport_lines_needed_in_mall(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx) +516 pstate_width_fw_delay_lines + dc->caps.subvp_swath_height_margin_lines;517 518 // W/A for DCC corruption with certain high resolution timings.519 // Determing if pipesplit is used. If so, add meta_row_height to the phantom vactive.520 num_dpp = vba->NoOfDPP[vba->VoltageLevel][vba->maxMpcComb][vba->pipe_plane[pipe_idx]];521 phantom_vactive += num_dpp > 1 ? vba->meta_row_height[vba->pipe_plane[pipe_idx]] : 0;522 523 /* dc->debug.subvp_extra_lines 0 by default*/524 phantom_vactive += dc->debug.subvp_extra_lines;525 526 // For backporch of phantom pipe, use vstartup of the main pipe527 phantom_bp = get_vstartup(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);528 529 phantom_stream->dst.y = 0;530 phantom_stream->dst.height = phantom_vactive;531 /* When scaling, DML provides the end to end required number of lines for MALL.532 * dst.height is always correct for this case, but src.height is not which causes a533 * delta between main and phantom pipe scaling outputs. Need to adjust src.height on534 * phantom for this case.535 */536 phantom_stream->src.y = 0;537 phantom_stream->src.height = (double)phantom_vactive * (double)main_stream->src.height / (double)main_stream->dst.height;538 539 phantom_stream->timing.v_addressable = phantom_vactive;540 phantom_stream->timing.v_front_porch = 1;541 phantom_stream->timing.v_total = phantom_stream->timing.v_addressable +542 phantom_stream->timing.v_front_porch +543 phantom_stream->timing.v_sync_width +544 phantom_bp;545 phantom_stream->timing.flags.DSC = 0; // Don't need DSC for phantom timing546}547 548/**549 * dcn32_get_num_free_pipes - Calculate number of free pipes550 * @dc: current dc state551 * @context: new dc state552 *553 * This function assumes that a "used" pipe is a pipe that has554 * both a stream and a plane assigned to it.555 *556 * Return: Number of free pipes available in the context557 */558static unsigned int dcn32_get_num_free_pipes(struct dc *dc, struct dc_state *context)559{560 unsigned int i;561 unsigned int free_pipes = 0;562 unsigned int num_pipes = 0;563 564 for (i = 0; i < dc->res_pool->pipe_count; i++) {565 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];566 567 if (pipe->stream && !pipe->top_pipe) {568 while (pipe) {569 num_pipes++;570 pipe = pipe->bottom_pipe;571 }572 }573 }574 575 free_pipes = dc->res_pool->pipe_count - num_pipes;576 return free_pipes;577}578 579/**580 * dcn32_assign_subvp_pipe - Function to decide which pipe will use Sub-VP.581 * @dc: current dc state582 * @context: new dc state583 * @index: [out] dc pipe index for the pipe chosen to have phantom pipes assigned584 *585 * We enter this function if we are Sub-VP capable (i.e. enough pipes available)586 * and regular P-State switching (i.e. VACTIVE/VBLANK) is not supported, or if587 * we are forcing SubVP P-State switching on the current config.588 *589 * The number of pipes used for the chosen surface must be less than or equal to the590 * number of free pipes available.591 *592 * In general we choose surfaces with the longest frame time first (better for SubVP + VBLANK).593 * For multi-display cases the ActiveDRAMClockChangeMargin doesn't provide enough info on its own594 * for determining which should be the SubVP pipe (need a way to determine if a pipe / plane doesn't595 * support MCLK switching naturally [i.e. ACTIVE or VBLANK]).596 *597 * Return: True if a valid pipe assignment was found for Sub-VP. Otherwise false.598 */599static bool dcn32_assign_subvp_pipe(struct dc *dc,600 struct dc_state *context,601 unsigned int *index)602{603 unsigned int i, pipe_idx;604 unsigned int max_frame_time = 0;605 bool valid_assignment_found = false;606 unsigned int free_pipes = dcn32_get_num_free_pipes(dc, context);607 struct vba_vars_st *vba = &context->bw_ctx.dml.vba;608 609 for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {610 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];611 unsigned int num_pipes = 0;612 unsigned int refresh_rate = 0;613 614 if (!pipe->stream)615 continue;616 617 // Round up618 refresh_rate = (pipe->stream->timing.pix_clk_100hz * 100 +619 pipe->stream->timing.v_total * pipe->stream->timing.h_total - 1)620 / (double)(pipe->stream->timing.v_total * pipe->stream->timing.h_total);621 /* SubVP pipe candidate requirements:622 * - Refresh rate < 120hz623 * - Not able to switch in vactive naturally (switching in active means the624 * DET provides enough buffer to hide the P-State switch latency -- trying625 * to combine this with SubVP can cause issues with the scheduling).626 * - Not TMZ surface627 */628 if (pipe->plane_state && !pipe->top_pipe && !pipe->prev_odm_pipe && !dcn32_is_center_timing(pipe) &&629 !(pipe->stream->timing.pix_clk_100hz / 10000 > DCN3_2_MAX_SUBVP_PIXEL_RATE_MHZ) &&630 (!dcn32_is_psr_capable(pipe) || (context->stream_count == 1 && dc->caps.dmub_caps.subvp_psr)) &&631 dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_NONE &&632 (refresh_rate < 120 || dcn32_allow_subvp_high_refresh_rate(dc, context, pipe)) &&633 !pipe->plane_state->address.tmz_surface &&634 (vba->ActiveDRAMClockChangeLatencyMarginPerState[vba->VoltageLevel][vba->maxMpcComb][vba->pipe_plane[pipe_idx]] <= 0 ||635 (vba->ActiveDRAMClockChangeLatencyMarginPerState[vba->VoltageLevel][vba->maxMpcComb][vba->pipe_plane[pipe_idx]] > 0 &&636 dcn32_allow_subvp_with_active_margin(pipe)))) {637 while (pipe) {638 num_pipes++;639 pipe = pipe->bottom_pipe;640 }641 642 pipe = &context->res_ctx.pipe_ctx[i];643 if (num_pipes <= free_pipes) {644 struct dc_stream_state *stream = pipe->stream;645 unsigned int frame_us = (stream->timing.v_total * stream->timing.h_total /646 (double)(stream->timing.pix_clk_100hz * 100)) * 1000000;647 if (frame_us > max_frame_time) {648 *index = i;649 max_frame_time = frame_us;650 valid_assignment_found = true;651 }652 }653 }654 pipe_idx++;655 }656 return valid_assignment_found;657}658 659/**660 * dcn32_enough_pipes_for_subvp - Function to check if there are "enough" pipes for SubVP.661 * @dc: current dc state662 * @context: new dc state663 *664 * This function returns true if there are enough free pipes665 * to create the required phantom pipes for any given stream666 * (that does not already have phantom pipe assigned).667 *668 * e.g. For a 2 stream config where the first stream uses one669 * pipe and the second stream uses 2 pipes (i.e. pipe split),670 * this function will return true because there is 1 remaining671 * pipe which can be used as the phantom pipe for the non pipe672 * split pipe.673 *674 * Return:675 * True if there are enough free pipes to assign phantom pipes to at least one676 * stream that does not already have phantom pipes assigned. Otherwise false.677 */678static bool dcn32_enough_pipes_for_subvp(struct dc *dc, struct dc_state *context)679{680 unsigned int i, split_cnt, free_pipes;681 unsigned int min_pipe_split = dc->res_pool->pipe_count + 1; // init as max number of pipes + 1682 bool subvp_possible = false;683 684 for (i = 0; i < dc->res_pool->pipe_count; i++) {685 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];686 687 // Find the minimum pipe split count for non SubVP pipes688 if (resource_is_pipe_type(pipe, OPP_HEAD) &&689 dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_NONE) {690 split_cnt = 0;691 while (pipe) {692 split_cnt++;693 pipe = pipe->bottom_pipe;694 }695 696 if (split_cnt < min_pipe_split)697 min_pipe_split = split_cnt;698 }699 }700 701 free_pipes = dcn32_get_num_free_pipes(dc, context);702 703 // SubVP only possible if at least one pipe is being used (i.e. free_pipes704 // should not equal to the pipe_count)705 if (free_pipes >= min_pipe_split && free_pipes < dc->res_pool->pipe_count)706 subvp_possible = true;707 708 return subvp_possible;709}710 711/**712 * subvp_subvp_schedulable - Determine if SubVP + SubVP config is schedulable713 * @dc: current dc state714 * @context: new dc state715 *716 * High level algorithm:717 * 1. Find longest microschedule length (in us) between the two SubVP pipes718 * 2. Check if the worst case overlap (VBLANK in middle of ACTIVE) for both719 * pipes still allows for the maximum microschedule to fit in the active720 * region for both pipes.721 *722 * Return: True if the SubVP + SubVP config is schedulable, false otherwise723 */724static bool subvp_subvp_schedulable(struct dc *dc, struct dc_state *context)725{726 struct pipe_ctx *subvp_pipes[2] = {0};727 struct dc_stream_state *phantom = NULL;728 uint32_t microschedule_lines = 0;729 uint32_t index = 0;730 uint32_t i;731 uint32_t max_microschedule_us = 0;732 int32_t vactive1_us, vactive2_us, vblank1_us, vblank2_us;733 734 for (i = 0; i < dc->res_pool->pipe_count; i++) {735 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];736 uint32_t time_us = 0;737 738 /* Loop to calculate the maximum microschedule time between the two SubVP pipes,739 * and also to store the two main SubVP pipe pointers in subvp_pipes[2].740 */741 phantom = dc_state_get_paired_subvp_stream(context, pipe->stream);742 if (phantom && pipe->stream && pipe->plane_state && !pipe->top_pipe &&743 dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_MAIN) {744 microschedule_lines = (phantom->timing.v_total - phantom->timing.v_front_porch) +745 phantom->timing.v_addressable;746 747 // Round up when calculating microschedule time (+ 1 at the end)748 time_us = (microschedule_lines * phantom->timing.h_total) /749 (double)(phantom->timing.pix_clk_100hz * 100) * 1000000 +750 dc->caps.subvp_prefetch_end_to_mall_start_us +751 dc->caps.subvp_fw_processing_delay_us + 1;752 if (time_us > max_microschedule_us)753 max_microschedule_us = time_us;754 755 subvp_pipes[index] = pipe;756 index++;757 758 // Maximum 2 SubVP pipes759 if (index == 2)760 break;761 }762 }763 vactive1_us = ((subvp_pipes[0]->stream->timing.v_addressable * subvp_pipes[0]->stream->timing.h_total) /764 (double)(subvp_pipes[0]->stream->timing.pix_clk_100hz * 100)) * 1000000;765 vactive2_us = ((subvp_pipes[1]->stream->timing.v_addressable * subvp_pipes[1]->stream->timing.h_total) /766 (double)(subvp_pipes[1]->stream->timing.pix_clk_100hz * 100)) * 1000000;767 vblank1_us = (((subvp_pipes[0]->stream->timing.v_total - subvp_pipes[0]->stream->timing.v_addressable) *768 subvp_pipes[0]->stream->timing.h_total) /769 (double)(subvp_pipes[0]->stream->timing.pix_clk_100hz * 100)) * 1000000;770 vblank2_us = (((subvp_pipes[1]->stream->timing.v_total - subvp_pipes[1]->stream->timing.v_addressable) *771 subvp_pipes[1]->stream->timing.h_total) /772 (double)(subvp_pipes[1]->stream->timing.pix_clk_100hz * 100)) * 1000000;773 774 if ((vactive1_us - vblank2_us) / 2 > max_microschedule_us &&775 (vactive2_us - vblank1_us) / 2 > max_microschedule_us)776 return true;777 778 return false;779}780 781/**782 * subvp_drr_schedulable() - Determine if SubVP + DRR config is schedulable783 * @dc: current dc state784 * @context: new dc state785 *786 * High level algorithm:787 * 1. Get timing for SubVP pipe, phantom pipe, and DRR pipe788 * 2. Determine the frame time for the DRR display when adding required margin for MCLK switching789 * (the margin is equal to the MALL region + DRR margin (500us))790 * 3.If (SubVP Active - Prefetch > Stretched DRR frame + max(MALL region, Stretched DRR frame))791 * then report the configuration as supported792 *793 * Return: True if the SubVP + DRR config is schedulable, false otherwise794 */795static bool subvp_drr_schedulable(struct dc *dc, struct dc_state *context)796{797 bool schedulable = false;798 uint32_t i;799 struct pipe_ctx *pipe = NULL;800 struct pipe_ctx *drr_pipe = NULL;801 struct dc_crtc_timing *main_timing = NULL;802 struct dc_crtc_timing *phantom_timing = NULL;803 struct dc_crtc_timing *drr_timing = NULL;804 int16_t prefetch_us = 0;805 int16_t mall_region_us = 0;806 int16_t drr_frame_us = 0; // nominal frame time807 int16_t subvp_active_us = 0;808 int16_t stretched_drr_us = 0;809 int16_t drr_stretched_vblank_us = 0;810 int16_t max_vblank_mallregion = 0;811 struct dc_stream_state *phantom_stream;812 bool subvp_found = false;813 bool drr_found = false;814 815 // Find SubVP pipe816 for (i = 0; i < dc->res_pool->pipe_count; i++) {817 pipe = &context->res_ctx.pipe_ctx[i];818 819 // We check for master pipe, but it shouldn't matter since we only need820 // the pipe for timing info (stream should be same for any pipe splits)821 if (!resource_is_pipe_type(pipe, OTG_MASTER) ||822 !resource_is_pipe_type(pipe, DPP_PIPE))823 continue;824 825 // Find the SubVP pipe826 if (dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_MAIN) {827 subvp_found = true;828 break;829 }830 }831 832 // Find the DRR pipe833 for (i = 0; i < dc->res_pool->pipe_count; i++) {834 drr_pipe = &context->res_ctx.pipe_ctx[i];835 836 // We check for master pipe only837 if (!resource_is_pipe_type(drr_pipe, OTG_MASTER) ||838 !resource_is_pipe_type(drr_pipe, DPP_PIPE))839 continue;840 841 if (dc_state_get_pipe_subvp_type(context, drr_pipe) == SUBVP_NONE && drr_pipe->stream->ignore_msa_timing_param &&842 (drr_pipe->stream->allow_freesync || drr_pipe->stream->vrr_active_variable || drr_pipe->stream->vrr_active_fixed)) {843 drr_found = true;844 break;845 }846 }847 848 phantom_stream = dc_state_get_paired_subvp_stream(context, pipe->stream);849 if (phantom_stream && subvp_found && drr_found) {850 main_timing = &pipe->stream->timing;851 phantom_timing = &phantom_stream->timing;852 drr_timing = &drr_pipe->stream->timing;853 prefetch_us = (phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total /854 (double)(phantom_timing->pix_clk_100hz * 100) * 1000000 +855 dc->caps.subvp_prefetch_end_to_mall_start_us;856 subvp_active_us = main_timing->v_addressable * main_timing->h_total /857 (double)(main_timing->pix_clk_100hz * 100) * 1000000;858 drr_frame_us = drr_timing->v_total * drr_timing->h_total /859 (double)(drr_timing->pix_clk_100hz * 100) * 1000000;860 // P-State allow width and FW delays already included phantom_timing->v_addressable861 mall_region_us = phantom_timing->v_addressable * phantom_timing->h_total /862 (double)(phantom_timing->pix_clk_100hz * 100) * 1000000;863 stretched_drr_us = drr_frame_us + mall_region_us + SUBVP_DRR_MARGIN_US;864 drr_stretched_vblank_us = (drr_timing->v_total - drr_timing->v_addressable) * drr_timing->h_total /865 (double)(drr_timing->pix_clk_100hz * 100) * 1000000 + (stretched_drr_us - drr_frame_us);866 max_vblank_mallregion = drr_stretched_vblank_us > mall_region_us ? drr_stretched_vblank_us : mall_region_us;867 }868 869 /* We consider SubVP + DRR schedulable if the stretched frame duration of the DRR display (i.e. the870 * highest refresh rate + margin that can support UCLK P-State switch) passes the static analysis871 * for VBLANK: (VACTIVE region of the SubVP pipe can fit the MALL prefetch, VBLANK frame time,872 * and the max of (VBLANK blanking time, MALL region)).873 */874 if (drr_timing &&875 stretched_drr_us < (1 / (double)drr_timing->min_refresh_in_uhz) * 1000000 * 1000000 &&876 subvp_active_us - prefetch_us - stretched_drr_us - max_vblank_mallregion > 0)877 schedulable = true;878 879 return schedulable;880}881 882 883/**884 * subvp_vblank_schedulable - Determine if SubVP + VBLANK config is schedulable885 * @dc: current dc state886 * @context: new dc state887 *888 * High level algorithm:889 * 1. Get timing for SubVP pipe, phantom pipe, and VBLANK pipe890 * 2. If (SubVP Active - Prefetch > Vblank Frame Time + max(MALL region, Vblank blanking time))891 * then report the configuration as supported892 * 3. If the VBLANK display is DRR, then take the DRR static schedulability path893 *894 * Return: True if the SubVP + VBLANK/DRR config is schedulable, false otherwise895 */896static bool subvp_vblank_schedulable(struct dc *dc, struct dc_state *context)897{898 struct pipe_ctx *pipe = NULL;899 struct pipe_ctx *subvp_pipe = NULL;900 bool found = false;901 bool schedulable = false;902 uint32_t i = 0;903 uint8_t vblank_index = 0;904 uint16_t prefetch_us = 0;905 uint16_t mall_region_us = 0;906 uint16_t vblank_frame_us = 0;907 uint16_t subvp_active_us = 0;908 uint16_t vblank_blank_us = 0;909 uint16_t max_vblank_mallregion = 0;910 struct dc_crtc_timing *main_timing = NULL;911 struct dc_crtc_timing *phantom_timing = NULL;912 struct dc_crtc_timing *vblank_timing = NULL;913 struct dc_stream_state *phantom_stream;914 enum mall_stream_type pipe_mall_type;915 916 /* For SubVP + VBLANK/DRR cases, we assume there can only be917 * a single VBLANK/DRR display. If DML outputs SubVP + VBLANK918 * is supported, it is either a single VBLANK case or two VBLANK919 * displays which are synchronized (in which case they have identical920 * timings).921 */922 for (i = 0; i < dc->res_pool->pipe_count; i++) {923 pipe = &context->res_ctx.pipe_ctx[i];924 pipe_mall_type = dc_state_get_pipe_subvp_type(context, pipe);925 926 // We check for master pipe, but it shouldn't matter since we only need927 // the pipe for timing info (stream should be same for any pipe splits)928 if (!resource_is_pipe_type(pipe, OTG_MASTER) ||929 !resource_is_pipe_type(pipe, DPP_PIPE))930 continue;931 932 if (!found && pipe_mall_type == SUBVP_NONE) {933 // Found pipe which is not SubVP or Phantom (i.e. the VBLANK pipe).934 vblank_index = i;935 found = true;936 }937 938 if (!subvp_pipe && pipe_mall_type == SUBVP_MAIN)939 subvp_pipe = pipe;940 }941 if (found && subvp_pipe) {942 phantom_stream = dc_state_get_paired_subvp_stream(context, subvp_pipe->stream);943 main_timing = &subvp_pipe->stream->timing;944 phantom_timing = &phantom_stream->timing;945 vblank_timing = &context->res_ctx.pipe_ctx[vblank_index].stream->timing;946 // Prefetch time is equal to VACTIVE + BP + VSYNC of the phantom pipe947 // Also include the prefetch end to mallstart delay time948 prefetch_us = (phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total /949 (double)(phantom_timing->pix_clk_100hz * 100) * 1000000 +950 dc->caps.subvp_prefetch_end_to_mall_start_us;951 // P-State allow width and FW delays already included phantom_timing->v_addressable952 mall_region_us = phantom_timing->v_addressable * phantom_timing->h_total /953 (double)(phantom_timing->pix_clk_100hz * 100) * 1000000;954 vblank_frame_us = vblank_timing->v_total * vblank_timing->h_total /955 (double)(vblank_timing->pix_clk_100hz * 100) * 1000000;956 vblank_blank_us = (vblank_timing->v_total - vblank_timing->v_addressable) * vblank_timing->h_total /957 (double)(vblank_timing->pix_clk_100hz * 100) * 1000000;958 subvp_active_us = main_timing->v_addressable * main_timing->h_total /959 (double)(main_timing->pix_clk_100hz * 100) * 1000000;960 max_vblank_mallregion = vblank_blank_us > mall_region_us ? vblank_blank_us : mall_region_us;961 962 // Schedulable if VACTIVE region of the SubVP pipe can fit the MALL prefetch, VBLANK frame time,963 // and the max of (VBLANK blanking time, MALL region)964 // TODO: Possibly add some margin (i.e. the below conditions should be [...] > X instead of [...] > 0)965 if (subvp_active_us - prefetch_us - vblank_frame_us - max_vblank_mallregion > 0)966 schedulable = true;967 }968 return schedulable;969}970 971/**972 * subvp_subvp_admissable() - Determine if subvp + subvp config is admissible973 *974 * @dc: Current DC state975 * @context: New DC state to be programmed976 *977 * SubVP + SubVP is admissible under the following conditions:978 * - All SubVP pipes are < 120Hz OR979 * - All SubVP pipes are >= 120hz980 *981 * Return: True if admissible, false otherwise982 */983static bool subvp_subvp_admissable(struct dc *dc,984 struct dc_state *context)985{986 bool result = false;987 uint32_t i;988 uint8_t subvp_count = 0;989 uint32_t min_refresh = subvp_high_refresh_list.min_refresh, max_refresh = 0;990 uint64_t refresh_rate = 0;991 992 for (i = 0; i < dc->res_pool->pipe_count; i++) {993 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];994 995 if (!pipe->stream)996 continue;997 998 if (pipe->plane_state && !pipe->top_pipe &&999 dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_MAIN) {1000 refresh_rate = (pipe->stream->timing.pix_clk_100hz * (uint64_t)100 +1001 pipe->stream->timing.v_total * (uint64_t)pipe->stream->timing.h_total - (uint64_t)1);1002 refresh_rate = div_u64(refresh_rate, pipe->stream->timing.v_total);1003 refresh_rate = div_u64(refresh_rate, pipe->stream->timing.h_total);1004 1005 if ((uint32_t)refresh_rate < min_refresh)1006 min_refresh = (uint32_t)refresh_rate;1007 if ((uint32_t)refresh_rate > max_refresh)1008 max_refresh = (uint32_t)refresh_rate;1009 subvp_count++;1010 }1011 }1012 1013 if (subvp_count == 2 && ((min_refresh < 120 && max_refresh < 120) ||1014 (min_refresh >= subvp_high_refresh_list.min_refresh &&1015 max_refresh <= subvp_high_refresh_list.max_refresh)))1016 result = true;1017 1018 return result;1019}1020 1021/**1022 * subvp_validate_static_schedulability - Check which SubVP case is calculated1023 * and handle static analysis based on the case.1024 * @dc: current dc state1025 * @context: new dc state1026 * @vlevel: Voltage level calculated by DML1027 *1028 * Three cases:1029 * 1. SubVP + SubVP1030 * 2. SubVP + VBLANK (DRR checked internally)1031 * 3. SubVP + VACTIVE (currently unsupported)1032 *1033 * Return: True if statically schedulable, false otherwise1034 */1035static bool subvp_validate_static_schedulability(struct dc *dc,1036 struct dc_state *context,1037 int vlevel)1038{1039 bool schedulable = false;1040 struct vba_vars_st *vba = &context->bw_ctx.dml.vba;1041 uint32_t i, pipe_idx;1042 uint8_t subvp_count = 0;1043 uint8_t vactive_count = 0;1044 uint8_t non_subvp_pipes = 0;1045 1046 for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {1047 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];1048 enum mall_stream_type pipe_mall_type = dc_state_get_pipe_subvp_type(context, pipe);1049 1050 if (!pipe->stream)1051 continue;1052 1053 if (pipe->plane_state && !pipe->top_pipe) {1054 if (pipe_mall_type == SUBVP_MAIN)1055 subvp_count++;1056 if (pipe_mall_type == SUBVP_NONE)1057 non_subvp_pipes++;1058 }1059 1060 // Count how many planes that aren't SubVP/phantom are capable of VACTIVE1061 // switching (SubVP + VACTIVE unsupported). In situations where we force1062 // SubVP for a VACTIVE plane, we don't want to increment the vactive_count.1063 if (vba->ActiveDRAMClockChangeLatencyMarginPerState[vlevel][vba->maxMpcComb][vba->pipe_plane[pipe_idx]] > 0 &&1064 pipe_mall_type == SUBVP_NONE) {1065 vactive_count++;1066 }1067 pipe_idx++;1068 }1069 1070 if (subvp_count == 2) {1071 // Static schedulability check for SubVP + SubVP case1072 schedulable = subvp_subvp_admissable(dc, context) && subvp_subvp_schedulable(dc, context);1073 } else if (subvp_count == 1 && non_subvp_pipes == 0) {1074 // Single SubVP configs will be supported by default as long as it's suppported by DML1075 schedulable = true;1076 } else if (subvp_count == 1 && non_subvp_pipes == 1) {1077 if (dcn32_subvp_drr_admissable(dc, context))1078 schedulable = subvp_drr_schedulable(dc, context);1079 else if (dcn32_subvp_vblank_admissable(dc, context, vlevel))1080 schedulable = subvp_vblank_schedulable(dc, context);1081 } else if (vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_vactive_w_mall_sub_vp &&1082 vactive_count > 0) {1083 // For single display SubVP cases, DML will output dm_dram_clock_change_vactive_w_mall_sub_vp by default.1084 // We tell the difference between SubVP vs. SubVP + VACTIVE by checking the vactive_count.1085 // SubVP + VACTIVE currently unsupported1086 schedulable = false;1087 }1088 return schedulable;1089}1090 1091static void assign_subvp_index(struct dc *dc, struct dc_state *context)1092{1093 int i;1094 int index = 0;1095 1096 for (i = 0; i < dc->res_pool->pipe_count; i++) {1097 struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];1098 1099 if (resource_is_pipe_type(pipe_ctx, OTG_MASTER) &&1100 dc_state_get_pipe_subvp_type(context, pipe_ctx) == SUBVP_MAIN) {1101 pipe_ctx->subvp_index = index++;1102 } else {1103 pipe_ctx->subvp_index = 0;1104 }1105 }1106}1107 1108struct pipe_slice_table {1109 struct {1110 struct dc_stream_state *stream;1111 int slice_count;1112 } odm_combines[MAX_STREAMS];1113 int odm_combine_count;1114 1115 struct {1116 struct pipe_ctx *pri_pipe;1117 struct dc_plane_state *plane;1118 int slice_count;1119 } mpc_combines[MAX_PLANES];1120 int mpc_combine_count;1121};1122 1123 1124static void update_slice_table_for_stream(struct pipe_slice_table *table,1125 struct dc_stream_state *stream, int diff)1126{1127 int i;1128 1129 for (i = 0; i < table->odm_combine_count; i++) {1130 if (table->odm_combines[i].stream == stream) {1131 table->odm_combines[i].slice_count += diff;1132 break;1133 }1134 }1135 1136 if (i == table->odm_combine_count) {1137 table->odm_combine_count++;1138 table->odm_combines[i].stream = stream;1139 table->odm_combines[i].slice_count = diff;1140 }1141}1142 1143static void update_slice_table_for_plane(struct pipe_slice_table *table,1144 struct pipe_ctx *dpp_pipe, struct dc_plane_state *plane, int diff)1145{1146 int i;1147 struct pipe_ctx *pri_dpp_pipe = resource_get_primary_dpp_pipe(dpp_pipe);1148 1149 for (i = 0; i < table->mpc_combine_count; i++) {1150 if (table->mpc_combines[i].plane == plane &&1151 table->mpc_combines[i].pri_pipe == pri_dpp_pipe) {1152 table->mpc_combines[i].slice_count += diff;1153 break;1154 }1155 }1156 1157 if (i == table->mpc_combine_count) {1158 table->mpc_combine_count++;1159 table->mpc_combines[i].plane = plane;1160 table->mpc_combines[i].pri_pipe = pri_dpp_pipe;1161 table->mpc_combines[i].slice_count = diff;1162 }1163}1164 1165static void init_pipe_slice_table_from_context(1166 struct pipe_slice_table *table,1167 struct dc_state *context)1168{1169 int i, j;1170 struct pipe_ctx *otg_master;1171 struct pipe_ctx *dpp_pipes[MAX_PIPES];1172 struct dc_stream_state *stream;1173 int count;1174 1175 memset(table, 0, sizeof(*table));1176 1177 for (i = 0; i < context->stream_count; i++) {1178 stream = context->streams[i];1179 otg_master = resource_get_otg_master_for_stream(1180 &context->res_ctx, stream);1181 if (!otg_master)1182 continue;1183 1184 count = resource_get_odm_slice_count(otg_master);1185 update_slice_table_for_stream(table, stream, count);1186 1187 count = resource_get_dpp_pipes_for_opp_head(otg_master,1188 &context->res_ctx, dpp_pipes);1189 for (j = 0; j < count; j++)1190 if (dpp_pipes[j]->plane_state)1191 update_slice_table_for_plane(table, dpp_pipes[j],1192 dpp_pipes[j]->plane_state, 1);1193 }1194}1195 1196static bool update_pipe_slice_table_with_split_flags(1197 struct pipe_slice_table *table,1198 struct dc *dc,1199 struct dc_state *context,1200 struct vba_vars_st *vba,1201 int split[MAX_PIPES],1202 bool merge[MAX_PIPES])1203{1204 /* NOTE: we are deprecating the support for the concept of pipe splitting1205 * or pipe merging. Instead we append slices to the end and remove1206 * slices from the end. The following code converts a pipe split or1207 * merge to an append or remove operation.1208 *1209 * For example:1210 * When split flags describe the following pipe connection transition1211 *1212 * from:1213 * pipe 0 (split=2) -> pipe 1 (split=2)1214 * to: (old behavior)1215 * pipe 0 -> pipe 2 -> pipe 1 -> pipe 31216 *1217 * the code below actually does:1218 * pipe 0 -> pipe 1 -> pipe 2 -> pipe 31219 *1220 * This is the new intended behavior and for future DCNs we will retire1221 * the old concept completely.1222 */1223 struct pipe_ctx *pipe;1224 bool odm;1225 int dc_pipe_idx, dml_pipe_idx = 0;1226 bool updated = false;1227 1228 for (dc_pipe_idx = 0;1229 dc_pipe_idx < dc->res_pool->pipe_count; dc_pipe_idx++) {1230 pipe = &context->res_ctx.pipe_ctx[dc_pipe_idx];1231 if (resource_is_pipe_type(pipe, FREE_PIPE))1232 continue;1233 1234 if (merge[dc_pipe_idx]) {1235 if (resource_is_pipe_type(pipe, OPP_HEAD))1236 /* merging OPP head means reducing ODM slice1237 * count by 11238 */1239 update_slice_table_for_stream(table, pipe->stream, -1);1240 else if (resource_is_pipe_type(pipe, DPP_PIPE) &&1241 resource_get_odm_slice_index(resource_get_opp_head(pipe)) == 0)1242 /* merging DPP pipe of the first ODM slice means1243 * reducing MPC slice count by 11244 */1245 update_slice_table_for_plane(table, pipe, pipe->plane_state, -1);1246 updated = true;1247 }1248 1249 if (split[dc_pipe_idx]) {1250 odm = vba->ODMCombineEnabled[vba->pipe_plane[dml_pipe_idx]] !=1251 dm_odm_combine_mode_disabled;1252 if (odm && resource_is_pipe_type(pipe, OPP_HEAD))1253 update_slice_table_for_stream(1254 table, pipe->stream, split[dc_pipe_idx] - 1);1255 else if (!odm && resource_is_pipe_type(pipe, DPP_PIPE))1256 update_slice_table_for_plane(table, pipe,1257 pipe->plane_state, split[dc_pipe_idx] - 1);1258 updated = true;1259 }1260 dml_pipe_idx++;1261 }1262 return updated;1263}1264 1265static void update_pipes_with_slice_table(struct dc *dc, struct dc_state *context,1266 struct pipe_slice_table *table)1267{1268 int i;1269 1270 for (i = 0; i < table->odm_combine_count; i++)1271 resource_update_pipes_for_stream_with_slice_count(context,1272 dc->current_state, dc->res_pool,1273 table->odm_combines[i].stream,1274 table->odm_combines[i].slice_count);1275 1276 for (i = 0; i < table->mpc_combine_count; i++)1277 resource_update_pipes_for_plane_with_slice_count(context,1278 dc->current_state, dc->res_pool,1279 table->mpc_combines[i].plane,1280 table->mpc_combines[i].slice_count);1281}1282 1283static bool update_pipes_with_split_flags(struct dc *dc, struct dc_state *context,1284 struct vba_vars_st *vba, int split[MAX_PIPES],1285 bool merge[MAX_PIPES])1286{1287 struct pipe_slice_table slice_table;1288 bool updated;1289 1290 init_pipe_slice_table_from_context(&slice_table, context);1291 updated = update_pipe_slice_table_with_split_flags(1292 &slice_table, dc, context, vba,1293 split, merge);1294 update_pipes_with_slice_table(dc, context, &slice_table);1295 return updated;1296}1297 1298static bool should_apply_odm_power_optimization(struct dc *dc,1299 struct dc_state *context, struct vba_vars_st *v, int *split,1300 bool *merge)1301{1302 struct dc_stream_state *stream = context->streams[0];1303 struct pipe_slice_table slice_table;1304 int i;1305 1306 /*1307 * this debug flag allows us to disable ODM power optimization feature1308 * unconditionally. we force the feature off if this is set to false.1309 */1310 if (!dc->debug.enable_single_display_2to1_odm_policy)1311 return false;1312 1313 /* current design and test coverage is only limited to allow ODM power1314 * optimization for single stream. Supporting it for multiple streams1315 * use case would require additional algorithm to decide how to1316 * optimize power consumption when there are not enough free pipes to1317 * allocate for all the streams. This level of optimization would1318 * require multiple attempts of revalidation to make an optimized1319 * decision. Unfortunately We do not support revalidation flow in1320 * current version of DML.1321 */1322 if (context->stream_count != 1)1323 return false;1324 1325 /*1326 * Our hardware doesn't support ODM for HDMI TMDS1327 */1328 if (dc_is_hdmi_signal(stream->signal))1329 return false;1330 1331 /*1332 * ODM Combine 2:1 requires horizontal timing divisible by 2 so each1333 * ODM segment has the same size.1334 */1335 if (!is_h_timing_divisible_by_2(stream))1336 return false;1337 1338 /*1339 * No power benefits if the timing's pixel clock is not high enough to1340 * raise display clock from minimum power state.1341 */1342 if (stream->timing.pix_clk_100hz * 100 <= DCN3_2_VMIN_DISPCLK_HZ)1343 return false;1344 1345 if (dc->config.enable_windowed_mpo_odm) {1346 /*1347 * ODM power optimization should only be allowed if the feature1348 * can be seamlessly toggled off within an update. This would1349 * require that the feature is applied on top of a minimal1350 * state. A minimal state is defined as a state validated1351 * without the need of pipe split. Therefore, when transition to1352 * toggle the feature off, the same stream and plane1353 * configuration can be supported by the pipe resource in the1354 * first ODM slice alone without the need to acquire extra1355 * resources.1356 */1357 init_pipe_slice_table_from_context(&slice_table, context);1358 update_pipe_slice_table_with_split_flags(1359 &slice_table, dc, context, v,1360 split, merge);1361 for (i = 0; i < slice_table.mpc_combine_count; i++)1362 if (slice_table.mpc_combines[i].slice_count > 1)1363 return false;1364 1365 for (i = 0; i < slice_table.odm_combine_count; i++)1366 if (slice_table.odm_combines[i].slice_count > 1)1367 return false;1368 } else {1369 /*1370 * the new ODM power optimization feature reduces software1371 * design limitation and allows ODM power optimization to be1372 * supported even with presence of overlay planes. The new1373 * feature is enabled based on enable_windowed_mpo_odm flag. If1374 * the flag is not set, we limit our feature scope due to1375 * previous software design limitation1376 */1377 if (context->stream_status[0].plane_count != 1)1378 return false;1379 1380 if (memcmp(&context->stream_status[0].plane_states[0]->clip_rect,1381 &stream->src, sizeof(struct rect)) != 0)1382 return false;1383 1384 if (stream->src.width >= 5120 &&1385 stream->src.width > stream->dst.width)1386 return false;1387 }1388 return true;1389}1390 1391static void try_odm_power_optimization_and_revalidate(1392 struct dc *dc,1393 struct dc_state *context,1394 display_e2e_pipe_params_st *pipes,1395 int *split,1396 bool *merge,1397 unsigned int *vlevel,1398 int pipe_cnt)1399{1400 int i;1401 unsigned int new_vlevel;1402 unsigned int cur_policy[MAX_PIPES];1403 1404 for (i = 0; i < pipe_cnt; i++) {1405 cur_policy[i] = pipes[i].pipe.dest.odm_combine_policy;1406 pipes[i].pipe.dest.odm_combine_policy = dm_odm_combine_policy_2to1;1407 }1408 1409 new_vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, pipe_cnt);1410 1411 if (new_vlevel < context->bw_ctx.dml.soc.num_states) {1412 memset(split, 0, MAX_PIPES * sizeof(int));1413 memset(merge, 0, MAX_PIPES * sizeof(bool));1414 *vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, new_vlevel, split, merge);1415 context->bw_ctx.dml.vba.VoltageLevel = *vlevel;1416 } else {1417 for (i = 0; i < pipe_cnt; i++)1418 pipes[i].pipe.dest.odm_combine_policy = cur_policy[i];1419 }1420}1421 1422static bool is_test_pattern_enabled(1423 struct dc_state *context)1424{1425 int i;1426 1427 for (i = 0; i < context->stream_count; i++) {1428 if (context->streams[i]->test_pattern.type != DP_TEST_PATTERN_VIDEO_MODE)1429 return true;1430 }1431 1432 return false;1433}1434 1435static bool dcn32_full_validate_bw_helper(struct dc *dc,1436 struct dc_state *context,1437 display_e2e_pipe_params_st *pipes,1438 int *vlevel,1439 int *split,1440 bool *merge,1441 int *pipe_cnt,1442 bool *repopulate_pipes)1443{1444 struct vba_vars_st *vba = &context->bw_ctx.dml.vba;1445 unsigned int dc_pipe_idx = 0;1446 int i = 0;1447 bool found_supported_config = false;1448 int vlevel_temp = 0;1449 1450 dc_assert_fp_enabled();1451 1452 /*1453 * DML favors voltage over p-state, but we're more interested in1454 * supporting p-state over voltage. We can't support p-state in1455 * prefetch mode > 0 so try capping the prefetch mode to start.1456 * Override present for testing.1457 */1458 if (dc->debug.dml_disallow_alternate_prefetch_modes)1459 context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =1460 dm_prefetch_support_uclk_fclk_and_stutter;1461 else1462 context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =1463 dm_prefetch_support_uclk_fclk_and_stutter_if_possible;1464 1465 *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt);1466 /* This may adjust vlevel and maxMpcComb */1467 if (*vlevel < context->bw_ctx.dml.soc.num_states) {1468 *vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, *vlevel, split, merge);1469 vba->VoltageLevel = *vlevel;1470 }1471 1472 /* Apply split and merge flags before checking for subvp */1473 if (!dcn32_apply_merge_split_flags_helper(dc, context, repopulate_pipes, split, merge))1474 return false;1475 memset(split, 0, MAX_PIPES * sizeof(int));1476 memset(merge, 0, MAX_PIPES * sizeof(bool));1477 1478 /* Conditions for setting up phantom pipes for SubVP:1479 * 1. Not force disable SubVP1480 * 2. Full update (i.e. !fast_validate)1481 * 3. Enough pipes are available to support SubVP (TODO: Which pipes will use VACTIVE / VBLANK / SUBVP?)1482 * 4. Display configuration passes validation1483 * 5. (Config doesn't support MCLK in VACTIVE/VBLANK || dc->debug.force_subvp_mclk_switch)1484 */1485 if (!dc->debug.force_disable_subvp && !dc->caps.dmub_caps.gecc_enable && dcn32_all_pipes_have_stream_and_plane(dc, context) &&1486 !dcn32_mpo_in_use(context) && !dcn32_any_surfaces_rotated(dc, context) && !is_test_pattern_enabled(context) &&1487 (*vlevel == context->bw_ctx.dml.soc.num_states || (vba->DRAMSpeedPerState[*vlevel] != vba->DRAMSpeedPerState[0] &&1488 vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] != dm_dram_clock_change_unsupported) ||1489 vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] == dm_dram_clock_change_unsupported ||1490 dc->debug.force_subvp_mclk_switch)) {1491 1492 vlevel_temp = *vlevel;1493 1494 while (!found_supported_config && dcn32_enough_pipes_for_subvp(dc, context) &&1495 dcn32_assign_subvp_pipe(dc, context, &dc_pipe_idx)) {1496 /* For the case where *vlevel = num_states, bandwidth validation has failed for this config.1497 * Adding phantom pipes won't change the validation result, so change the DML input param1498 * for P-State support before adding phantom pipes and recalculating the DML result.1499 * However, this case is only applicable for SubVP + DRR cases because the prefetch mode1500 * will not allow for switch in VBLANK. The DRR display must have it's VBLANK stretched1501 * enough to support MCLK switching.1502 */1503 if (*vlevel == context->bw_ctx.dml.soc.num_states &&1504 context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final ==1505 dm_prefetch_support_uclk_fclk_and_stutter) {1506 context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =1507 dm_prefetch_support_fclk_and_stutter;1508 /* There are params (such as FabricClock) that need to be recalculated1509 * after validation fails (otherwise it will be 0). Calculation for1510 * phantom vactive requires call into DML, so we must ensure all the1511 * vba params are valid otherwise we'll get incorrect phantom vactive.1512 */1513 *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt);1514 }1515 1516 dc->res_pool->funcs->add_phantom_pipes(dc, context, pipes, *pipe_cnt, dc_pipe_idx);1517 1518 *pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, false);1519 // Populate dppclk to trigger a recalculate in dml_get_voltage_level1520 // so the phantom pipe DLG params can be assigned correctly.1521 pipes[0].clks_cfg.dppclk_mhz = get_dppclk_calculated(&context->bw_ctx.dml, pipes, *pipe_cnt, 0);1522 *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt);1523 1524 /* Check that vlevel requested supports pstate or not1525 * if not, select the lowest vlevel that supports it1526 */1527 for (i = *vlevel; i < context->bw_ctx.dml.soc.num_states; i++) {1528 if (vba->DRAMClockChangeSupport[i][vba->maxMpcComb] != dm_dram_clock_change_unsupported) {1529 *vlevel = i;1530 break;1531 }1532 }1533 1534 if (*vlevel < context->bw_ctx.dml.soc.num_states1535 && subvp_validate_static_schedulability(dc, context, *vlevel))1536 found_supported_config = true;1537 if (found_supported_config) {1538 // For SubVP + DRR cases, we can force the lowest vlevel that supports the mode1539 if (dcn32_subvp_drr_admissable(dc, context) && subvp_drr_schedulable(dc, context)) {1540 /* find lowest vlevel that supports the config */1541 for (i = *vlevel; i >= 0; i--) {1542 if (vba->ModeSupport[i][vba->maxMpcComb]) {1543 *vlevel = i;1544 } else {1545 break;1546 }1547 }1548 }1549 }1550 }1551 1552 if (vba->DRAMSpeedPerState[*vlevel] >= vba->DRAMSpeedPerState[vlevel_temp])1553 found_supported_config = false;1554 1555 // If SubVP pipe config is unsupported (or cannot be used for UCLK switching)1556 // remove phantom pipes and repopulate dml pipes1557 if (!found_supported_config) {1558 dc_state_remove_phantom_streams_and_planes(dc, context);1559 dc_state_release_phantom_streams_and_planes(dc, context);1560 vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] = dm_dram_clock_change_unsupported;1561 *pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, false);1562 1563 *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt);1564 /* This may adjust vlevel and maxMpcComb */1565 if (*vlevel < context->bw_ctx.dml.soc.num_states) {1566 *vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, *vlevel, split, merge);1567 vba->VoltageLevel = *vlevel;1568 }1569 } else {1570 // Most populate phantom DLG params before programming hardware / timing for phantom pipe1571 dcn32_helper_populate_phantom_dlg_params(dc, context, pipes, *pipe_cnt);1572 1573 /* Call validate_apply_pipe_split flags after calling DML getters for1574 * phantom dlg params, or some of the VBA params indicating pipe split1575 * can be overwritten by the getters.1576 *1577 * When setting up SubVP config, all pipes are merged before attempting to1578 * add phantom pipes. If pipe split (ODM / MPC) is required, both the main1579 * and phantom pipes will be split in the regular pipe splitting sequence.1580 */1581 *vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, *vlevel, split, merge);1582 vba->VoltageLevel = *vlevel;1583 // Note: We can't apply the phantom pipes to hardware at this time. We have to wait1584 // until driver has acquired the DMCUB lock to do it safely.1585 assign_subvp_index(dc, context);1586 }1587 }1588 1589 if (should_apply_odm_power_optimization(dc, context, vba, split, merge))1590 try_odm_power_optimization_and_revalidate(1591 dc, context, pipes, split, merge, vlevel, *pipe_cnt);1592 1593 return true;1594}1595 1596static bool is_dtbclk_required(struct dc *dc, struct dc_state *context)1597{1598 int i;1599 1600 for (i = 0; i < dc->res_pool->pipe_count; i++) {1601 if (!context->res_ctx.pipe_ctx[i].stream)1602 continue;1603 if (dc->link_srv->dp_is_128b_132b_signal(&context->res_ctx.pipe_ctx[i]))1604 return true;1605 }1606 return false;1607}1608 1609static void dcn20_adjust_freesync_v_startup(const struct dc_crtc_timing *dc_crtc_timing, int *vstartup_start)1610{1611 struct dc_crtc_timing patched_crtc_timing;1612 uint32_t asic_blank_end = 0;1613 uint32_t asic_blank_start = 0;1614 uint32_t newVstartup = 0;1615 1616 patched_crtc_timing = *dc_crtc_timing;1617 1618 if (patched_crtc_timing.flags.INTERLACE == 1) {1619 if (patched_crtc_timing.v_front_porch < 2)1620 patched_crtc_timing.v_front_porch = 2;1621 } else {1622 if (patched_crtc_timing.v_front_porch < 1)1623 patched_crtc_timing.v_front_porch = 1;1624 }1625 1626 /* blank_start = frame end - front porch */1627 asic_blank_start = patched_crtc_timing.v_total -1628 patched_crtc_timing.v_front_porch;1629 1630 /* blank_end = blank_start - active */1631 asic_blank_end = asic_blank_start -1632 patched_crtc_timing.v_border_bottom -1633 patched_crtc_timing.v_addressable -1634 patched_crtc_timing.v_border_top;1635 1636 newVstartup = asic_blank_end + (patched_crtc_timing.v_total - asic_blank_start);1637 1638 *vstartup_start = ((newVstartup > *vstartup_start) ? newVstartup : *vstartup_start);1639}1640 1641static void dcn32_calculate_dlg_params(struct dc *dc, struct dc_state *context,1642 display_e2e_pipe_params_st *pipes,1643 int pipe_cnt, int vlevel)1644{1645 int i, pipe_idx, active_hubp_count = 0;1646 bool usr_retraining_support = false;1647 bool unbounded_req_enabled = false;1648 struct vba_vars_st *vba = &context->bw_ctx.dml.vba;1649 1650 dc_assert_fp_enabled();1651 1652 /* Writeback MCIF_WB arbitration parameters */1653 dc->res_pool->funcs->set_mcif_arb_params(dc, context, pipes, pipe_cnt);1654 1655 context->bw_ctx.bw.dcn.clk.dispclk_khz = context->bw_ctx.dml.vba.DISPCLK * 1000;1656 context->bw_ctx.bw.dcn.clk.dcfclk_khz = context->bw_ctx.dml.vba.DCFCLK * 1000;1657 context->bw_ctx.bw.dcn.clk.socclk_khz = context->bw_ctx.dml.vba.SOCCLK * 1000;1658 context->bw_ctx.bw.dcn.clk.dramclk_khz = context->bw_ctx.dml.vba.DRAMSpeed * 1000 / 16;1659 context->bw_ctx.bw.dcn.clk.dcfclk_deep_sleep_khz = context->bw_ctx.dml.vba.DCFCLKDeepSleep * 1000;1660 context->bw_ctx.bw.dcn.clk.fclk_khz = context->bw_ctx.dml.vba.FabricClock * 1000;1661 context->bw_ctx.bw.dcn.clk.p_state_change_support =1662 context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb]1663 != dm_dram_clock_change_unsupported;1664 1665 /* Pstate change might not be supported by hardware, but it might be1666 * possible with firmware driven vertical blank stretching.1667 */1668 context->bw_ctx.bw.dcn.clk.p_state_change_support |= context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching;1669 1670 context->bw_ctx.bw.dcn.clk.dppclk_khz = 0;1671 context->bw_ctx.bw.dcn.clk.dtbclk_en = is_dtbclk_required(dc, context);1672 context->bw_ctx.bw.dcn.clk.ref_dtbclk_khz = context->bw_ctx.dml.vba.DTBCLKPerState[vlevel] * 1000;1673 if (context->bw_ctx.dml.vba.FCLKChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] == dm_fclock_change_unsupported)1674 context->bw_ctx.bw.dcn.clk.fclk_p_state_change_support = false;1675 else1676 context->bw_ctx.bw.dcn.clk.fclk_p_state_change_support = true;1677 1678 usr_retraining_support = context->bw_ctx.dml.vba.USRRetrainingSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb];1679 ASSERT(usr_retraining_support);1680 1681 if (context->bw_ctx.bw.dcn.clk.dispclk_khz < dc->debug.min_disp_clk_khz)1682 context->bw_ctx.bw.dcn.clk.dispclk_khz = dc->debug.min_disp_clk_khz;1683 1684 unbounded_req_enabled = get_unbounded_request_enabled(&context->bw_ctx.dml, pipes, pipe_cnt);1685 1686 if (unbounded_req_enabled && pipe_cnt > 1) {1687 // Unbounded requesting should not ever be used when more than 1 pipe is enabled.1688 ASSERT(false);1689 unbounded_req_enabled = false;1690 }1691 1692 context->bw_ctx.bw.dcn.mall_ss_size_bytes = 0;1693 context->bw_ctx.bw.dcn.mall_ss_psr_active_size_bytes = 0;1694 context->bw_ctx.bw.dcn.mall_subvp_size_bytes = 0;1695 1696 for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {1697 if (!context->res_ctx.pipe_ctx[i].stream)1698 continue;1699 if (context->res_ctx.pipe_ctx[i].plane_state)1700 active_hubp_count++;1701 pipes[pipe_idx].pipe.dest.vstartup_start = get_vstartup(&context->bw_ctx.dml, pipes, pipe_cnt,1702 pipe_idx);1703 pipes[pipe_idx].pipe.dest.vupdate_offset = get_vupdate_offset(&context->bw_ctx.dml, pipes, pipe_cnt,1704 pipe_idx);1705 pipes[pipe_idx].pipe.dest.vupdate_width = get_vupdate_width(&context->bw_ctx.dml, pipes, pipe_cnt,1706 pipe_idx);1707 pipes[pipe_idx].pipe.dest.vready_offset = get_vready_offset(&context->bw_ctx.dml, pipes, pipe_cnt,1708 pipe_idx);1709 1710 if (dc_state_get_pipe_subvp_type(context, &context->res_ctx.pipe_ctx[i]) == SUBVP_PHANTOM) {1711 // Phantom pipe requires that DET_SIZE = 0 and no unbounded requests1712 context->res_ctx.pipe_ctx[i].det_buffer_size_kb = 0;1713 context->res_ctx.pipe_ctx[i].unbounded_req = false;1714 } else {1715 context->res_ctx.pipe_ctx[i].det_buffer_size_kb = get_det_buffer_size_kbytes(&context->bw_ctx.dml, pipes, pipe_cnt,1716 pipe_idx);1717 context->res_ctx.pipe_ctx[i].unbounded_req = unbounded_req_enabled;1718 }1719 1720 if (context->bw_ctx.bw.dcn.clk.dppclk_khz < pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000)1721 context->bw_ctx.bw.dcn.clk.dppclk_khz = pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000;1722 if (context->res_ctx.pipe_ctx[i].plane_state)1723 context->res_ctx.pipe_ctx[i].plane_res.bw.dppclk_khz = pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000;1724 else1725 context->res_ctx.pipe_ctx[i].plane_res.bw.dppclk_khz = 0;1726 context->res_ctx.pipe_ctx[i].pipe_dlg_param = pipes[pipe_idx].pipe.dest;1727 1728 context->res_ctx.pipe_ctx[i].surface_size_in_mall_bytes = get_surface_size_in_mall(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);1729 1730 if (vba->ActiveDRAMClockChangeLatencyMarginPerState[vba->VoltageLevel][vba->maxMpcComb][vba->pipe_plane[pipe_idx]] > 0)1731 context->res_ctx.pipe_ctx[i].has_vactive_margin = true;1732 else1733 context->res_ctx.pipe_ctx[i].has_vactive_margin = false;1734 1735 /* MALL Allocation Sizes */1736 /* count from active, top pipes per plane only */1737 if (context->res_ctx.pipe_ctx[i].stream && context->res_ctx.pipe_ctx[i].plane_state &&1738 (context->res_ctx.pipe_ctx[i].top_pipe == NULL ||1739 context->res_ctx.pipe_ctx[i].plane_state != context->res_ctx.pipe_ctx[i].top_pipe->plane_state) &&1740 context->res_ctx.pipe_ctx[i].prev_odm_pipe == NULL) {1741 /* SS: all active surfaces stored in MALL */1742 if (dc_state_get_pipe_subvp_type(context, &context->res_ctx.pipe_ctx[i]) != SUBVP_PHANTOM) {1743 context->bw_ctx.bw.dcn.mall_ss_size_bytes += context->res_ctx.pipe_ctx[i].surface_size_in_mall_bytes;1744 1745 if (context->res_ctx.pipe_ctx[i].stream->link->psr_settings.psr_version == DC_PSR_VERSION_UNSUPPORTED) {1746 /* SS PSR On: all active surfaces part of streams not supporting PSR stored in MALL */1747 context->bw_ctx.bw.dcn.mall_ss_psr_active_size_bytes += context->res_ctx.pipe_ctx[i].surface_size_in_mall_bytes;1748 }1749 } else {1750 /* SUBVP: phantom surfaces only stored in MALL */1751 context->bw_ctx.bw.dcn.mall_subvp_size_bytes += context->res_ctx.pipe_ctx[i].surface_size_in_mall_bytes;1752 }1753 }1754 1755 if (context->res_ctx.pipe_ctx[i].stream->adaptive_sync_infopacket.valid)1756 dcn20_adjust_freesync_v_startup(1757 &context->res_ctx.pipe_ctx[i].stream->timing,1758 &context->res_ctx.pipe_ctx[i].pipe_dlg_param.vstartup_start);1759 1760 pipe_idx++;1761 }1762 /* If DCN isn't making memory requests we can allow pstate change and lower clocks */1763 if (!active_hubp_count) {1764 context->bw_ctx.bw.dcn.clk.socclk_khz = 0;1765 context->bw_ctx.bw.dcn.clk.dppclk_khz = 0;1766 context->bw_ctx.bw.dcn.clk.dcfclk_khz = 0;1767 context->bw_ctx.bw.dcn.clk.dcfclk_deep_sleep_khz = 0;1768 context->bw_ctx.bw.dcn.clk.dramclk_khz = 0;1769 context->bw_ctx.bw.dcn.clk.fclk_khz = 0;1770 context->bw_ctx.bw.dcn.clk.p_state_change_support = true;1771 context->bw_ctx.bw.dcn.clk.fclk_p_state_change_support = true;1772 }1773 /*save a original dppclock copy*/1774 context->bw_ctx.bw.dcn.clk.bw_dppclk_khz = context->bw_ctx.bw.dcn.clk.dppclk_khz;1775 context->bw_ctx.bw.dcn.clk.bw_dispclk_khz = context->bw_ctx.bw.dcn.clk.dispclk_khz;1776 context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz = context->bw_ctx.dml.soc.clock_limits[vlevel].dppclk_mhz1777 * 1000;1778 context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz = context->bw_ctx.dml.soc.clock_limits[vlevel].dispclk_mhz1779 * 1000;1780 1781 context->bw_ctx.bw.dcn.clk.num_ways = dcn32_helper_calculate_num_ways_for_subvp(dc, context);1782 1783 context->bw_ctx.bw.dcn.compbuf_size_kb = context->bw_ctx.dml.ip.config_return_buffer_size_in_kbytes;1784 1785 for (i = 0; i < dc->res_pool->pipe_count; i++) {1786 if (context->res_ctx.pipe_ctx[i].stream)1787 context->bw_ctx.bw.dcn.compbuf_size_kb -= context->res_ctx.pipe_ctx[i].det_buffer_size_kb;1788 }1789 1790 for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {1791 1792 if (!context->res_ctx.pipe_ctx[i].stream)1793 continue;1794 1795 context->bw_ctx.dml.funcs.rq_dlg_get_dlg_reg_v2(&context->bw_ctx.dml,1796 &context->res_ctx.pipe_ctx[i].dlg_regs, &context->res_ctx.pipe_ctx[i].ttu_regs, pipes,1797 pipe_cnt, pipe_idx);1798 1799 context->bw_ctx.dml.funcs.rq_dlg_get_rq_reg_v2(&context->res_ctx.pipe_ctx[i].rq_regs,1800 &context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);1801 pipe_idx++;1802 }1803}1804 1805static struct pipe_ctx *dcn32_find_split_pipe(1806 struct dc *dc,1807 struct dc_state *context,1808 int old_index)1809{1810 struct pipe_ctx *pipe = NULL;1811 int i;1812 1813 if (old_index >= 0 && context->res_ctx.pipe_ctx[old_index].stream == NULL) {1814 pipe = &context->res_ctx.pipe_ctx[old_index];1815 pipe->pipe_idx = old_index;1816 }1817 1818 if (!pipe)1819 for (i = dc->res_pool->pipe_count - 1; i >= 0; i--) {1820 if (dc->current_state->res_ctx.pipe_ctx[i].top_pipe == NULL1821 && dc->current_state->res_ctx.pipe_ctx[i].prev_odm_pipe == NULL) {1822 if (context->res_ctx.pipe_ctx[i].stream == NULL) {1823 pipe = &context->res_ctx.pipe_ctx[i];1824 pipe->pipe_idx = i;1825 break;1826 }1827 }1828 }1829 1830 /*1831 * May need to fix pipes getting tossed from 1 opp to another on flip1832 * Add for debugging transient underflow during topology updates:1833 * ASSERT(pipe);1834 */1835 if (!pipe)1836 for (i = dc->res_pool->pipe_count - 1; i >= 0; i--) {1837 if (context->res_ctx.pipe_ctx[i].stream == NULL) {1838 pipe = &context->res_ctx.pipe_ctx[i];1839 pipe->pipe_idx = i;1840 break;1841 }1842 }1843 1844 return pipe;1845}1846 1847static bool dcn32_split_stream_for_mpc_or_odm(1848 const struct dc *dc,1849 struct resource_context *res_ctx,1850 struct pipe_ctx *pri_pipe,1851 struct pipe_ctx *sec_pipe,1852 bool odm)1853{1854 int pipe_idx = sec_pipe->pipe_idx;1855 const struct resource_pool *pool = dc->res_pool;1856 1857 DC_LOGGER_INIT(dc->ctx->logger);1858 1859 if (odm && pri_pipe->plane_state) {1860 /* ODM + window MPO, where MPO window is on left half only */1861 if (pri_pipe->plane_state->clip_rect.x + pri_pipe->plane_state->clip_rect.width <=1862 pri_pipe->stream->src.x + pri_pipe->stream->src.width/2) {1863 1864 DC_LOG_SCALER("%s - ODM + window MPO(left). pri_pipe:%d\n",1865 __func__,1866 pri_pipe->pipe_idx);1867 return true;1868 }1869 1870 /* ODM + window MPO, where MPO window is on right half only */1871 if (pri_pipe->plane_state->clip_rect.x >= pri_pipe->stream->src.x + pri_pipe->stream->src.width/2) {1872 1873 DC_LOG_SCALER("%s - ODM + window MPO(right). pri_pipe:%d\n",1874 __func__,1875 pri_pipe->pipe_idx);1876 return true;1877 }1878 }1879 1880 *sec_pipe = *pri_pipe;1881 1882 sec_pipe->pipe_idx = pipe_idx;1883 sec_pipe->plane_res.mi = pool->mis[pipe_idx];1884 sec_pipe->plane_res.hubp = pool->hubps[pipe_idx];1885 sec_pipe->plane_res.ipp = pool->ipps[pipe_idx];1886 sec_pipe->plane_res.xfm = pool->transforms[pipe_idx];1887 sec_pipe->plane_res.dpp = pool->dpps[pipe_idx];1888 sec_pipe->plane_res.mpcc_inst = pool->dpps[pipe_idx]->inst;1889 sec_pipe->stream_res.dsc = NULL;1890 if (odm) {1891 if (pri_pipe->next_odm_pipe) {1892 ASSERT(pri_pipe->next_odm_pipe != sec_pipe);1893 sec_pipe->next_odm_pipe = pri_pipe->next_odm_pipe;1894 sec_pipe->next_odm_pipe->prev_odm_pipe = sec_pipe;1895 }1896 if (pri_pipe->top_pipe && pri_pipe->top_pipe->next_odm_pipe) {1897 pri_pipe->top_pipe->next_odm_pipe->bottom_pipe = sec_pipe;1898 sec_pipe->top_pipe = pri_pipe->top_pipe->next_odm_pipe;1899 }1900 if (pri_pipe->bottom_pipe && pri_pipe->bottom_pipe->next_odm_pipe) {1901 pri_pipe->bottom_pipe->next_odm_pipe->top_pipe = sec_pipe;1902 sec_pipe->bottom_pipe = pri_pipe->bottom_pipe->next_odm_pipe;1903 }1904 pri_pipe->next_odm_pipe = sec_pipe;1905 sec_pipe->prev_odm_pipe = pri_pipe;1906 ASSERT(sec_pipe->top_pipe == NULL);1907 1908 if (!sec_pipe->top_pipe)1909 sec_pipe->stream_res.opp = pool->opps[pipe_idx];1910 else1911 sec_pipe->stream_res.opp = sec_pipe->top_pipe->stream_res.opp;1912 if (sec_pipe->stream->timing.flags.DSC == 1) {1913 dcn20_acquire_dsc(dc, res_ctx, &sec_pipe->stream_res.dsc, pipe_idx);1914 ASSERT(sec_pipe->stream_res.dsc);1915 if (sec_pipe->stream_res.dsc == NULL)1916 return false;1917 }1918 } else {1919 if (pri_pipe->bottom_pipe) {1920 ASSERT(pri_pipe->bottom_pipe != sec_pipe);1921 sec_pipe->bottom_pipe = pri_pipe->bottom_pipe;1922 sec_pipe->bottom_pipe->top_pipe = sec_pipe;1923 }1924 pri_pipe->bottom_pipe = sec_pipe;1925 sec_pipe->top_pipe = pri_pipe;1926 1927 ASSERT(pri_pipe->plane_state);1928 }1929 1930 return true;1931}1932 1933static bool dcn32_apply_merge_split_flags_helper(1934 struct dc *dc,1935 struct dc_state *context,1936 bool *repopulate_pipes,1937 int *split,1938 bool *merge)1939{1940 int i, pipe_idx;1941 bool newly_split[MAX_PIPES] = { false };1942 struct vba_vars_st *vba = &context->bw_ctx.dml.vba;1943 1944 if (dc->config.enable_windowed_mpo_odm) {1945 if (update_pipes_with_split_flags(1946 dc, context, vba, split, merge))1947 *repopulate_pipes = true;1948 } else {1949 1950 /* the code below will be removed once windowed mpo odm is fully1951 * enabled.1952 */1953 /* merge pipes if necessary */1954 for (i = 0; i < dc->res_pool->pipe_count; i++) {1955 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];1956 1957 /*skip pipes that don't need merging*/1958 if (!merge[i])1959 continue;1960 1961 /* if ODM merge we ignore mpc tree, mpo pipes will have their own flags */1962 if (pipe->prev_odm_pipe) {1963 /*split off odm pipe*/1964 pipe->prev_odm_pipe->next_odm_pipe = pipe->next_odm_pipe;1965 if (pipe->next_odm_pipe)1966 pipe->next_odm_pipe->prev_odm_pipe = pipe->prev_odm_pipe;1967 1968 /*2:1ODM+MPC Split MPO to Single Pipe + MPC Split MPO*/1969 if (pipe->bottom_pipe) {1970 if (pipe->bottom_pipe->prev_odm_pipe || pipe->bottom_pipe->next_odm_pipe) {1971 /*MPC split rules will handle this case*/1972 pipe->bottom_pipe->top_pipe = NULL;1973 } else {1974 /* when merging an ODM pipes, the bottom MPC pipe must now point to1975 * the previous ODM pipe and its associated stream assets1976 */1977 if (pipe->prev_odm_pipe->bottom_pipe) {1978 /* 3 plane MPO*/1979 pipe->bottom_pipe->top_pipe = pipe->prev_odm_pipe->bottom_pipe;1980 pipe->prev_odm_pipe->bottom_pipe->bottom_pipe = pipe->bottom_pipe;1981 } else {1982 /* 2 plane MPO*/1983 pipe->bottom_pipe->top_pipe = pipe->prev_odm_pipe;1984 pipe->prev_odm_pipe->bottom_pipe = pipe->bottom_pipe;1985 }1986 1987 memcpy(&pipe->bottom_pipe->stream_res, &pipe->bottom_pipe->top_pipe->stream_res, sizeof(struct stream_resource));1988 }1989 }1990 1991 if (pipe->top_pipe) {1992 pipe->top_pipe->bottom_pipe = NULL;1993 }1994 1995 pipe->bottom_pipe = NULL;1996 pipe->next_odm_pipe = NULL;1997 pipe->plane_state = NULL;1998 pipe->stream = NULL;1999 pipe->top_pipe = NULL;2000 pipe->prev_odm_pipe = NULL;2001 if (pipe->stream_res.dsc)2002 dcn20_release_dsc(&context->res_ctx, dc->res_pool, &pipe->stream_res.dsc);2003 memset(&pipe->plane_res, 0, sizeof(pipe->plane_res));2004 memset(&pipe->stream_res, 0, sizeof(pipe->stream_res));2005 memset(&pipe->link_res, 0, sizeof(pipe->link_res));2006 *repopulate_pipes = true;2007 } else if (pipe->top_pipe && pipe->top_pipe->plane_state == pipe->plane_state) {2008 struct pipe_ctx *top_pipe = pipe->top_pipe;2009 struct pipe_ctx *bottom_pipe = pipe->bottom_pipe;2010 2011 top_pipe->bottom_pipe = bottom_pipe;2012 if (bottom_pipe)2013 bottom_pipe->top_pipe = top_pipe;2014 2015 pipe->top_pipe = NULL;2016 pipe->bottom_pipe = NULL;2017 pipe->plane_state = NULL;2018 pipe->stream = NULL;2019 memset(&pipe->plane_res, 0, sizeof(pipe->plane_res));2020 memset(&pipe->stream_res, 0, sizeof(pipe->stream_res));2021 memset(&pipe->link_res, 0, sizeof(pipe->link_res));2022 *repopulate_pipes = true;2023 } else2024 ASSERT(0); /* Should never try to merge master pipe */2025 2026 }2027 2028 for (i = 0, pipe_idx = -1; i < dc->res_pool->pipe_count; i++) {2029 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];2030 struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i];2031 struct pipe_ctx *hsplit_pipe = NULL;2032 bool odm;2033 int old_index = -1;2034 2035 if (!pipe->stream || newly_split[i])2036 continue;2037 2038 pipe_idx++;2039 odm = vba->ODMCombineEnabled[vba->pipe_plane[pipe_idx]] != dm_odm_combine_mode_disabled;2040 2041 if (!pipe->plane_state && !odm)2042 continue;2043 2044 if (split[i]) {2045 if (odm) {2046 if (split[i] == 4 && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe)2047 old_index = old_pipe->next_odm_pipe->next_odm_pipe->pipe_idx;2048 else if (old_pipe->next_odm_pipe)2049 old_index = old_pipe->next_odm_pipe->pipe_idx;2050 } else {2051 if (split[i] == 4 && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe &&2052 old_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state)2053 old_index = old_pipe->bottom_pipe->bottom_pipe->pipe_idx;2054 else if (old_pipe->bottom_pipe &&2055 old_pipe->bottom_pipe->plane_state == old_pipe->plane_state)2056 old_index = old_pipe->bottom_pipe->pipe_idx;2057 }2058 hsplit_pipe = dcn32_find_split_pipe(dc, context, old_index);2059 ASSERT(hsplit_pipe);2060 if (!hsplit_pipe)2061 return false;2062 2063 if (!dcn32_split_stream_for_mpc_or_odm(2064 dc, &context->res_ctx,2065 pipe, hsplit_pipe, odm))2066 return false;2067 2068 newly_split[hsplit_pipe->pipe_idx] = true;2069 *repopulate_pipes = true;2070 }2071 if (split[i] == 4) {2072 struct pipe_ctx *pipe_4to1;2073 2074 if (odm && old_pipe->next_odm_pipe)2075 old_index = old_pipe->next_odm_pipe->pipe_idx;2076 else if (!odm && old_pipe->bottom_pipe &&2077 old_pipe->bottom_pipe->plane_state == old_pipe->plane_state)2078 old_index = old_pipe->bottom_pipe->pipe_idx;2079 else2080 old_index = -1;2081 pipe_4to1 = dcn32_find_split_pipe(dc, context, old_index);2082 ASSERT(pipe_4to1);2083 if (!pipe_4to1)2084 return false;2085 if (!dcn32_split_stream_for_mpc_or_odm(2086 dc, &context->res_ctx,2087 pipe, pipe_4to1, odm))2088 return false;2089 newly_split[pipe_4to1->pipe_idx] = true;2090 2091 if (odm && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe2092 && old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe)2093 old_index = old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe->pipe_idx;2094 else if (!odm && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe &&2095 old_pipe->bottom_pipe->bottom_pipe->bottom_pipe &&2096 old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state)2097 old_index = old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->pipe_idx;2098 else2099 old_index = -1;2100 pipe_4to1 = dcn32_find_split_pipe(dc, context, old_index);2101 ASSERT(pipe_4to1);2102 if (!pipe_4to1)2103 return false;2104 if (!dcn32_split_stream_for_mpc_or_odm(2105 dc, &context->res_ctx,2106 hsplit_pipe, pipe_4to1, odm))2107 return false;2108 newly_split[pipe_4to1->pipe_idx] = true;2109 }2110 if (odm)2111 dcn20_build_mapped_resource(dc, context, pipe->stream);2112 }2113 2114 for (i = 0; i < dc->res_pool->pipe_count; i++) {2115 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];2116 2117 if (pipe->plane_state) {2118 if (!resource_build_scaling_params(pipe))2119 return false;2120 }2121 }2122 2123 for (i = 0; i < context->stream_count; i++) {2124 struct pipe_ctx *otg_master = resource_get_otg_master_for_stream(&context->res_ctx,2125 context->streams[i]);2126 2127 if (otg_master)2128 resource_build_test_pattern_params(&context->res_ctx, otg_master);2129 }2130 }2131 return true;2132}2133 2134bool dcn32_internal_validate_bw(struct dc *dc,2135 struct dc_state *context,2136 display_e2e_pipe_params_st *pipes,2137 int *pipe_cnt_out,2138 int *vlevel_out,2139 bool fast_validate)2140{2141 bool out = false;2142 bool repopulate_pipes = false;2143 int split[MAX_PIPES] = { 0 };2144 bool merge[MAX_PIPES] = { false };2145 int pipe_cnt, i, pipe_idx;2146 int vlevel = context->bw_ctx.dml.soc.num_states;2147 struct vba_vars_st *vba = &context->bw_ctx.dml.vba;2148 2149 dc_assert_fp_enabled();2150 2151 ASSERT(pipes);2152 if (!pipes)2153 return false;2154 2155 /* For each full update, remove all existing phantom pipes first */2156 dc_state_remove_phantom_streams_and_planes(dc, context);2157 dc_state_release_phantom_streams_and_planes(dc, context);2158 2159 dc->res_pool->funcs->update_soc_for_wm_a(dc, context);2160 2161 for (i = 0; i < context->stream_count; i++)2162 resource_update_pipes_for_stream_with_slice_count(context, dc->current_state, dc->res_pool, context->streams[i], 1);2163 pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, fast_validate);2164 2165 if (!pipe_cnt) {2166 out = true;2167 goto validate_out;2168 }2169 2170 dml_log_pipe_params(&context->bw_ctx.dml, pipes, pipe_cnt);2171 context->bw_ctx.dml.soc.max_vratio_pre = dcn32_determine_max_vratio_prefetch(dc, context);2172 2173 if (!fast_validate) {2174 if (!dcn32_full_validate_bw_helper(dc, context, pipes, &vlevel, split, merge,2175 &pipe_cnt, &repopulate_pipes))2176 goto validate_fail;2177 }2178 2179 if (fast_validate ||2180 (dc->debug.dml_disallow_alternate_prefetch_modes &&2181 (vlevel == context->bw_ctx.dml.soc.num_states ||2182 vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_unsupported))) {2183 /*2184 * If dml_disallow_alternate_prefetch_modes is false, then we have already2185 * tried alternate prefetch modes during full validation.2186 *2187 * If mode is unsupported or there is no p-state support, then2188 * fall back to favouring voltage.2189 *2190 * If Prefetch mode 0 failed for this config, or passed with Max UCLK, then try2191 * to support with Prefetch mode 1 (dm_prefetch_support_fclk_and_stutter == 2)2192 */2193 context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =2194 dm_prefetch_support_none;2195 2196 context->bw_ctx.dml.validate_max_state = fast_validate;2197 vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, pipe_cnt);2198 2199 context->bw_ctx.dml.validate_max_state = false;2200 2201 if (vlevel < context->bw_ctx.dml.soc.num_states) {2202 memset(split, 0, sizeof(split));2203 memset(merge, 0, sizeof(merge));2204 vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, vlevel, split, merge);2205 /* dcn20_validate_apply_pipe_split_flags can modify voltage level outside of DML */2206 vba->VoltageLevel = vlevel;2207 }2208 }2209 2210 dml_log_mode_support_params(&context->bw_ctx.dml);2211 2212 if (vlevel == context->bw_ctx.dml.soc.num_states)2213 goto validate_fail;2214 2215 for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {2216 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];2217 struct pipe_ctx *mpo_pipe = pipe->bottom_pipe;2218 2219 if (!pipe->stream)2220 continue;2221 2222 if (vba->ODMCombineEnabled[vba->pipe_plane[pipe_idx]] != dm_odm_combine_mode_disabled2223 && !dc->config.enable_windowed_mpo_odm2224 && pipe->plane_state && mpo_pipe2225 && memcmp(&mpo_pipe->plane_state->clip_rect,2226 &pipe->stream->src,2227 sizeof(struct rect)) != 0) {2228 ASSERT(mpo_pipe->plane_state != pipe->plane_state);2229 goto validate_fail;2230 }2231 pipe_idx++;2232 }2233 2234 if (!dcn32_apply_merge_split_flags_helper(dc, context, &repopulate_pipes, split, merge))2235 goto validate_fail;2236 2237 /* Actual dsc count per stream dsc validation*/2238 if (!dcn20_validate_dsc(dc, context)) {2239 vba->ValidationStatus[vba->soc.num_states] = DML_FAIL_DSC_VALIDATION_FAILURE;2240 goto validate_fail;2241 }2242 2243 if (repopulate_pipes) {2244 int flag_max_mpc_comb = vba->maxMpcComb;2245 int flag_vlevel = vlevel;2246 int i;2247 2248 pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, fast_validate);2249 if (!dc->config.enable_windowed_mpo_odm)2250 dcn32_update_dml_pipes_odm_policy_based_on_context(dc, context, pipes);2251 2252 /* repopulate_pipes = 1 means the pipes were either split or merged. In this case2253 * we have to re-calculate the DET allocation and run through DML once more to2254 * ensure all the params are calculated correctly. We do not need to run the2255 * pipe split check again after this call (pipes are already split / merged).2256 * */2257 context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =2258 dm_prefetch_support_uclk_fclk_and_stutter_if_possible;2259 2260 vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, pipe_cnt);2261 2262 if (vlevel == context->bw_ctx.dml.soc.num_states) {2263 /* failed after DET size changes */2264 goto validate_fail;2265 } else if (flag_max_mpc_comb == 0 &&2266 flag_max_mpc_comb != context->bw_ctx.dml.vba.maxMpcComb) {2267 /* check the context constructed with pipe split flags is still valid*/2268 bool flags_valid = false;2269 for (i = flag_vlevel; i < context->bw_ctx.dml.soc.num_states; i++) {2270 if (vba->ModeSupport[i][flag_max_mpc_comb]) {2271 vba->maxMpcComb = flag_max_mpc_comb;2272 vba->VoltageLevel = i;2273 vlevel = i;2274 flags_valid = true;2275 break;2276 }2277 }2278 2279 /* this should never happen */2280 if (!flags_valid)2281 goto validate_fail;2282 }2283 }2284 *vlevel_out = vlevel;2285 *pipe_cnt_out = pipe_cnt;2286 2287 out = true;2288 goto validate_out;2289 2290validate_fail:2291 out = false;2292 2293validate_out:2294 return out;2295}2296 2297 2298void dcn32_calculate_wm_and_dlg_fpu(struct dc *dc, struct dc_state *context,2299 display_e2e_pipe_params_st *pipes,2300 int pipe_cnt,2301 int vlevel)2302{2303 int i, pipe_idx, vlevel_temp = 0;2304 double dcfclk = dcn3_2_soc.clock_limits[0].dcfclk_mhz;2305 double dcfclk_from_validation = context->bw_ctx.dml.vba.DCFCLKState[vlevel][context->bw_ctx.dml.vba.maxMpcComb];2306 double dram_speed_from_validation = context->bw_ctx.dml.vba.DRAMSpeed;2307 double dcfclk_from_fw_based_mclk_switching = dcfclk_from_validation;2308 bool pstate_en = context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] !=2309 dm_dram_clock_change_unsupported;2310 unsigned int dummy_latency_index = 0;2311 int maxMpcComb = context->bw_ctx.dml.vba.maxMpcComb;2312 unsigned int min_dram_speed_mts = context->bw_ctx.dml.vba.DRAMSpeed;2313 bool subvp_in_use = dcn32_subvp_in_use(dc, context);2314 unsigned int min_dram_speed_mts_margin;2315 bool need_fclk_lat_as_dummy = false;2316 bool is_subvp_p_drr = false;2317 struct dc_stream_state *fpo_candidate_stream = NULL;2318 struct dc_stream_status *stream_status = NULL;2319 2320 dc_assert_fp_enabled();2321 2322 /* need to find dummy latency index for subvp */2323 if (subvp_in_use) {2324 /* Override DRAMClockChangeSupport for SubVP + DRR case where the DRR cannot switch without stretching it's VBLANK */2325 if (!pstate_en) {2326 context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][maxMpcComb] = dm_dram_clock_change_vblank_w_mall_sub_vp;2327 context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final = dm_prefetch_support_fclk_and_stutter;2328 pstate_en = true;2329 is_subvp_p_drr = true;2330 }2331 dummy_latency_index = dcn32_find_dummy_latency_index_for_fw_based_mclk_switch(dc,2332 context, pipes, pipe_cnt, vlevel);2333 2334 /* For DCN32/321 need to validate with fclk pstate change latency equal to dummy so prefetch is2335 * scheduled correctly to account for dummy pstate.2336 */2337 if (context->bw_ctx.dml.soc.fclk_change_latency_us < dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dummy_pstate_latency_us) {2338 need_fclk_lat_as_dummy = true;2339 context->bw_ctx.dml.soc.fclk_change_latency_us =2340 dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dummy_pstate_latency_us;2341 }2342 context->bw_ctx.dml.soc.dram_clock_change_latency_us =2343 dc->clk_mgr->bw_params->wm_table.nv_entries[WM_A].dml_input.pstate_latency_us;2344 dcn32_internal_validate_bw(dc, context, pipes, &pipe_cnt, &vlevel, false);2345 maxMpcComb = context->bw_ctx.dml.vba.maxMpcComb;2346 if (is_subvp_p_drr) {2347 context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][maxMpcComb] = dm_dram_clock_change_vblank_w_mall_sub_vp;2348 }2349 }2350 2351 context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching = false;2352 for (i = 0; i < context->stream_count; i++) {2353 stream_status = NULL;2354 if (context->streams[i])2355 stream_status = dc_state_get_stream_status(context, context->streams[i]);2356 if (stream_status)2357 stream_status->fpo_in_use = false;2358 }2359 2360 if (!pstate_en || (!dc->debug.disable_fpo_optimizations &&2361 pstate_en && vlevel != 0)) {2362 /* only when the mclk switch can not be natural, is the fw based vblank stretch attempted */2363 fpo_candidate_stream = dcn32_can_support_mclk_switch_using_fw_based_vblank_stretch(dc, context);2364 if (fpo_candidate_stream) {2365 stream_status = dc_state_get_stream_status(context, fpo_candidate_stream);2366 if (stream_status)2367 stream_status->fpo_in_use = true;2368 context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching = true;2369 }2370 2371 if (context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching) {2372 dummy_latency_index = dcn32_find_dummy_latency_index_for_fw_based_mclk_switch(dc,2373 context, pipes, pipe_cnt, vlevel);2374 2375 /* After calling dcn30_find_dummy_latency_index_for_fw_based_mclk_switch2376 * we reinstate the original dram_clock_change_latency_us on the context2377 * and all variables that may have changed up to this point, except the2378 * newly found dummy_latency_index2379 */2380 context->bw_ctx.dml.soc.dram_clock_change_latency_us =2381 dc->clk_mgr->bw_params->wm_table.nv_entries[WM_A].dml_input.pstate_latency_us;2382 /* For DCN32/321 need to validate with fclk pstate change latency equal to dummy so2383 * prefetch is scheduled correctly to account for dummy pstate.2384 */2385 if (context->bw_ctx.dml.soc.fclk_change_latency_us < dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dummy_pstate_latency_us) {2386 need_fclk_lat_as_dummy = true;2387 context->bw_ctx.dml.soc.fclk_change_latency_us =2388 dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dummy_pstate_latency_us;2389 }2390 dcn32_internal_validate_bw(dc, context, pipes, &pipe_cnt, &vlevel_temp, false);2391 if (vlevel_temp < vlevel) {2392 vlevel = vlevel_temp;2393 maxMpcComb = context->bw_ctx.dml.vba.maxMpcComb;2394 dcfclk_from_fw_based_mclk_switching = context->bw_ctx.dml.vba.DCFCLKState[vlevel][context->bw_ctx.dml.vba.maxMpcComb];2395 pstate_en = true;2396 context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][maxMpcComb] = dm_dram_clock_change_vblank;2397 } else {2398 /* Restore FCLK latency and re-run validation to go back to original validation2399 * output if we find that enabling FPO does not give us any benefit (i.e. lower2400 * voltage level)2401 */2402 context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching = false;2403 for (i = 0; i < context->stream_count; i++) {2404 stream_status = NULL;2405 if (context->streams[i])2406 stream_status = dc_state_get_stream_status(context, context->streams[i]);2407 if (stream_status)2408 stream_status->fpo_in_use = false;2409 }2410 context->bw_ctx.dml.soc.fclk_change_latency_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_A].dml_input.fclk_change_latency_us;2411 dcn32_internal_validate_bw(dc, context, pipes, &pipe_cnt, &vlevel, false);2412 }2413 }2414 }2415 2416 /* Set B:2417 * For Set B calculations use clocks from clock_limits[2] when available i.e. when SMU is present,2418 * otherwise use arbitrary low value from spreadsheet for DCFCLK as lower is safer for watermark2419 * calculations to cover bootup clocks.2420 * DCFCLK: soc.clock_limits[2] when available2421 * UCLK: soc.clock_limits[2] when available2422 */2423 if (dcn3_2_soc.num_states > 2) {2424 vlevel_temp = 2;2425 dcfclk = dcn3_2_soc.clock_limits[2].dcfclk_mhz;2426 } else2427 dcfclk = 615; //DCFCLK Vmin_lv2428 2429 pipes[0].clks_cfg.voltage = vlevel_temp;2430 pipes[0].clks_cfg.dcfclk_mhz = dcfclk;2431 pipes[0].clks_cfg.socclk_mhz = context->bw_ctx.dml.soc.clock_limits[vlevel_temp].socclk_mhz;2432 2433 if (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_B].valid) {2434 context->bw_ctx.dml.soc.dram_clock_change_latency_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_B].dml_input.pstate_latency_us;2435 context->bw_ctx.dml.soc.fclk_change_latency_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_B].dml_input.fclk_change_latency_us;2436 context->bw_ctx.dml.soc.sr_enter_plus_exit_time_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_B].dml_input.sr_enter_plus_exit_time_us;2437 context->bw_ctx.dml.soc.sr_exit_time_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_B].dml_input.sr_exit_time_us;2438 }2439 context->bw_ctx.bw.dcn.watermarks.b.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2440 context->bw_ctx.bw.dcn.watermarks.b.cstate_pstate.cstate_enter_plus_exit_ns = get_wm_stutter_enter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2441 context->bw_ctx.bw.dcn.watermarks.b.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2442 context->bw_ctx.bw.dcn.watermarks.b.cstate_pstate.pstate_change_ns = get_wm_dram_clock_change(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2443 context->bw_ctx.bw.dcn.watermarks.b.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2444 context->bw_ctx.bw.dcn.watermarks.b.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2445 context->bw_ctx.bw.dcn.watermarks.b.frac_urg_bw_flip = get_fraction_of_urgent_bandwidth_imm_flip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2446 context->bw_ctx.bw.dcn.watermarks.b.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2447 context->bw_ctx.bw.dcn.watermarks.b.cstate_pstate.fclk_pstate_change_ns = get_fclk_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2448 context->bw_ctx.bw.dcn.watermarks.b.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2449 2450 /* Set D:2451 * All clocks min.2452 * DCFCLK: Min, as reported by PM FW when available2453 * UCLK : Min, as reported by PM FW when available2454 * sr_enter_exit/sr_exit should be lower than used for DRAM (TBD after bringup or later, use as decided in Clk Mgr)2455 */2456 2457 /*2458 if (dcn3_2_soc.num_states > 2) {2459 vlevel_temp = 0;2460 dcfclk = dc->clk_mgr->bw_params->clk_table.entries[0].dcfclk_mhz;2461 } else2462 dcfclk = 615; //DCFCLK Vmin_lv2463 2464 pipes[0].clks_cfg.voltage = vlevel_temp;2465 pipes[0].clks_cfg.dcfclk_mhz = dcfclk;2466 pipes[0].clks_cfg.socclk_mhz = context->bw_ctx.dml.soc.clock_limits[vlevel_temp].socclk_mhz;2467 2468 if (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_D].valid) {2469 context->bw_ctx.dml.soc.dram_clock_change_latency_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_D].dml_input.pstate_latency_us;2470 context->bw_ctx.dml.soc.fclk_change_latency_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_D].dml_input.fclk_change_latency_us;2471 context->bw_ctx.dml.soc.sr_enter_plus_exit_time_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_D].dml_input.sr_enter_plus_exit_time_us;2472 context->bw_ctx.dml.soc.sr_exit_time_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_D].dml_input.sr_exit_time_us;2473 }2474 context->bw_ctx.bw.dcn.watermarks.d.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2475 context->bw_ctx.bw.dcn.watermarks.d.cstate_pstate.cstate_enter_plus_exit_ns = get_wm_stutter_enter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2476 context->bw_ctx.bw.dcn.watermarks.d.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2477 context->bw_ctx.bw.dcn.watermarks.d.cstate_pstate.pstate_change_ns = get_wm_dram_clock_change(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2478 context->bw_ctx.bw.dcn.watermarks.d.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2479 context->bw_ctx.bw.dcn.watermarks.d.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2480 context->bw_ctx.bw.dcn.watermarks.d.frac_urg_bw_flip = get_fraction_of_urgent_bandwidth_imm_flip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2481 context->bw_ctx.bw.dcn.watermarks.d.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2482 context->bw_ctx.bw.dcn.watermarks.d.cstate_pstate.fclk_pstate_change_ns = get_fclk_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2483 context->bw_ctx.bw.dcn.watermarks.d.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2484 */2485 2486 /* Set C, for Dummy P-State:2487 * All clocks min.2488 * DCFCLK: Min, as reported by PM FW, when available2489 * UCLK : Min, as reported by PM FW, when available2490 * pstate latency as per UCLK state dummy pstate latency2491 */2492 2493 // For Set A and Set C use values from validation2494 pipes[0].clks_cfg.voltage = vlevel;2495 pipes[0].clks_cfg.dcfclk_mhz = dcfclk_from_validation;2496 pipes[0].clks_cfg.socclk_mhz = context->bw_ctx.dml.soc.clock_limits[vlevel].socclk_mhz;2497 2498 if (context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching) {2499 pipes[0].clks_cfg.dcfclk_mhz = dcfclk_from_fw_based_mclk_switching;2500 }2501 2502 if (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_C].valid) {2503 min_dram_speed_mts = dram_speed_from_validation;2504 min_dram_speed_mts_margin = 160;2505 2506 context->bw_ctx.dml.soc.dram_clock_change_latency_us =2507 dc->clk_mgr->bw_params->dummy_pstate_table[0].dummy_pstate_latency_us;2508 2509 if (context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][maxMpcComb] ==2510 dm_dram_clock_change_unsupported) {2511 int min_dram_speed_mts_offset = dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_memclk_levels - 1;2512 2513 min_dram_speed_mts =2514 dc->clk_mgr->bw_params->clk_table.entries[min_dram_speed_mts_offset].memclk_mhz * 16;2515 }2516 2517 if (!context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching && !subvp_in_use) {2518 /* find largest table entry that is lower than dram speed,2519 * but lower than DPM0 still uses DPM02520 */2521 for (dummy_latency_index = 3; dummy_latency_index > 0; dummy_latency_index--)2522 if (min_dram_speed_mts + min_dram_speed_mts_margin >2523 dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dram_speed_mts)2524 break;2525 }2526 2527 context->bw_ctx.dml.soc.dram_clock_change_latency_us =2528 dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dummy_pstate_latency_us;2529 2530 context->bw_ctx.dml.soc.fclk_change_latency_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_C].dml_input.fclk_change_latency_us;2531 context->bw_ctx.dml.soc.sr_enter_plus_exit_time_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_C].dml_input.sr_enter_plus_exit_time_us;2532 context->bw_ctx.dml.soc.sr_exit_time_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_C].dml_input.sr_exit_time_us;2533 }2534 2535 context->bw_ctx.bw.dcn.watermarks.c.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2536 context->bw_ctx.bw.dcn.watermarks.c.cstate_pstate.cstate_enter_plus_exit_ns = get_wm_stutter_enter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2537 context->bw_ctx.bw.dcn.watermarks.c.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2538 context->bw_ctx.bw.dcn.watermarks.c.cstate_pstate.pstate_change_ns = get_wm_dram_clock_change(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2539 context->bw_ctx.bw.dcn.watermarks.c.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2540 context->bw_ctx.bw.dcn.watermarks.c.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2541 context->bw_ctx.bw.dcn.watermarks.c.frac_urg_bw_flip = get_fraction_of_urgent_bandwidth_imm_flip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2542 context->bw_ctx.bw.dcn.watermarks.c.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2543 /* On DCN32/321, PMFW will set PSTATE_CHANGE_TYPE = 1 (FCLK) for UCLK dummy p-state.2544 * In this case we must program FCLK WM Set C to use the UCLK dummy p-state WM2545 * value.2546 */2547 context->bw_ctx.bw.dcn.watermarks.c.cstate_pstate.fclk_pstate_change_ns = get_wm_dram_clock_change(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2548 context->bw_ctx.bw.dcn.watermarks.c.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2549 2550 if ((!pstate_en) && (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_C].valid)) {2551 /* The only difference between A and C is p-state latency, if p-state is not supported2552 * with full p-state latency we want to calculate DLG based on dummy p-state latency,2553 * Set A p-state watermark set to 0 on DCN30, when p-state unsupported, for now keep as DCN30.2554 */2555 context->bw_ctx.bw.dcn.watermarks.a = context->bw_ctx.bw.dcn.watermarks.c;2556 context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns = 0;2557 /* Calculate FCLK p-state change watermark based on FCLK pstate change latency in case2558 * UCLK p-state is not supported, to avoid underflow in case FCLK pstate is supported2559 */2560 context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.fclk_pstate_change_ns = get_fclk_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2561 } else {2562 /* Set A:2563 * All clocks min.2564 * DCFCLK: Min, as reported by PM FW, when available2565 * UCLK: Min, as reported by PM FW, when available2566 */2567 2568 /* For set A set the correct latency values (i.e. non-dummy values) unconditionally2569 */2570 context->bw_ctx.dml.soc.dram_clock_change_latency_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_A].dml_input.pstate_latency_us;2571 context->bw_ctx.dml.soc.sr_enter_plus_exit_time_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_A].dml_input.sr_enter_plus_exit_time_us;2572 context->bw_ctx.dml.soc.sr_exit_time_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_A].dml_input.sr_exit_time_us;2573 2574 context->bw_ctx.bw.dcn.watermarks.a.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2575 context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.cstate_enter_plus_exit_ns = get_wm_stutter_enter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2576 context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2577 context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns = get_wm_dram_clock_change(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2578 context->bw_ctx.bw.dcn.watermarks.a.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2579 context->bw_ctx.bw.dcn.watermarks.a.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2580 context->bw_ctx.bw.dcn.watermarks.a.frac_urg_bw_flip = get_fraction_of_urgent_bandwidth_imm_flip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2581 context->bw_ctx.bw.dcn.watermarks.a.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2582 context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.fclk_pstate_change_ns = get_fclk_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2583 context->bw_ctx.bw.dcn.watermarks.a.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;2584 }2585 2586 /* Make set D = set A since we do not optimized watermarks for MALL */2587 context->bw_ctx.bw.dcn.watermarks.d = context->bw_ctx.bw.dcn.watermarks.a;2588 2589 for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {2590 if (!context->res_ctx.pipe_ctx[i].stream)2591 continue;2592 2593 pipes[pipe_idx].clks_cfg.dispclk_mhz = get_dispclk_calculated(&context->bw_ctx.dml, pipes, pipe_cnt);2594 pipes[pipe_idx].clks_cfg.dppclk_mhz = get_dppclk_calculated(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);2595 2596 if (dc->config.forced_clocks) {2597 pipes[pipe_idx].clks_cfg.dispclk_mhz = context->bw_ctx.dml.soc.clock_limits[0].dispclk_mhz;2598 pipes[pipe_idx].clks_cfg.dppclk_mhz = context->bw_ctx.dml.soc.clock_limits[0].dppclk_mhz;2599 }2600 if (dc->debug.min_disp_clk_khz > pipes[pipe_idx].clks_cfg.dispclk_mhz * 1000)2601 pipes[pipe_idx].clks_cfg.dispclk_mhz = dc->debug.min_disp_clk_khz / 1000.0;2602 if (dc->debug.min_dpp_clk_khz > pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000)2603 pipes[pipe_idx].clks_cfg.dppclk_mhz = dc->debug.min_dpp_clk_khz / 1000.0;2604 2605 pipe_idx++;2606 }2607 2608 context->perf_params.stutter_period_us = context->bw_ctx.dml.vba.StutterPeriod;2609 2610 /* for proper prefetch calculations, if dummy lat > fclk lat, use fclk lat = dummy lat */2611 if (need_fclk_lat_as_dummy)2612 context->bw_ctx.dml.soc.fclk_change_latency_us =2613 dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dummy_pstate_latency_us;2614 2615 dcn32_calculate_dlg_params(dc, context, pipes, pipe_cnt, vlevel);2616 2617 if (!pstate_en)2618 /* Restore full p-state latency */2619 context->bw_ctx.dml.soc.dram_clock_change_latency_us =2620 dc->clk_mgr->bw_params->wm_table.nv_entries[WM_A].dml_input.pstate_latency_us;2621 2622 /* revert fclk lat changes if required */2623 if (need_fclk_lat_as_dummy)2624 context->bw_ctx.dml.soc.fclk_change_latency_us =2625 dc->clk_mgr->bw_params->wm_table.nv_entries[WM_A].dml_input.fclk_change_latency_us;2626}2627 2628static void dcn32_get_optimal_dcfclk_fclk_for_uclk(unsigned int uclk_mts,2629 unsigned int *optimal_dcfclk,2630 unsigned int *optimal_fclk)2631{2632 double bw_from_dram, bw_from_dram1, bw_from_dram2;2633 2634 bw_from_dram1 = uclk_mts * dcn3_2_soc.num_chans *2635 dcn3_2_soc.dram_channel_width_bytes * (dcn3_2_soc.max_avg_dram_bw_use_normal_percent / 100);2636 bw_from_dram2 = uclk_mts * dcn3_2_soc.num_chans *2637 dcn3_2_soc.dram_channel_width_bytes * (dcn3_2_soc.max_avg_sdp_bw_use_normal_percent / 100);2638 2639 bw_from_dram = (bw_from_dram1 < bw_from_dram2) ? bw_from_dram1 : bw_from_dram2;2640 2641 if (optimal_fclk)2642 *optimal_fclk = bw_from_dram /2643 (dcn3_2_soc.fabric_datapath_to_dcn_data_return_bytes * (dcn3_2_soc.max_avg_sdp_bw_use_normal_percent / 100));2644 2645 if (optimal_dcfclk)2646 *optimal_dcfclk = bw_from_dram /2647 (dcn3_2_soc.return_bus_width_bytes * (dcn3_2_soc.max_avg_sdp_bw_use_normal_percent / 100));2648}2649 2650static void remove_entry_from_table_at_index(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries,2651 unsigned int index)2652{2653 int i;2654 2655 if (*num_entries == 0)2656 return;2657 2658 for (i = index; i < *num_entries - 1; i++) {2659 table[i] = table[i + 1];2660 }2661 memset(&table[--(*num_entries)], 0, sizeof(struct _vcs_dpi_voltage_scaling_st));2662}2663 2664void dcn32_patch_dpm_table(struct clk_bw_params *bw_params)2665{2666 int i;2667 unsigned int max_dcfclk_mhz = 0, max_dispclk_mhz = 0, max_dppclk_mhz = 0,2668 max_phyclk_mhz = 0, max_dtbclk_mhz = 0, max_fclk_mhz = 0, max_uclk_mhz = 0;2669 2670 for (i = 0; i < MAX_NUM_DPM_LVL; i++) {2671 if (bw_params->clk_table.entries[i].dcfclk_mhz > max_dcfclk_mhz)2672 max_dcfclk_mhz = bw_params->clk_table.entries[i].dcfclk_mhz;2673 if (bw_params->clk_table.entries[i].fclk_mhz > max_fclk_mhz)2674 max_fclk_mhz = bw_params->clk_table.entries[i].fclk_mhz;2675 if (bw_params->clk_table.entries[i].memclk_mhz > max_uclk_mhz)2676 max_uclk_mhz = bw_params->clk_table.entries[i].memclk_mhz;2677 if (bw_params->clk_table.entries[i].dispclk_mhz > max_dispclk_mhz)2678 max_dispclk_mhz = bw_params->clk_table.entries[i].dispclk_mhz;2679 if (bw_params->clk_table.entries[i].dppclk_mhz > max_dppclk_mhz)2680 max_dppclk_mhz = bw_params->clk_table.entries[i].dppclk_mhz;2681 if (bw_params->clk_table.entries[i].phyclk_mhz > max_phyclk_mhz)2682 max_phyclk_mhz = bw_params->clk_table.entries[i].phyclk_mhz;2683 if (bw_params->clk_table.entries[i].dtbclk_mhz > max_dtbclk_mhz)2684 max_dtbclk_mhz = bw_params->clk_table.entries[i].dtbclk_mhz;2685 }2686 2687 /* Scan through clock values we currently have and if they are 0,2688 * then populate it with dcn3_2_soc.clock_limits[] value.2689 *2690 * Do it for DCFCLK, DISPCLK, DTBCLK and UCLK as any of those being2691 * 0, will cause it to skip building the clock table.2692 */2693 if (max_dcfclk_mhz == 0)2694 bw_params->clk_table.entries[0].dcfclk_mhz = dcn3_2_soc.clock_limits[0].dcfclk_mhz;2695 if (max_dispclk_mhz == 0)2696 bw_params->clk_table.entries[0].dispclk_mhz = dcn3_2_soc.clock_limits[0].dispclk_mhz;2697 if (max_dtbclk_mhz == 0)2698 bw_params->clk_table.entries[0].dtbclk_mhz = dcn3_2_soc.clock_limits[0].dtbclk_mhz;2699 if (max_uclk_mhz == 0)2700 bw_params->clk_table.entries[0].memclk_mhz = dcn3_2_soc.clock_limits[0].dram_speed_mts / 16;2701}2702 2703static void swap_table_entries(struct _vcs_dpi_voltage_scaling_st *first_entry,2704 struct _vcs_dpi_voltage_scaling_st *second_entry)2705{2706 struct _vcs_dpi_voltage_scaling_st temp_entry = *first_entry;2707 *first_entry = *second_entry;2708 *second_entry = temp_entry;2709}2710 2711/*2712 * sort_entries_with_same_bw - Sort entries sharing the same bandwidth by DCFCLK2713 */2714static void sort_entries_with_same_bw(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries)2715{2716 unsigned int start_index = 0;2717 unsigned int end_index = 0;2718 unsigned int current_bw = 0;2719 2720 for (int i = 0; i < (*num_entries - 1); i++) {2721 if (table[i].net_bw_in_kbytes_sec == table[i+1].net_bw_in_kbytes_sec) {2722 current_bw = table[i].net_bw_in_kbytes_sec;2723 start_index = i;2724 end_index = ++i;2725 2726 while ((i < (*num_entries - 1)) && (table[i+1].net_bw_in_kbytes_sec == current_bw))2727 end_index = ++i;2728 }2729 2730 if (start_index != end_index) {2731 for (int j = start_index; j < end_index; j++) {2732 for (int k = start_index; k < end_index; k++) {2733 if (table[k].dcfclk_mhz > table[k+1].dcfclk_mhz)2734 swap_table_entries(&table[k], &table[k+1]);2735 }2736 }2737 }2738 2739 start_index = 0;2740 end_index = 0;2741 2742 }2743}2744 2745/*2746 * remove_inconsistent_entries - Ensure entries with the same bandwidth have MEMCLK and FCLK monotonically increasing2747 * and remove entries that do not2748 */2749static void remove_inconsistent_entries(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries)2750{2751 for (int i = 0; i < (*num_entries - 1); i++) {2752 if (table[i].net_bw_in_kbytes_sec == table[i+1].net_bw_in_kbytes_sec) {2753 if ((table[i].dram_speed_mts > table[i+1].dram_speed_mts) ||2754 (table[i].fabricclk_mhz > table[i+1].fabricclk_mhz))2755 remove_entry_from_table_at_index(table, num_entries, i);2756 }2757 }2758}2759 2760/*2761 * override_max_clk_values - Overwrite the max clock frequencies with the max DC mode timings2762 * Input:2763 * max_clk_limit - struct containing the desired clock timings2764 * Output:2765 * curr_clk_limit - struct containing the timings that need to be overwritten2766 * Return: 0 upon success, non-zero for failure2767 */2768static int override_max_clk_values(struct clk_limit_table_entry *max_clk_limit,2769 struct clk_limit_table_entry *curr_clk_limit)2770{2771 if (NULL == max_clk_limit || NULL == curr_clk_limit)2772 return -1; //invalid parameters2773 2774 //only overwrite if desired max clock frequency is initialized2775 if (max_clk_limit->dcfclk_mhz != 0)2776 curr_clk_limit->dcfclk_mhz = max_clk_limit->dcfclk_mhz;2777 2778 if (max_clk_limit->fclk_mhz != 0)2779 curr_clk_limit->fclk_mhz = max_clk_limit->fclk_mhz;2780 2781 if (max_clk_limit->memclk_mhz != 0)2782 curr_clk_limit->memclk_mhz = max_clk_limit->memclk_mhz;2783 2784 if (max_clk_limit->socclk_mhz != 0)2785 curr_clk_limit->socclk_mhz = max_clk_limit->socclk_mhz;2786 2787 if (max_clk_limit->dtbclk_mhz != 0)2788 curr_clk_limit->dtbclk_mhz = max_clk_limit->dtbclk_mhz;2789 2790 if (max_clk_limit->dispclk_mhz != 0)2791 curr_clk_limit->dispclk_mhz = max_clk_limit->dispclk_mhz;2792 2793 return 0;2794}2795 2796static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk_bw_params *bw_params,2797 struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries)2798{2799 int i, j;2800 struct _vcs_dpi_voltage_scaling_st entry = {0};2801 struct clk_limit_table_entry max_clk_data = {0};2802 2803 unsigned int min_dcfclk_mhz = 199, min_fclk_mhz = 299;2804 2805 static const unsigned int num_dcfclk_stas = 5;2806 unsigned int dcfclk_sta_targets[DC__VOLTAGE_STATES] = {199, 615, 906, 1324, 1564};2807 2808 unsigned int num_uclk_dpms = 0;2809 unsigned int num_fclk_dpms = 0;2810 unsigned int num_dcfclk_dpms = 0;2811 2812 unsigned int num_dc_uclk_dpms = 0;2813 unsigned int num_dc_fclk_dpms = 0;2814 unsigned int num_dc_dcfclk_dpms = 0;2815 2816 for (i = 0; i < MAX_NUM_DPM_LVL; i++) {2817 if (bw_params->clk_table.entries[i].dcfclk_mhz > max_clk_data.dcfclk_mhz)2818 max_clk_data.dcfclk_mhz = bw_params->clk_table.entries[i].dcfclk_mhz;2819 if (bw_params->clk_table.entries[i].fclk_mhz > max_clk_data.fclk_mhz)2820 max_clk_data.fclk_mhz = bw_params->clk_table.entries[i].fclk_mhz;2821 if (bw_params->clk_table.entries[i].memclk_mhz > max_clk_data.memclk_mhz)2822 max_clk_data.memclk_mhz = bw_params->clk_table.entries[i].memclk_mhz;2823 if (bw_params->clk_table.entries[i].dispclk_mhz > max_clk_data.dispclk_mhz)2824 max_clk_data.dispclk_mhz = bw_params->clk_table.entries[i].dispclk_mhz;2825 if (bw_params->clk_table.entries[i].dppclk_mhz > max_clk_data.dppclk_mhz)2826 max_clk_data.dppclk_mhz = bw_params->clk_table.entries[i].dppclk_mhz;2827 if (bw_params->clk_table.entries[i].phyclk_mhz > max_clk_data.phyclk_mhz)2828 max_clk_data.phyclk_mhz = bw_params->clk_table.entries[i].phyclk_mhz;2829 if (bw_params->clk_table.entries[i].dtbclk_mhz > max_clk_data.dtbclk_mhz)2830 max_clk_data.dtbclk_mhz = bw_params->clk_table.entries[i].dtbclk_mhz;2831 2832 if (bw_params->clk_table.entries[i].memclk_mhz > 0) {2833 num_uclk_dpms++;2834 if (bw_params->clk_table.entries[i].memclk_mhz <= bw_params->dc_mode_limit.memclk_mhz)2835 num_dc_uclk_dpms++;2836 }2837 if (bw_params->clk_table.entries[i].fclk_mhz > 0) {2838 num_fclk_dpms++;2839 if (bw_params->clk_table.entries[i].fclk_mhz <= bw_params->dc_mode_limit.fclk_mhz)2840 num_dc_fclk_dpms++;2841 }2842 if (bw_params->clk_table.entries[i].dcfclk_mhz > 0) {2843 num_dcfclk_dpms++;2844 if (bw_params->clk_table.entries[i].dcfclk_mhz <= bw_params->dc_mode_limit.dcfclk_mhz)2845 num_dc_dcfclk_dpms++;2846 }2847 }2848 2849 if (!disable_dc_mode_overwrite) {2850 //Overwrite max frequencies with max DC mode frequencies for DC mode systems2851 override_max_clk_values(&bw_params->dc_mode_limit, &max_clk_data);2852 num_uclk_dpms = num_dc_uclk_dpms;2853 num_fclk_dpms = num_dc_fclk_dpms;2854 num_dcfclk_dpms = num_dc_dcfclk_dpms;2855 bw_params->clk_table.num_entries_per_clk.num_memclk_levels = num_uclk_dpms;2856 bw_params->clk_table.num_entries_per_clk.num_fclk_levels = num_fclk_dpms;2857 }2858 2859 if (num_dcfclk_dpms > 0 && bw_params->clk_table.entries[0].fclk_mhz > min_fclk_mhz)2860 min_fclk_mhz = bw_params->clk_table.entries[0].fclk_mhz;2861 2862 if (!max_clk_data.dcfclk_mhz || !max_clk_data.dispclk_mhz || !max_clk_data.dtbclk_mhz)2863 return -1;2864 2865 if (max_clk_data.dppclk_mhz == 0)2866 max_clk_data.dppclk_mhz = max_clk_data.dispclk_mhz;2867 2868 if (max_clk_data.fclk_mhz == 0)2869 max_clk_data.fclk_mhz = max_clk_data.dcfclk_mhz *2870 dcn3_2_soc.pct_ideal_sdp_bw_after_urgent /2871 dcn3_2_soc.pct_ideal_fabric_bw_after_urgent;2872 2873 if (max_clk_data.phyclk_mhz == 0)2874 max_clk_data.phyclk_mhz = dcn3_2_soc.clock_limits[0].phyclk_mhz;2875 2876 *num_entries = 0;2877 entry.dispclk_mhz = max_clk_data.dispclk_mhz;2878 entry.dscclk_mhz = max_clk_data.dispclk_mhz / 3;2879 entry.dppclk_mhz = max_clk_data.dppclk_mhz;2880 entry.dtbclk_mhz = max_clk_data.dtbclk_mhz;2881 entry.phyclk_mhz = max_clk_data.phyclk_mhz;2882 entry.phyclk_d18_mhz = dcn3_2_soc.clock_limits[0].phyclk_d18_mhz;2883 entry.phyclk_d32_mhz = dcn3_2_soc.clock_limits[0].phyclk_d32_mhz;2884 2885 // Insert all the DCFCLK STAs2886 for (i = 0; i < num_dcfclk_stas; i++) {2887 entry.dcfclk_mhz = dcfclk_sta_targets[i];2888 entry.fabricclk_mhz = 0;2889 entry.dram_speed_mts = 0;2890 2891 get_optimal_ntuple(&entry);2892 entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry);2893 insert_entry_into_table_sorted(table, num_entries, &entry);2894 }2895 2896 // Insert the max DCFCLK2897 entry.dcfclk_mhz = max_clk_data.dcfclk_mhz;2898 entry.fabricclk_mhz = 0;2899 entry.dram_speed_mts = 0;2900 2901 get_optimal_ntuple(&entry);2902 entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry);2903 insert_entry_into_table_sorted(table, num_entries, &entry);2904 2905 // Insert the UCLK DPMS2906 for (i = 0; i < num_uclk_dpms; i++) {2907 entry.dcfclk_mhz = 0;2908 entry.fabricclk_mhz = 0;2909 entry.dram_speed_mts = bw_params->clk_table.entries[i].memclk_mhz * 16;2910 2911 get_optimal_ntuple(&entry);2912 entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry);2913 insert_entry_into_table_sorted(table, num_entries, &entry);2914 }2915 2916 // If FCLK is coarse grained, insert individual DPMs.2917 if (num_fclk_dpms > 2) {2918 for (i = 0; i < num_fclk_dpms; i++) {2919 entry.dcfclk_mhz = 0;2920 entry.fabricclk_mhz = bw_params->clk_table.entries[i].fclk_mhz;2921 entry.dram_speed_mts = 0;2922 2923 get_optimal_ntuple(&entry);2924 entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry);2925 insert_entry_into_table_sorted(table, num_entries, &entry);2926 }2927 }2928 // If FCLK fine grained, only insert max2929 else {2930 entry.dcfclk_mhz = 0;2931 entry.fabricclk_mhz = max_clk_data.fclk_mhz;2932 entry.dram_speed_mts = 0;2933 2934 get_optimal_ntuple(&entry);2935 entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry);2936 insert_entry_into_table_sorted(table, num_entries, &entry);2937 }2938 2939 // At this point, the table contains all "points of interest" based on2940 // DPMs from PMFW, and STAs. Table is sorted by BW, and all clock2941 // ratios (by derate, are exact).2942 2943 // Remove states that require higher clocks than are supported2944 for (i = *num_entries - 1; i >= 0 ; i--) {2945 if (table[i].dcfclk_mhz > max_clk_data.dcfclk_mhz ||2946 table[i].fabricclk_mhz > max_clk_data.fclk_mhz ||2947 table[i].dram_speed_mts > max_clk_data.memclk_mhz * 16)2948 remove_entry_from_table_at_index(table, num_entries, i);2949 }2950 2951 // Insert entry with all max dc limits without bandwidth matching2952 if (!disable_dc_mode_overwrite) {2953 struct _vcs_dpi_voltage_scaling_st max_dc_limits_entry = entry;2954 2955 max_dc_limits_entry.dcfclk_mhz = max_clk_data.dcfclk_mhz;2956 max_dc_limits_entry.fabricclk_mhz = max_clk_data.fclk_mhz;2957 max_dc_limits_entry.dram_speed_mts = max_clk_data.memclk_mhz * 16;2958 2959 max_dc_limits_entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&max_dc_limits_entry);2960 insert_entry_into_table_sorted(table, num_entries, &max_dc_limits_entry);2961 2962 sort_entries_with_same_bw(table, num_entries);2963 remove_inconsistent_entries(table, num_entries);2964 }2965 2966 // At this point, the table only contains supported points of interest2967 // it could be used as is, but some states may be redundant due to2968 // coarse grained nature of some clocks, so we want to round up to2969 // coarse grained DPMs and remove duplicates.2970 2971 // Round up UCLKs2972 for (i = *num_entries - 1; i >= 0 ; i--) {2973 for (j = 0; j < num_uclk_dpms; j++) {2974 if (bw_params->clk_table.entries[j].memclk_mhz * 16 >= table[i].dram_speed_mts) {2975 table[i].dram_speed_mts = bw_params->clk_table.entries[j].memclk_mhz * 16;2976 break;2977 }2978 }2979 }2980 2981 // If FCLK is coarse grained, round up to next DPMs2982 if (num_fclk_dpms > 2) {2983 for (i = *num_entries - 1; i >= 0 ; i--) {2984 for (j = 0; j < num_fclk_dpms; j++) {2985 if (bw_params->clk_table.entries[j].fclk_mhz >= table[i].fabricclk_mhz) {2986 table[i].fabricclk_mhz = bw_params->clk_table.entries[j].fclk_mhz;2987 break;2988 }2989 }2990 }2991 }2992 // Otherwise, round up to minimum.2993 else {2994 for (i = *num_entries - 1; i >= 0 ; i--) {2995 if (table[i].fabricclk_mhz < min_fclk_mhz) {2996 table[i].fabricclk_mhz = min_fclk_mhz;2997 }2998 }2999 }3000 3001 // Round DCFCLKs up to minimum3002 for (i = *num_entries - 1; i >= 0 ; i--) {3003 if (table[i].dcfclk_mhz < min_dcfclk_mhz) {3004 table[i].dcfclk_mhz = min_dcfclk_mhz;3005 }3006 }3007 3008 // Remove duplicate states, note duplicate states are always neighbouring since table is sorted.3009 i = 0;3010 while (i < *num_entries - 1) {3011 if (table[i].dcfclk_mhz == table[i + 1].dcfclk_mhz &&3012 table[i].fabricclk_mhz == table[i + 1].fabricclk_mhz &&3013 table[i].dram_speed_mts == table[i + 1].dram_speed_mts)3014 remove_entry_from_table_at_index(table, num_entries, i + 1);3015 else3016 i++;3017 }3018 3019 // Fix up the state indicies3020 for (i = *num_entries - 1; i >= 0 ; i--) {3021 table[i].state = i;3022 }3023 3024 return 0;3025}3026 3027/*3028 * dcn32_update_bw_bounding_box3029 *3030 * This would override some dcn3_2 ip_or_soc initial parameters hardcoded from3031 * spreadsheet with actual values as per dGPU SKU:3032 * - with passed few options from dc->config3033 * - with dentist_vco_frequency from Clk Mgr (currently hardcoded, but might3034 * need to get it from PM FW)3035 * - with passed latency values (passed in ns units) in dc-> bb override for3036 * debugging purposes3037 * - with passed latencies from VBIOS (in 100_ns units) if available for3038 * certain dGPU SKU3039 * - with number of DRAM channels from VBIOS (which differ for certain dGPU SKU3040 * of the same ASIC)3041 * - clocks levels with passed clk_table entries from Clk Mgr as reported by PM3042 * FW for different clocks (which might differ for certain dGPU SKU of the3043 * same ASIC)3044 */3045void dcn32_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params)3046{3047 dc_assert_fp_enabled();3048 3049 /* Overrides from dc->config options */3050 dcn3_2_ip.clamp_min_dcfclk = dc->config.clamp_min_dcfclk;3051 3052 /* Override from passed dc->bb_overrides if available*/3053 if ((int)(dcn3_2_soc.sr_exit_time_us * 1000) != dc->bb_overrides.sr_exit_time_ns3054 && dc->bb_overrides.sr_exit_time_ns) {3055 dc->dml2_options.bbox_overrides.sr_exit_latency_us =3056 dcn3_2_soc.sr_exit_time_us = dc->bb_overrides.sr_exit_time_ns / 1000.0;3057 }3058 3059 if ((int)(dcn3_2_soc.sr_enter_plus_exit_time_us * 1000)3060 != dc->bb_overrides.sr_enter_plus_exit_time_ns3061 && dc->bb_overrides.sr_enter_plus_exit_time_ns) {3062 dc->dml2_options.bbox_overrides.sr_enter_plus_exit_latency_us =3063 dcn3_2_soc.sr_enter_plus_exit_time_us =3064 dc->bb_overrides.sr_enter_plus_exit_time_ns / 1000.0;3065 }3066 3067 if ((int)(dcn3_2_soc.urgent_latency_us * 1000) != dc->bb_overrides.urgent_latency_ns3068 && dc->bb_overrides.urgent_latency_ns) {3069 dcn3_2_soc.urgent_latency_us = dc->bb_overrides.urgent_latency_ns / 1000.0;3070 dc->dml2_options.bbox_overrides.urgent_latency_us =3071 dcn3_2_soc.urgent_latency_pixel_data_only_us = dc->bb_overrides.urgent_latency_ns / 1000.0;3072 }3073 3074 if ((int)(dcn3_2_soc.dram_clock_change_latency_us * 1000)3075 != dc->bb_overrides.dram_clock_change_latency_ns3076 && dc->bb_overrides.dram_clock_change_latency_ns) {3077 dc->dml2_options.bbox_overrides.dram_clock_change_latency_us =3078 dcn3_2_soc.dram_clock_change_latency_us =3079 dc->bb_overrides.dram_clock_change_latency_ns / 1000.0;3080 }3081 3082 if ((int)(dcn3_2_soc.fclk_change_latency_us * 1000)3083 != dc->bb_overrides.fclk_clock_change_latency_ns3084 && dc->bb_overrides.fclk_clock_change_latency_ns) {3085 dc->dml2_options.bbox_overrides.fclk_change_latency_us =3086 dcn3_2_soc.fclk_change_latency_us =3087 dc->bb_overrides.fclk_clock_change_latency_ns / 1000;3088 }3089 3090 if ((int)(dcn3_2_soc.dummy_pstate_latency_us * 1000)3091 != dc->bb_overrides.dummy_clock_change_latency_ns3092 && dc->bb_overrides.dummy_clock_change_latency_ns) {3093 dcn3_2_soc.dummy_pstate_latency_us =3094 dc->bb_overrides.dummy_clock_change_latency_ns / 1000.0;3095 }3096 3097 /* Override from VBIOS if VBIOS bb_info available */3098 if (dc->ctx->dc_bios->funcs->get_soc_bb_info) {3099 struct bp_soc_bb_info bb_info = {0};3100 3101 if (dc->ctx->dc_bios->funcs->get_soc_bb_info(dc->ctx->dc_bios, &bb_info) == BP_RESULT_OK) {3102 if (bb_info.dram_clock_change_latency_100ns > 0)3103 dc->dml2_options.bbox_overrides.dram_clock_change_latency_us =3104 dcn3_2_soc.dram_clock_change_latency_us =3105 bb_info.dram_clock_change_latency_100ns * 10;3106 3107 if (bb_info.dram_sr_enter_exit_latency_100ns > 0)3108 dc->dml2_options.bbox_overrides.sr_enter_plus_exit_latency_us =3109 dcn3_2_soc.sr_enter_plus_exit_time_us =3110 bb_info.dram_sr_enter_exit_latency_100ns * 10;3111 3112 if (bb_info.dram_sr_exit_latency_100ns > 0)3113 dc->dml2_options.bbox_overrides.sr_exit_latency_us =3114 dcn3_2_soc.sr_exit_time_us =3115 bb_info.dram_sr_exit_latency_100ns * 10;3116 }3117 }3118 3119 /* Override from VBIOS for num_chan */3120 if (dc->ctx->dc_bios->vram_info.num_chans) {3121 dc->dml2_options.bbox_overrides.dram_num_chan =3122 dcn3_2_soc.num_chans = dc->ctx->dc_bios->vram_info.num_chans;3123 dcn3_2_soc.mall_allocated_for_dcn_mbytes = (double)(dcn32_calc_num_avail_chans_for_mall(dc,3124 dc->ctx->dc_bios->vram_info.num_chans) * dc->caps.mall_size_per_mem_channel);3125 }3126 3127 if (dc->ctx->dc_bios->vram_info.dram_channel_width_bytes)3128 dc->dml2_options.bbox_overrides.dram_chanel_width_bytes =3129 dcn3_2_soc.dram_channel_width_bytes = dc->ctx->dc_bios->vram_info.dram_channel_width_bytes;3130 3131 /* DML DSC delay factor workaround */3132 dcn3_2_ip.dsc_delay_factor_wa = dc->debug.dsc_delay_factor_wa_x1000 / 1000.0;3133 3134 dcn3_2_ip.min_prefetch_in_strobe_us = dc->debug.min_prefetch_in_strobe_ns / 1000.0;3135 3136 /* Override dispclk_dppclk_vco_speed_mhz from Clk Mgr */3137 dcn3_2_soc.dispclk_dppclk_vco_speed_mhz = dc->clk_mgr->dentist_vco_freq_khz / 1000.0;3138 dc->dml.soc.dispclk_dppclk_vco_speed_mhz = dc->clk_mgr->dentist_vco_freq_khz / 1000.0;3139 dc->dml2_options.bbox_overrides.disp_pll_vco_speed_mhz = dc->clk_mgr->dentist_vco_freq_khz / 1000.0;3140 dc->dml2_options.bbox_overrides.xtalclk_mhz = dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency / 1000.0;3141 dc->dml2_options.bbox_overrides.dchub_refclk_mhz = dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000.0;3142 dc->dml2_options.bbox_overrides.dprefclk_mhz = dc->clk_mgr->dprefclk_khz / 1000.0;3143 3144 /* Overrides Clock levelsfrom CLK Mgr table entries as reported by PM FW */3145 if (bw_params->clk_table.entries[0].memclk_mhz) {3146 if (dc->debug.use_legacy_soc_bb_mechanism) {3147 unsigned int i = 0, j = 0, num_states = 0;3148 3149 unsigned int dcfclk_mhz[DC__VOLTAGE_STATES] = {0};3150 unsigned int dram_speed_mts[DC__VOLTAGE_STATES] = {0};3151 unsigned int optimal_uclk_for_dcfclk_sta_targets[DC__VOLTAGE_STATES] = {0};3152 unsigned int optimal_dcfclk_for_uclk[DC__VOLTAGE_STATES] = {0};3153 unsigned int min_dcfclk = UINT_MAX;3154 /* Set 199 as first value in STA target array to have a minimum DCFCLK value.3155 * For DCN32 we set min to 199 so minimum FCLK DPM0 (300Mhz can be achieved) */3156 unsigned int dcfclk_sta_targets[DC__VOLTAGE_STATES] = {199, 615, 906, 1324, 1564};3157 unsigned int num_dcfclk_sta_targets = 4, num_uclk_states = 0;3158 unsigned int max_dcfclk_mhz = 0, max_dispclk_mhz = 0, max_dppclk_mhz = 0, max_phyclk_mhz = 0;3159 3160 for (i = 0; i < MAX_NUM_DPM_LVL; i++) {3161 if (bw_params->clk_table.entries[i].dcfclk_mhz > max_dcfclk_mhz)3162 max_dcfclk_mhz = bw_params->clk_table.entries[i].dcfclk_mhz;3163 if (bw_params->clk_table.entries[i].dcfclk_mhz != 0 &&3164 bw_params->clk_table.entries[i].dcfclk_mhz < min_dcfclk)3165 min_dcfclk = bw_params->clk_table.entries[i].dcfclk_mhz;3166 if (bw_params->clk_table.entries[i].dispclk_mhz > max_dispclk_mhz)3167 max_dispclk_mhz = bw_params->clk_table.entries[i].dispclk_mhz;3168 if (bw_params->clk_table.entries[i].dppclk_mhz > max_dppclk_mhz)3169 max_dppclk_mhz = bw_params->clk_table.entries[i].dppclk_mhz;3170 if (bw_params->clk_table.entries[i].phyclk_mhz > max_phyclk_mhz)3171 max_phyclk_mhz = bw_params->clk_table.entries[i].phyclk_mhz;3172 }3173 if (min_dcfclk > dcfclk_sta_targets[0])3174 dcfclk_sta_targets[0] = min_dcfclk;3175 if (!max_dcfclk_mhz)3176 max_dcfclk_mhz = dcn3_2_soc.clock_limits[0].dcfclk_mhz;3177 if (!max_dispclk_mhz)3178 max_dispclk_mhz = dcn3_2_soc.clock_limits[0].dispclk_mhz;3179 if (!max_dppclk_mhz)3180 max_dppclk_mhz = dcn3_2_soc.clock_limits[0].dppclk_mhz;3181 if (!max_phyclk_mhz)3182 max_phyclk_mhz = dcn3_2_soc.clock_limits[0].phyclk_mhz;3183 3184 if (max_dcfclk_mhz > dcfclk_sta_targets[num_dcfclk_sta_targets-1]) {3185 // If max DCFCLK is greater than the max DCFCLK STA target, insert into the DCFCLK STA target array3186 dcfclk_sta_targets[num_dcfclk_sta_targets] = max_dcfclk_mhz;3187 num_dcfclk_sta_targets++;3188 } else if (max_dcfclk_mhz < dcfclk_sta_targets[num_dcfclk_sta_targets-1]) {3189 // If max DCFCLK is less than the max DCFCLK STA target, cap values and remove duplicates3190 for (i = 0; i < num_dcfclk_sta_targets; i++) {3191 if (dcfclk_sta_targets[i] > max_dcfclk_mhz) {3192 dcfclk_sta_targets[i] = max_dcfclk_mhz;3193 break;3194 }3195 }3196 // Update size of array since we "removed" duplicates3197 num_dcfclk_sta_targets = i + 1;3198 }3199 3200 num_uclk_states = bw_params->clk_table.num_entries;3201 3202 // Calculate optimal dcfclk for each uclk3203 for (i = 0; i < num_uclk_states; i++) {3204 dcn32_get_optimal_dcfclk_fclk_for_uclk(bw_params->clk_table.entries[i].memclk_mhz * 16,3205 &optimal_dcfclk_for_uclk[i], NULL);3206 if (optimal_dcfclk_for_uclk[i] < bw_params->clk_table.entries[0].dcfclk_mhz) {3207 optimal_dcfclk_for_uclk[i] = bw_params->clk_table.entries[0].dcfclk_mhz;3208 }3209 }3210 3211 // Calculate optimal uclk for each dcfclk sta target3212 for (i = 0; i < num_dcfclk_sta_targets; i++) {3213 for (j = 0; j < num_uclk_states; j++) {3214 if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j]) {3215 optimal_uclk_for_dcfclk_sta_targets[i] =3216 bw_params->clk_table.entries[j].memclk_mhz * 16;3217 break;3218 }3219 }3220 }3221 3222 i = 0;3223 j = 0;3224 // create the final dcfclk and uclk table3225 while (i < num_dcfclk_sta_targets && j < num_uclk_states && num_states < DC__VOLTAGE_STATES) {3226 if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j] && i < num_dcfclk_sta_targets) {3227 dcfclk_mhz[num_states] = dcfclk_sta_targets[i];3228 dram_speed_mts[num_states++] = optimal_uclk_for_dcfclk_sta_targets[i++];3229 } else {3230 if (j < num_uclk_states && optimal_dcfclk_for_uclk[j] <= max_dcfclk_mhz) {3231 dcfclk_mhz[num_states] = optimal_dcfclk_for_uclk[j];3232 dram_speed_mts[num_states++] = bw_params->clk_table.entries[j++].memclk_mhz * 16;3233 } else {3234 j = num_uclk_states;3235 }3236 }3237 }3238 3239 while (i < num_dcfclk_sta_targets && num_states < DC__VOLTAGE_STATES) {3240 dcfclk_mhz[num_states] = dcfclk_sta_targets[i];3241 dram_speed_mts[num_states++] = optimal_uclk_for_dcfclk_sta_targets[i++];3242 }3243 3244 while (j < num_uclk_states && num_states < DC__VOLTAGE_STATES &&3245 optimal_dcfclk_for_uclk[j] <= max_dcfclk_mhz) {3246 dcfclk_mhz[num_states] = optimal_dcfclk_for_uclk[j];3247 dram_speed_mts[num_states++] = bw_params->clk_table.entries[j++].memclk_mhz * 16;3248 }3249 3250 /* bw_params->clk_table.entries[MAX_NUM_DPM_LVL].3251 * MAX_NUM_DPM_LVL is 8.3252 * dcn3_02_soc.clock_limits[DC__VOLTAGE_STATES].3253 * DC__VOLTAGE_STATES is 40.3254 */3255 if (num_states > MAX_NUM_DPM_LVL) {3256 ASSERT(0);3257 return;3258 }3259 3260 dcn3_2_soc.num_states = num_states;3261 for (i = 0; i < dcn3_2_soc.num_states; i++) {3262 dcn3_2_soc.clock_limits[i].state = i;3263 dcn3_2_soc.clock_limits[i].dcfclk_mhz = dcfclk_mhz[i];3264 dcn3_2_soc.clock_limits[i].fabricclk_mhz = dcfclk_mhz[i];3265 3266 /* Fill all states with max values of all these clocks */3267 dcn3_2_soc.clock_limits[i].dispclk_mhz = max_dispclk_mhz;3268 dcn3_2_soc.clock_limits[i].dppclk_mhz = max_dppclk_mhz;3269 dcn3_2_soc.clock_limits[i].phyclk_mhz = max_phyclk_mhz;3270 dcn3_2_soc.clock_limits[i].dscclk_mhz = max_dispclk_mhz / 3;3271 3272 /* Populate from bw_params for DTBCLK, SOCCLK */3273 if (i > 0) {3274 if (!bw_params->clk_table.entries[i].dtbclk_mhz) {3275 dcn3_2_soc.clock_limits[i].dtbclk_mhz = dcn3_2_soc.clock_limits[i-1].dtbclk_mhz;3276 } else {3277 dcn3_2_soc.clock_limits[i].dtbclk_mhz = bw_params->clk_table.entries[i].dtbclk_mhz;3278 }3279 } else if (bw_params->clk_table.entries[i].dtbclk_mhz) {3280 dcn3_2_soc.clock_limits[i].dtbclk_mhz = bw_params->clk_table.entries[i].dtbclk_mhz;3281 }3282 3283 if (!bw_params->clk_table.entries[i].socclk_mhz && i > 0)3284 dcn3_2_soc.clock_limits[i].socclk_mhz = dcn3_2_soc.clock_limits[i-1].socclk_mhz;3285 else3286 dcn3_2_soc.clock_limits[i].socclk_mhz = bw_params->clk_table.entries[i].socclk_mhz;3287 3288 if (!dram_speed_mts[i] && i > 0)3289 dcn3_2_soc.clock_limits[i].dram_speed_mts = dcn3_2_soc.clock_limits[i-1].dram_speed_mts;3290 else3291 dcn3_2_soc.clock_limits[i].dram_speed_mts = dram_speed_mts[i];3292 3293 /* These clocks cannot come from bw_params, always fill from dcn3_2_soc[0] */3294 /* PHYCLK_D18, PHYCLK_D32 */3295 dcn3_2_soc.clock_limits[i].phyclk_d18_mhz = dcn3_2_soc.clock_limits[0].phyclk_d18_mhz;3296 dcn3_2_soc.clock_limits[i].phyclk_d32_mhz = dcn3_2_soc.clock_limits[0].phyclk_d32_mhz;3297 }3298 } else {3299 build_synthetic_soc_states(dc->debug.disable_dc_mode_overwrite, bw_params,3300 dcn3_2_soc.clock_limits, &dcn3_2_soc.num_states);3301 }3302 3303 /* Re-init DML with updated bb */3304 dml_init_instance(&dc->dml, &dcn3_2_soc, &dcn3_2_ip, DML_PROJECT_DCN32);3305 if (dc->current_state)3306 dml_init_instance(&dc->current_state->bw_ctx.dml, &dcn3_2_soc, &dcn3_2_ip, DML_PROJECT_DCN32);3307 }3308 3309 if (dc->clk_mgr->bw_params->clk_table.num_entries > 1) {3310 unsigned int i = 0;3311 3312 dc->dml2_options.bbox_overrides.clks_table.num_states = dc->clk_mgr->bw_params->clk_table.num_entries;3313 3314 dc->dml2_options.bbox_overrides.clks_table.num_entries_per_clk.num_dcfclk_levels =3315 dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dcfclk_levels;3316 3317 dc->dml2_options.bbox_overrides.clks_table.num_entries_per_clk.num_fclk_levels =3318 dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_fclk_levels;3319 3320 dc->dml2_options.bbox_overrides.clks_table.num_entries_per_clk.num_memclk_levels =3321 dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_memclk_levels;3322 3323 dc->dml2_options.bbox_overrides.clks_table.num_entries_per_clk.num_socclk_levels =3324 dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_socclk_levels;3325 3326 dc->dml2_options.bbox_overrides.clks_table.num_entries_per_clk.num_dtbclk_levels =3327 dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dtbclk_levels;3328 3329 dc->dml2_options.bbox_overrides.clks_table.num_entries_per_clk.num_dispclk_levels =3330 dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dispclk_levels;3331 3332 dc->dml2_options.bbox_overrides.clks_table.num_entries_per_clk.num_dppclk_levels =3333 dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dppclk_levels;3334 3335 for (i = 0; i < dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dcfclk_levels; i++) {3336 if (dc->clk_mgr->bw_params->clk_table.entries[i].dcfclk_mhz)3337 dc->dml2_options.bbox_overrides.clks_table.clk_entries[i].dcfclk_mhz =3338 dc->clk_mgr->bw_params->clk_table.entries[i].dcfclk_mhz;3339 }3340 3341 for (i = 0; i < dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_fclk_levels; i++) {3342 if (dc->clk_mgr->bw_params->clk_table.entries[i].fclk_mhz)3343 dc->dml2_options.bbox_overrides.clks_table.clk_entries[i].fclk_mhz =3344 dc->clk_mgr->bw_params->clk_table.entries[i].fclk_mhz;3345 }3346 3347 for (i = 0; i < dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_memclk_levels; i++) {3348 if (dc->clk_mgr->bw_params->clk_table.entries[i].memclk_mhz)3349 dc->dml2_options.bbox_overrides.clks_table.clk_entries[i].memclk_mhz =3350 dc->clk_mgr->bw_params->clk_table.entries[i].memclk_mhz;3351 }3352 3353 for (i = 0; i < dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_socclk_levels; i++) {3354 if (dc->clk_mgr->bw_params->clk_table.entries[i].socclk_mhz)3355 dc->dml2_options.bbox_overrides.clks_table.clk_entries[i].socclk_mhz =3356 dc->clk_mgr->bw_params->clk_table.entries[i].socclk_mhz;3357 }3358 3359 for (i = 0; i < dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dtbclk_levels; i++) {3360 if (dc->clk_mgr->bw_params->clk_table.entries[i].dtbclk_mhz)3361 dc->dml2_options.bbox_overrides.clks_table.clk_entries[i].dtbclk_mhz =3362 dc->clk_mgr->bw_params->clk_table.entries[i].dtbclk_mhz;3363 }3364 3365 for (i = 0; i < dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_dispclk_levels; i++) {3366 if (dc->clk_mgr->bw_params->clk_table.entries[i].dispclk_mhz) {3367 dc->dml2_options.bbox_overrides.clks_table.clk_entries[i].dispclk_mhz =3368 dc->clk_mgr->bw_params->clk_table.entries[i].dispclk_mhz;3369 dc->dml2_options.bbox_overrides.clks_table.clk_entries[i].dppclk_mhz =3370 dc->clk_mgr->bw_params->clk_table.entries[i].dispclk_mhz;3371 }3372 }3373 }3374}3375 3376void dcn32_zero_pipe_dcc_fraction(display_e2e_pipe_params_st *pipes,3377 int pipe_cnt)3378{3379 dc_assert_fp_enabled();3380 3381 pipes[pipe_cnt].pipe.src.dcc_fraction_of_zs_req_luma = 0;3382 pipes[pipe_cnt].pipe.src.dcc_fraction_of_zs_req_chroma = 0;3383}3384 3385bool dcn32_allow_subvp_with_active_margin(struct pipe_ctx *pipe)3386{3387 bool allow = false;3388 uint32_t refresh_rate = 0;3389 uint32_t min_refresh = subvp_active_margin_list.min_refresh;3390 uint32_t max_refresh = subvp_active_margin_list.max_refresh;3391 uint32_t i;3392 3393 for (i = 0; i < SUBVP_ACTIVE_MARGIN_LIST_LEN; i++) {3394 uint32_t width = subvp_active_margin_list.res[i].width;3395 uint32_t height = subvp_active_margin_list.res[i].height;3396 3397 refresh_rate = (pipe->stream->timing.pix_clk_100hz * (uint64_t)100 +3398 pipe->stream->timing.v_total * pipe->stream->timing.h_total - (uint64_t)1);3399 refresh_rate = div_u64(refresh_rate, pipe->stream->timing.v_total);3400 refresh_rate = div_u64(refresh_rate, pipe->stream->timing.h_total);3401 3402 if (refresh_rate >= min_refresh && refresh_rate <= max_refresh &&3403 dcn32_check_native_scaling_for_res(pipe, width, height)) {3404 allow = true;3405 break;3406 }3407 }3408 return allow;3409}3410 3411/**3412 * dcn32_allow_subvp_high_refresh_rate: Determine if the high refresh rate config will allow subvp3413 *3414 * @dc: Current DC state3415 * @context: New DC state to be programmed3416 * @pipe: Pipe to be considered for use in subvp3417 *3418 * On high refresh rate display configs, we will allow subvp under the following conditions:3419 * 1. Resolution is 3840x2160, 3440x1440, or 2560x14403420 * 2. Refresh rate is between 120hz - 165hz3421 * 3. No scaling3422 * 4. Freesync is inactive3423 * 5. For single display cases, freesync must be disabled3424 *3425 * Return: True if pipe can be used for subvp, false otherwise3426 */3427bool dcn32_allow_subvp_high_refresh_rate(struct dc *dc, struct dc_state *context, struct pipe_ctx *pipe)3428{3429 bool allow = false;3430 uint32_t refresh_rate = 0;3431 uint32_t subvp_min_refresh = subvp_high_refresh_list.min_refresh;3432 uint32_t subvp_max_refresh = subvp_high_refresh_list.max_refresh;3433 uint32_t min_refresh = subvp_max_refresh;3434 uint32_t i;3435 3436 /* Only allow SubVP on high refresh displays if all connected displays3437 * are considered "high refresh" (i.e. >= 120hz). We do not want to3438 * allow combinations such as 120hz (SubVP) + 60hz (SubVP).3439 */3440 for (i = 0; i < dc->res_pool->pipe_count; i++) {3441 struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];3442 3443 if (!pipe_ctx->stream)3444 continue;3445 refresh_rate = (pipe_ctx->stream->timing.pix_clk_100hz * 100 +3446 pipe_ctx->stream->timing.v_total * pipe_ctx->stream->timing.h_total - 1)3447 / (double)(pipe_ctx->stream->timing.v_total * pipe_ctx->stream->timing.h_total);3448 3449 if (refresh_rate < min_refresh)3450 min_refresh = refresh_rate;3451 }3452 3453 if (!dc->debug.disable_subvp_high_refresh && min_refresh >= subvp_min_refresh && pipe->stream &&3454 pipe->plane_state && !(pipe->stream->vrr_active_variable || pipe->stream->vrr_active_fixed)) {3455 refresh_rate = (pipe->stream->timing.pix_clk_100hz * 100 +3456 pipe->stream->timing.v_total * pipe->stream->timing.h_total - 1)3457 / (double)(pipe->stream->timing.v_total * pipe->stream->timing.h_total);3458 if (refresh_rate >= subvp_min_refresh && refresh_rate <= subvp_max_refresh) {3459 for (i = 0; i < SUBVP_HIGH_REFRESH_LIST_LEN; i++) {3460 uint32_t width = subvp_high_refresh_list.res[i].width;3461 uint32_t height = subvp_high_refresh_list.res[i].height;3462 3463 if (dcn32_check_native_scaling_for_res(pipe, width, height)) {3464 if ((context->stream_count == 1 && !pipe->stream->allow_freesync) || context->stream_count > 1) {3465 allow = true;3466 break;3467 }3468 }3469 }3470 }3471 }3472 return allow;3473}3474 3475/**3476 * dcn32_determine_max_vratio_prefetch: Determine max Vratio for prefetch by driver policy3477 *3478 * @dc: Current DC state3479 * @context: New DC state to be programmed3480 *3481 * Return: Max vratio for prefetch3482 */3483double dcn32_determine_max_vratio_prefetch(struct dc *dc, struct dc_state *context)3484{3485 double max_vratio_pre = __DML_MAX_BW_RATIO_PRE__; // Default value is 43486 int i;3487 3488 /* For single display MPO configs, allow the max vratio to be 83489 * if any plane is YUV420 format3490 */3491 if (context->stream_count == 1 && context->stream_status[0].plane_count > 1) {3492 for (i = 0; i < context->stream_status[0].plane_count; i++) {3493 if (context->stream_status[0].plane_states[i]->format == SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr ||3494 context->stream_status[0].plane_states[i]->format == SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb) {3495 max_vratio_pre = __DML_MAX_VRATIO_PRE__;3496 }3497 }3498 }3499 return max_vratio_pre;3500}3501 3502/**3503 * dcn32_assign_fpo_vactive_candidate - Assign the FPO stream candidate for FPO + VActive case3504 *3505 * This function chooses the FPO candidate stream for FPO + VActive cases (2 stream config).3506 * For FPO + VAtive cases, the assumption is that one display has ActiveMargin > 0, and the3507 * other display has ActiveMargin <= 0. This function will choose the pipe/stream that has3508 * ActiveMargin <= 0 to be the FPO stream candidate if found.3509 *3510 *3511 * @dc: current dc state3512 * @context: new dc state3513 * @fpo_candidate_stream: pointer to FPO stream candidate if one is found3514 *3515 * Return: void3516 */3517void dcn32_assign_fpo_vactive_candidate(struct dc *dc, const struct dc_state *context, struct dc_stream_state **fpo_candidate_stream)3518{3519 unsigned int i, pipe_idx;3520 const struct vba_vars_st *vba = &context->bw_ctx.dml.vba;3521 3522 for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {3523 const struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];3524 3525 /* In DCN32/321, FPO uses per-pipe P-State force.3526 * If there's no planes, HUBP is power gated and3527 * therefore programming UCLK_PSTATE_FORCE does3528 * nothing (P-State will always be asserted naturally3529 * on a pipe that has HUBP power gated. Therefore we3530 * only want to enable FPO if the FPO pipe has both3531 * a stream and a plane.3532 */3533 if (!pipe->stream || !pipe->plane_state)3534 continue;3535 3536 if (vba->ActiveDRAMClockChangeLatencyMarginPerState[vba->VoltageLevel][vba->maxMpcComb][vba->pipe_plane[pipe_idx]] <= 0) {3537 *fpo_candidate_stream = pipe->stream;3538 break;3539 }3540 pipe_idx++;3541 }3542}3543 3544/**3545 * dcn32_find_vactive_pipe - Determines if the config has a pipe that can switch in VACTIVE3546 *3547 * @dc: current dc state3548 * @context: new dc state3549 * @fpo_candidate_stream: candidate stream to be chosen for FPO3550 * @vactive_margin_req_us: The vactive marign required for a vactive pipe to be considered "found"3551 *3552 * Return: True if VACTIVE display is found, false otherwise3553 */3554bool dcn32_find_vactive_pipe(struct dc *dc, const struct dc_state *context, struct dc_stream_state *fpo_candidate_stream, uint32_t vactive_margin_req_us)3555{3556 unsigned int i, pipe_idx;3557 const struct vba_vars_st *vba = &context->bw_ctx.dml.vba;3558 bool vactive_found = true;3559 unsigned int blank_us = 0;3560 3561 for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {3562 const struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];3563 3564 if (!pipe->stream)3565 continue;3566 3567 /* Don't need to check for vactive margin on the FPO candidate stream */3568 if (fpo_candidate_stream && pipe->stream == fpo_candidate_stream) {3569 pipe_idx++;3570 continue;3571 }3572 3573 /* Every plane (apart from the ones driven by the FPO pipes) needs to have active margin3574 * in order for us to have found a valid "vactive" config for FPO + Vactive3575 */3576 blank_us = ((pipe->stream->timing.v_total - pipe->stream->timing.v_addressable) * pipe->stream->timing.h_total /3577 (double)(pipe->stream->timing.pix_clk_100hz * 100)) * 1000000;3578 if (vba->ActiveDRAMClockChangeLatencyMarginPerState[vba->VoltageLevel][vba->maxMpcComb][vba->pipe_plane[pipe_idx]] < vactive_margin_req_us ||3579 pipe->stream->vrr_active_variable || pipe->stream->vrr_active_fixed || blank_us >= dc->debug.fpo_vactive_max_blank_us) {3580 vactive_found = false;3581 break;3582 }3583 pipe_idx++;3584 }3585 return vactive_found;3586}3587 3588void dcn32_set_clock_limits(const struct _vcs_dpi_soc_bounding_box_st *soc_bb)3589{3590 dc_assert_fp_enabled();3591 dcn3_2_soc.clock_limits[0].dcfclk_mhz = 1200.0;3592}3593 3594void dcn32_override_min_req_memclk(struct dc *dc, struct dc_state *context)3595{3596 // WA: restrict FPO and SubVP to use first non-strobe mode (DCN32 BW issue)3597 if ((context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching || dcn32_subvp_in_use(dc, context)) &&3598 dc->dml.soc.num_chans <= 8) {3599 int num_mclk_levels = dc->clk_mgr->bw_params->clk_table.num_entries_per_clk.num_memclk_levels;3600 3601 if (context->bw_ctx.dml.vba.DRAMSpeed <= dc->clk_mgr->bw_params->clk_table.entries[0].memclk_mhz * 16 &&3602 num_mclk_levels > 1) {3603 context->bw_ctx.dml.vba.DRAMSpeed = dc->clk_mgr->bw_params->clk_table.entries[1].memclk_mhz * 16;3604 context->bw_ctx.bw.dcn.clk.dramclk_khz = context->bw_ctx.dml.vba.DRAMSpeed * 1000 / 16;3605 }3606 }3607}3608