Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: MIT
0002 /*
0003  * Copyright 2022 Advanced Micro Devices, Inc.
0004  *
0005  * Permission is hereby granted, free of charge, to any person obtaining a
0006  * copy of this software and associated documentation files (the "Software"),
0007  * to deal in the Software without restriction, including without limitation
0008  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0009  * and/or sell copies of the Software, and to permit persons to whom the
0010  * Software is furnished to do so, subject to the following conditions:
0011  *
0012  * The above copyright notice and this permission notice shall be included in
0013  * all copies or substantial portions of the Software.
0014  *
0015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0016  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0017  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0018  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
0019  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0020  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0021  * OTHER DEALINGS IN THE SOFTWARE.
0022  *
0023  * Authors: AMD
0024  *
0025  */
0026 #include "dcn32_fpu.h"
0027 #include "dc_link_dp.h"
0028 #include "dcn32/dcn32_resource.h"
0029 #include "dcn20/dcn20_resource.h"
0030 #include "display_mode_vba_util_32.h"
0031 // We need this includes for WATERMARKS_* defines
0032 #include "clk_mgr/dcn32/dcn32_smu13_driver_if.h"
0033 #include "dcn30/dcn30_resource.h"
0034 
0035 #define DC_LOGGER_INIT(logger)
0036 
0037 struct _vcs_dpi_ip_params_st dcn3_2_ip = {
0038     .gpuvm_enable = 0,
0039     .gpuvm_max_page_table_levels = 4,
0040     .hostvm_enable = 0,
0041     .rob_buffer_size_kbytes = 128,
0042     .det_buffer_size_kbytes = DCN3_2_DEFAULT_DET_SIZE,
0043     .config_return_buffer_size_in_kbytes = 1280,
0044     .compressed_buffer_segment_size_in_kbytes = 64,
0045     .meta_fifo_size_in_kentries = 22,
0046     .zero_size_buffer_entries = 512,
0047     .compbuf_reserved_space_64b = 256,
0048     .compbuf_reserved_space_zs = 64,
0049     .dpp_output_buffer_pixels = 2560,
0050     .opp_output_buffer_lines = 1,
0051     .pixel_chunk_size_kbytes = 8,
0052     .alpha_pixel_chunk_size_kbytes = 4,
0053     .min_pixel_chunk_size_bytes = 1024,
0054     .dcc_meta_buffer_size_bytes = 6272,
0055     .meta_chunk_size_kbytes = 2,
0056     .min_meta_chunk_size_bytes = 256,
0057     .writeback_chunk_size_kbytes = 8,
0058     .ptoi_supported = false,
0059     .num_dsc = 4,
0060     .maximum_dsc_bits_per_component = 12,
0061     .maximum_pixels_per_line_per_dsc_unit = 6016,
0062     .dsc422_native_support = true,
0063     .is_line_buffer_bpp_fixed = true,
0064     .line_buffer_fixed_bpp = 57,
0065     .line_buffer_size_bits = 1171920,
0066     .max_line_buffer_lines = 32,
0067     .writeback_interface_buffer_size_kbytes = 90,
0068     .max_num_dpp = 4,
0069     .max_num_otg = 4,
0070     .max_num_hdmi_frl_outputs = 1,
0071     .max_num_wb = 1,
0072     .max_dchub_pscl_bw_pix_per_clk = 4,
0073     .max_pscl_lb_bw_pix_per_clk = 2,
0074     .max_lb_vscl_bw_pix_per_clk = 4,
0075     .max_vscl_hscl_bw_pix_per_clk = 4,
0076     .max_hscl_ratio = 6,
0077     .max_vscl_ratio = 6,
0078     .max_hscl_taps = 8,
0079     .max_vscl_taps = 8,
0080     .dpte_buffer_size_in_pte_reqs_luma = 64,
0081     .dpte_buffer_size_in_pte_reqs_chroma = 34,
0082     .dispclk_ramp_margin_percent = 1,
0083     .max_inter_dcn_tile_repeaters = 8,
0084     .cursor_buffer_size = 16,
0085     .cursor_chunk_size = 2,
0086     .writeback_line_buffer_buffer_size = 0,
0087     .writeback_min_hscl_ratio = 1,
0088     .writeback_min_vscl_ratio = 1,
0089     .writeback_max_hscl_ratio = 1,
0090     .writeback_max_vscl_ratio = 1,
0091     .writeback_max_hscl_taps = 1,
0092     .writeback_max_vscl_taps = 1,
0093     .dppclk_delay_subtotal = 47,
0094     .dppclk_delay_scl = 50,
0095     .dppclk_delay_scl_lb_only = 16,
0096     .dppclk_delay_cnvc_formatter = 28,
0097     .dppclk_delay_cnvc_cursor = 6,
0098     .dispclk_delay_subtotal = 125,
0099     .dynamic_metadata_vm_enabled = false,
0100     .odm_combine_4to1_supported = false,
0101     .dcc_supported = true,
0102     .max_num_dp2p0_outputs = 2,
0103     .max_num_dp2p0_streams = 4,
0104 };
0105 
0106 struct _vcs_dpi_soc_bounding_box_st dcn3_2_soc = {
0107     .clock_limits = {
0108         {
0109             .state = 0,
0110             .dcfclk_mhz = 1564.0,
0111             .fabricclk_mhz = 400.0,
0112             .dispclk_mhz = 2150.0,
0113             .dppclk_mhz = 2150.0,
0114             .phyclk_mhz = 810.0,
0115             .phyclk_d18_mhz = 667.0,
0116             .phyclk_d32_mhz = 625.0,
0117             .socclk_mhz = 1200.0,
0118             .dscclk_mhz = 716.667,
0119             .dram_speed_mts = 16000.0,
0120             .dtbclk_mhz = 1564.0,
0121         },
0122     },
0123     .num_states = 1,
0124     .sr_exit_time_us = 42.97,
0125     .sr_enter_plus_exit_time_us = 49.94,
0126     .sr_exit_z8_time_us = 285.0,
0127     .sr_enter_plus_exit_z8_time_us = 320,
0128     .writeback_latency_us = 12.0,
0129     .round_trip_ping_latency_dcfclk_cycles = 263,
0130     .urgent_latency_pixel_data_only_us = 4.0,
0131     .urgent_latency_pixel_mixed_with_vm_data_us = 4.0,
0132     .urgent_latency_vm_data_only_us = 4.0,
0133     .fclk_change_latency_us = 20,
0134     .usr_retraining_latency_us = 2,
0135     .smn_latency_us = 2,
0136     .mall_allocated_for_dcn_mbytes = 64,
0137     .urgent_out_of_order_return_per_channel_pixel_only_bytes = 4096,
0138     .urgent_out_of_order_return_per_channel_pixel_and_vm_bytes = 4096,
0139     .urgent_out_of_order_return_per_channel_vm_only_bytes = 4096,
0140     .pct_ideal_sdp_bw_after_urgent = 100.0,
0141     .pct_ideal_fabric_bw_after_urgent = 67.0,
0142     .pct_ideal_dram_sdp_bw_after_urgent_pixel_only = 20.0,
0143     .pct_ideal_dram_sdp_bw_after_urgent_pixel_and_vm = 60.0, // N/A, for now keep as is until DML implemented
0144     .pct_ideal_dram_sdp_bw_after_urgent_vm_only = 30.0, // N/A, for now keep as is until DML implemented
0145     .pct_ideal_dram_bw_after_urgent_strobe = 67.0,
0146     .max_avg_sdp_bw_use_normal_percent = 80.0,
0147     .max_avg_fabric_bw_use_normal_percent = 60.0,
0148     .max_avg_dram_bw_use_normal_strobe_percent = 50.0,
0149     .max_avg_dram_bw_use_normal_percent = 15.0,
0150     .num_chans = 8,
0151     .dram_channel_width_bytes = 2,
0152     .fabric_datapath_to_dcn_data_return_bytes = 64,
0153     .return_bus_width_bytes = 64,
0154     .downspread_percent = 0.38,
0155     .dcn_downspread_percent = 0.5,
0156     .dram_clock_change_latency_us = 400,
0157     .dispclk_dppclk_vco_speed_mhz = 4300.0,
0158     .do_urgent_latency_adjustment = true,
0159     .urgent_latency_adjustment_fabric_clock_component_us = 1.0,
0160     .urgent_latency_adjustment_fabric_clock_reference_mhz = 1000,
0161 };
0162 
0163 void dcn32_build_wm_range_table_fpu(struct clk_mgr_internal *clk_mgr)
0164 {
0165     /* defaults */
0166     double pstate_latency_us = clk_mgr->base.ctx->dc->dml.soc.dram_clock_change_latency_us;
0167     double fclk_change_latency_us = clk_mgr->base.ctx->dc->dml.soc.fclk_change_latency_us;
0168     double sr_exit_time_us = clk_mgr->base.ctx->dc->dml.soc.sr_exit_time_us;
0169     double sr_enter_plus_exit_time_us = clk_mgr->base.ctx->dc->dml.soc.sr_enter_plus_exit_time_us;
0170     /* For min clocks use as reported by PM FW and report those as min */
0171     uint16_t min_uclk_mhz           = clk_mgr->base.bw_params->clk_table.entries[0].memclk_mhz;
0172     uint16_t min_dcfclk_mhz         = clk_mgr->base.bw_params->clk_table.entries[0].dcfclk_mhz;
0173     uint16_t setb_min_uclk_mhz      = min_uclk_mhz;
0174     uint16_t dcfclk_mhz_for_the_second_state = clk_mgr->base.ctx->dc->dml.soc.clock_limits[2].dcfclk_mhz;
0175 
0176     dc_assert_fp_enabled();
0177 
0178     /* For Set B ranges use min clocks state 2 when available, and report those to PM FW */
0179     if (dcfclk_mhz_for_the_second_state)
0180         clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.min_dcfclk = dcfclk_mhz_for_the_second_state;
0181     else
0182         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;
0183 
0184     if (clk_mgr->base.bw_params->clk_table.entries[2].memclk_mhz)
0185         setb_min_uclk_mhz = clk_mgr->base.bw_params->clk_table.entries[2].memclk_mhz;
0186 
0187     /* Set A - Normal - default values */
0188     clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].valid = true;
0189     clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].dml_input.pstate_latency_us = pstate_latency_us;
0190     clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].dml_input.fclk_change_latency_us = fclk_change_latency_us;
0191     clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].dml_input.sr_exit_time_us = sr_exit_time_us;
0192     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;
0193     clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.wm_type = WATERMARKS_CLOCK_RANGE;
0194     clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.min_dcfclk = min_dcfclk_mhz;
0195     clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.max_dcfclk = 0xFFFF;
0196     clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.min_uclk = min_uclk_mhz;
0197     clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.max_uclk = 0xFFFF;
0198 
0199     /* Set B - Performance - higher clocks, using DPM[2] DCFCLK and UCLK */
0200     clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].valid = true;
0201     clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].dml_input.pstate_latency_us = pstate_latency_us;
0202     clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].dml_input.fclk_change_latency_us = fclk_change_latency_us;
0203     clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].dml_input.sr_exit_time_us = sr_exit_time_us;
0204     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;
0205     clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.wm_type = WATERMARKS_CLOCK_RANGE;
0206     clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.max_dcfclk = 0xFFFF;
0207     clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.min_uclk = setb_min_uclk_mhz;
0208     clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.max_uclk = 0xFFFF;
0209 
0210     /* Set C - Dummy P-State - P-State latency set to "dummy p-state" value */
0211     /* 'DalDummyClockChangeLatencyNs' registry key option set to 0x7FFFFFFF can be used to disable Set C for dummy p-state */
0212     if (clk_mgr->base.ctx->dc->bb_overrides.dummy_clock_change_latency_ns != 0x7FFFFFFF) {
0213         clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].valid = true;
0214         clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].dml_input.pstate_latency_us = 38;
0215         clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].dml_input.fclk_change_latency_us = fclk_change_latency_us;
0216         clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].dml_input.sr_exit_time_us = sr_exit_time_us;
0217         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;
0218         clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.wm_type = WATERMARKS_DUMMY_PSTATE;
0219         clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.min_dcfclk = min_dcfclk_mhz;
0220         clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.max_dcfclk = 0xFFFF;
0221         clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.min_uclk = min_uclk_mhz;
0222         clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.max_uclk = 0xFFFF;
0223         clk_mgr->base.bw_params->dummy_pstate_table[0].dram_speed_mts = clk_mgr->base.bw_params->clk_table.entries[0].memclk_mhz * 16;
0224         clk_mgr->base.bw_params->dummy_pstate_table[0].dummy_pstate_latency_us = 38;
0225         clk_mgr->base.bw_params->dummy_pstate_table[1].dram_speed_mts = clk_mgr->base.bw_params->clk_table.entries[1].memclk_mhz * 16;
0226         clk_mgr->base.bw_params->dummy_pstate_table[1].dummy_pstate_latency_us = 9;
0227         clk_mgr->base.bw_params->dummy_pstate_table[2].dram_speed_mts = clk_mgr->base.bw_params->clk_table.entries[2].memclk_mhz * 16;
0228         clk_mgr->base.bw_params->dummy_pstate_table[2].dummy_pstate_latency_us = 8;
0229         clk_mgr->base.bw_params->dummy_pstate_table[3].dram_speed_mts = clk_mgr->base.bw_params->clk_table.entries[3].memclk_mhz * 16;
0230         clk_mgr->base.bw_params->dummy_pstate_table[3].dummy_pstate_latency_us = 5;
0231     }
0232     /* Set D - MALL - SR enter and exit time specific to MALL, TBD after bringup or later phase for now use DRAM values / 2 */
0233     /* For MALL DRAM clock change latency is N/A, for watermak calculations use lowest value dummy P state latency */
0234     clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].valid = true;
0235     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;
0236     clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].dml_input.fclk_change_latency_us = fclk_change_latency_us;
0237     clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].dml_input.sr_exit_time_us = sr_exit_time_us / 2; // TBD
0238     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; // TBD
0239     clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.wm_type = WATERMARKS_MALL;
0240     clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.min_dcfclk = min_dcfclk_mhz;
0241     clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.max_dcfclk = 0xFFFF;
0242     clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.min_uclk = min_uclk_mhz;
0243     clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.max_uclk = 0xFFFF;
0244 }
0245 
0246 /**
0247  * Finds dummy_latency_index when MCLK switching using firmware based
0248  * vblank stretch is enabled. This function will iterate through the
0249  * table of dummy pstate latencies until the lowest value that allows
0250  * dm_allow_self_refresh_and_mclk_switch to happen is found
0251  */
0252 int dcn32_find_dummy_latency_index_for_fw_based_mclk_switch(struct dc *dc,
0253                                 struct dc_state *context,
0254                                 display_e2e_pipe_params_st *pipes,
0255                                 int pipe_cnt,
0256                                 int vlevel)
0257 {
0258     const int max_latency_table_entries = 4;
0259     const struct vba_vars_st *vba = &context->bw_ctx.dml.vba;
0260     int dummy_latency_index = 0;
0261 
0262     dc_assert_fp_enabled();
0263 
0264     while (dummy_latency_index < max_latency_table_entries) {
0265         context->bw_ctx.dml.soc.dram_clock_change_latency_us =
0266                 dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dummy_pstate_latency_us;
0267         dcn32_internal_validate_bw(dc, context, pipes, &pipe_cnt, &vlevel, false);
0268 
0269         if (vlevel < context->bw_ctx.dml.vba.soc.num_states &&
0270                 vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] != dm_dram_clock_change_unsupported)
0271             break;
0272 
0273         dummy_latency_index++;
0274     }
0275 
0276     if (dummy_latency_index == max_latency_table_entries) {
0277         ASSERT(dummy_latency_index != max_latency_table_entries);
0278         /* If the execution gets here, it means dummy p_states are
0279          * not possible. This should never happen and would mean
0280          * something is severely wrong.
0281          * Here we reset dummy_latency_index to 3, because it is
0282          * better to have underflows than system crashes.
0283          */
0284         dummy_latency_index = max_latency_table_entries - 1;
0285     }
0286 
0287     return dummy_latency_index;
0288 }
0289 
0290 /**
0291  * dcn32_helper_populate_phantom_dlg_params - Get DLG params for phantom pipes
0292  * and populate pipe_ctx with those params.
0293  *
0294  * This function must be called AFTER the phantom pipes are added to context
0295  * and run through DML (so that the DLG params for the phantom pipes can be
0296  * populated), and BEFORE we program the timing for the phantom pipes.
0297  *
0298  * @dc: [in] current dc state
0299  * @context: [in] new dc state
0300  * @pipes: [in] DML pipe params array
0301  * @pipe_cnt: [in] DML pipe count
0302  */
0303 void dcn32_helper_populate_phantom_dlg_params(struct dc *dc,
0304                           struct dc_state *context,
0305                           display_e2e_pipe_params_st *pipes,
0306                           int pipe_cnt)
0307 {
0308     uint32_t i, pipe_idx;
0309 
0310     dc_assert_fp_enabled();
0311 
0312     for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
0313         struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
0314 
0315         if (!pipe->stream)
0316             continue;
0317 
0318         if (pipe->plane_state && pipe->stream->mall_stream_config.type == SUBVP_PHANTOM) {
0319             pipes[pipe_idx].pipe.dest.vstartup_start =
0320                 get_vstartup(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);
0321             pipes[pipe_idx].pipe.dest.vupdate_offset =
0322                 get_vupdate_offset(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);
0323             pipes[pipe_idx].pipe.dest.vupdate_width =
0324                 get_vupdate_width(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);
0325             pipes[pipe_idx].pipe.dest.vready_offset =
0326                 get_vready_offset(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);
0327             pipe->pipe_dlg_param = pipes[pipe_idx].pipe.dest;
0328         }
0329         pipe_idx++;
0330     }
0331 }
0332 
0333 bool dcn32_predict_pipe_split(struct dc_state *context, display_pipe_params_st pipe, int index)
0334 {
0335     double pscl_throughput;
0336     double pscl_throughput_chroma;
0337     double dpp_clk_single_dpp, clock;
0338     double clk_frequency = 0.0;
0339     double vco_speed = context->bw_ctx.dml.soc.dispclk_dppclk_vco_speed_mhz;
0340 
0341     dc_assert_fp_enabled();
0342 
0343     dml32_CalculateSinglePipeDPPCLKAndSCLThroughput(pipe.scale_ratio_depth.hscl_ratio,
0344                             pipe.scale_ratio_depth.hscl_ratio_c,
0345                             pipe.scale_ratio_depth.vscl_ratio,
0346                             pipe.scale_ratio_depth.vscl_ratio_c,
0347                             context->bw_ctx.dml.ip.max_dchub_pscl_bw_pix_per_clk,
0348                             context->bw_ctx.dml.ip.max_pscl_lb_bw_pix_per_clk,
0349                             pipe.dest.pixel_rate_mhz,
0350                             pipe.src.source_format,
0351                             pipe.scale_taps.htaps,
0352                             pipe.scale_taps.htaps_c,
0353                             pipe.scale_taps.vtaps,
0354                             pipe.scale_taps.vtaps_c,
0355                             /* Output */
0356                             &pscl_throughput, &pscl_throughput_chroma,
0357                             &dpp_clk_single_dpp);
0358 
0359     clock = dpp_clk_single_dpp * (1 + context->bw_ctx.dml.soc.dcn_downspread_percent / 100);
0360 
0361     if (clock > 0)
0362         clk_frequency = vco_speed * 4.0 / ((int)(vco_speed * 4.0));
0363 
0364     if (clk_frequency > context->bw_ctx.dml.soc.clock_limits[index].dppclk_mhz)
0365         return true;
0366     else
0367         return false;
0368 }
0369 
0370 static float calculate_net_bw_in_kbytes_sec(struct _vcs_dpi_voltage_scaling_st *entry)
0371 {
0372     float memory_bw_kbytes_sec;
0373     float fabric_bw_kbytes_sec;
0374     float sdp_bw_kbytes_sec;
0375     float limiting_bw_kbytes_sec;
0376 
0377     memory_bw_kbytes_sec = entry->dram_speed_mts *
0378                 dcn3_2_soc.num_chans *
0379                 dcn3_2_soc.dram_channel_width_bytes *
0380                 ((float)dcn3_2_soc.pct_ideal_dram_sdp_bw_after_urgent_pixel_only / 100);
0381 
0382     fabric_bw_kbytes_sec = entry->fabricclk_mhz *
0383                 dcn3_2_soc.return_bus_width_bytes *
0384                 ((float)dcn3_2_soc.pct_ideal_fabric_bw_after_urgent / 100);
0385 
0386     sdp_bw_kbytes_sec = entry->dcfclk_mhz *
0387                 dcn3_2_soc.return_bus_width_bytes *
0388                 ((float)dcn3_2_soc.pct_ideal_sdp_bw_after_urgent / 100);
0389 
0390     limiting_bw_kbytes_sec = memory_bw_kbytes_sec;
0391 
0392     if (fabric_bw_kbytes_sec < limiting_bw_kbytes_sec)
0393         limiting_bw_kbytes_sec = fabric_bw_kbytes_sec;
0394 
0395     if (sdp_bw_kbytes_sec < limiting_bw_kbytes_sec)
0396         limiting_bw_kbytes_sec = sdp_bw_kbytes_sec;
0397 
0398     return limiting_bw_kbytes_sec;
0399 }
0400 
0401 static void get_optimal_ntuple(struct _vcs_dpi_voltage_scaling_st *entry)
0402 {
0403     if (entry->dcfclk_mhz > 0) {
0404         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);
0405 
0406         entry->fabricclk_mhz = bw_on_sdp / (dcn3_2_soc.return_bus_width_bytes * ((float)dcn3_2_soc.pct_ideal_fabric_bw_after_urgent / 100));
0407         entry->dram_speed_mts = bw_on_sdp / (dcn3_2_soc.num_chans *
0408                 dcn3_2_soc.dram_channel_width_bytes * ((float)dcn3_2_soc.pct_ideal_dram_sdp_bw_after_urgent_pixel_only / 100));
0409     } else if (entry->fabricclk_mhz > 0) {
0410         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);
0411 
0412         entry->dcfclk_mhz = bw_on_fabric / (dcn3_2_soc.return_bus_width_bytes * ((float)dcn3_2_soc.pct_ideal_sdp_bw_after_urgent / 100));
0413         entry->dram_speed_mts = bw_on_fabric / (dcn3_2_soc.num_chans *
0414                 dcn3_2_soc.dram_channel_width_bytes * ((float)dcn3_2_soc.pct_ideal_dram_sdp_bw_after_urgent_pixel_only / 100));
0415     } else if (entry->dram_speed_mts > 0) {
0416         float bw_on_dram = entry->dram_speed_mts * dcn3_2_soc.num_chans *
0417                 dcn3_2_soc.dram_channel_width_bytes * ((float)dcn3_2_soc.pct_ideal_dram_sdp_bw_after_urgent_pixel_only / 100);
0418 
0419         entry->fabricclk_mhz = bw_on_dram / (dcn3_2_soc.return_bus_width_bytes * ((float)dcn3_2_soc.pct_ideal_fabric_bw_after_urgent / 100));
0420         entry->dcfclk_mhz = bw_on_dram / (dcn3_2_soc.return_bus_width_bytes * ((float)dcn3_2_soc.pct_ideal_sdp_bw_after_urgent / 100));
0421     }
0422 }
0423 
0424 void insert_entry_into_table_sorted(struct _vcs_dpi_voltage_scaling_st *table,
0425                     unsigned int *num_entries,
0426                     struct _vcs_dpi_voltage_scaling_st *entry)
0427 {
0428     int i = 0;
0429     int index = 0;
0430     float net_bw_of_new_state = 0;
0431 
0432     dc_assert_fp_enabled();
0433 
0434     get_optimal_ntuple(entry);
0435 
0436     if (*num_entries == 0) {
0437         table[0] = *entry;
0438         (*num_entries)++;
0439     } else {
0440         net_bw_of_new_state = calculate_net_bw_in_kbytes_sec(entry);
0441         while (net_bw_of_new_state > calculate_net_bw_in_kbytes_sec(&table[index])) {
0442             index++;
0443             if (index >= *num_entries)
0444                 break;
0445         }
0446 
0447         for (i = *num_entries; i > index; i--)
0448             table[i] = table[i - 1];
0449 
0450         table[index] = *entry;
0451         (*num_entries)++;
0452     }
0453 }
0454 
0455 /**
0456  * dcn32_set_phantom_stream_timing: Set timing params for the phantom stream
0457  *
0458  * Set timing params of the phantom stream based on calculated output from DML.
0459  * This function first gets the DML pipe index using the DC pipe index, then
0460  * calls into DML (get_subviewport_lines_needed_in_mall) to get the number of
0461  * lines required for SubVP MCLK switching and assigns to the phantom stream
0462  * accordingly.
0463  *
0464  * - The number of SubVP lines calculated in DML does not take into account
0465  * FW processing delays and required pstate allow width, so we must include
0466  * that separately.
0467  *
0468  * - Set phantom backporch = vstartup of main pipe
0469  *
0470  * @dc: current dc state
0471  * @context: new dc state
0472  * @ref_pipe: Main pipe for the phantom stream
0473  * @pipes: DML pipe params
0474  * @pipe_cnt: number of DML pipes
0475  * @dc_pipe_idx: DC pipe index for the main pipe (i.e. ref_pipe)
0476  */
0477 void dcn32_set_phantom_stream_timing(struct dc *dc,
0478                      struct dc_state *context,
0479                      struct pipe_ctx *ref_pipe,
0480                      struct dc_stream_state *phantom_stream,
0481                      display_e2e_pipe_params_st *pipes,
0482                      unsigned int pipe_cnt,
0483                      unsigned int dc_pipe_idx)
0484 {
0485     unsigned int i, pipe_idx;
0486     struct pipe_ctx *pipe;
0487     uint32_t phantom_vactive, phantom_bp, pstate_width_fw_delay_lines;
0488     unsigned int vlevel = context->bw_ctx.dml.vba.VoltageLevel;
0489     unsigned int dcfclk = context->bw_ctx.dml.vba.DCFCLKState[vlevel][context->bw_ctx.dml.vba.maxMpcComb];
0490     unsigned int socclk = context->bw_ctx.dml.vba.SOCCLKPerState[vlevel];
0491 
0492     dc_assert_fp_enabled();
0493 
0494     // Find DML pipe index (pipe_idx) using dc_pipe_idx
0495     for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
0496         pipe = &context->res_ctx.pipe_ctx[i];
0497 
0498         if (!pipe->stream)
0499             continue;
0500 
0501         if (i == dc_pipe_idx)
0502             break;
0503 
0504         pipe_idx++;
0505     }
0506 
0507     // Calculate lines required for pstate allow width and FW processing delays
0508     pstate_width_fw_delay_lines = ((double)(dc->caps.subvp_fw_processing_delay_us +
0509             dc->caps.subvp_pstate_allow_width_us) / 1000000) *
0510             (ref_pipe->stream->timing.pix_clk_100hz * 100) /
0511             (double)ref_pipe->stream->timing.h_total;
0512 
0513     // Update clks_cfg for calling into recalculate
0514     pipes[0].clks_cfg.voltage = vlevel;
0515     pipes[0].clks_cfg.dcfclk_mhz = dcfclk;
0516     pipes[0].clks_cfg.socclk_mhz = socclk;
0517 
0518     // DML calculation for MALL region doesn't take into account FW delay
0519     // and required pstate allow width for multi-display cases
0520     /* Add 16 lines margin to the MALL REGION because SUB_VP_START_LINE must be aligned
0521      * to 2 swaths (i.e. 16 lines)
0522      */
0523     phantom_vactive = get_subviewport_lines_needed_in_mall(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx) +
0524                 pstate_width_fw_delay_lines + dc->caps.subvp_swath_height_margin_lines;
0525 
0526     // For backporch of phantom pipe, use vstartup of the main pipe
0527     phantom_bp = get_vstartup(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);
0528 
0529     phantom_stream->dst.y = 0;
0530     phantom_stream->dst.height = phantom_vactive;
0531     phantom_stream->src.y = 0;
0532     phantom_stream->src.height = phantom_vactive;
0533 
0534     phantom_stream->timing.v_addressable = phantom_vactive;
0535     phantom_stream->timing.v_front_porch = 1;
0536     phantom_stream->timing.v_total = phantom_stream->timing.v_addressable +
0537                         phantom_stream->timing.v_front_porch +
0538                         phantom_stream->timing.v_sync_width +
0539                         phantom_bp;
0540     phantom_stream->timing.flags.DSC = 0; // Don't need DSC for phantom timing
0541 }
0542 
0543 /**
0544  * dcn32_get_num_free_pipes: Calculate number of free pipes
0545  *
0546  * This function assumes that a "used" pipe is a pipe that has
0547  * both a stream and a plane assigned to it.
0548  *
0549  * @dc: current dc state
0550  * @context: new dc state
0551  *
0552  * Return:
0553  * Number of free pipes available in the context
0554  */
0555 static unsigned int dcn32_get_num_free_pipes(struct dc *dc, struct dc_state *context)
0556 {
0557     unsigned int i;
0558     unsigned int free_pipes = 0;
0559     unsigned int num_pipes = 0;
0560 
0561     for (i = 0; i < dc->res_pool->pipe_count; i++) {
0562         struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
0563 
0564         if (pipe->stream && !pipe->top_pipe) {
0565             while (pipe) {
0566                 num_pipes++;
0567                 pipe = pipe->bottom_pipe;
0568             }
0569         }
0570     }
0571 
0572     free_pipes = dc->res_pool->pipe_count - num_pipes;
0573     return free_pipes;
0574 }
0575 
0576 /**
0577  * dcn32_assign_subvp_pipe: Function to decide which pipe will use Sub-VP.
0578  *
0579  * We enter this function if we are Sub-VP capable (i.e. enough pipes available)
0580  * and regular P-State switching (i.e. VACTIVE/VBLANK) is not supported, or if
0581  * we are forcing SubVP P-State switching on the current config.
0582  *
0583  * The number of pipes used for the chosen surface must be less than or equal to the
0584  * number of free pipes available.
0585  *
0586  * In general we choose surfaces with the longest frame time first (better for SubVP + VBLANK).
0587  * For multi-display cases the ActiveDRAMClockChangeMargin doesn't provide enough info on its own
0588  * for determining which should be the SubVP pipe (need a way to determine if a pipe / plane doesn't
0589  * support MCLK switching naturally [i.e. ACTIVE or VBLANK]).
0590  *
0591  * @param dc: current dc state
0592  * @param context: new dc state
0593  * @param index: [out] dc pipe index for the pipe chosen to have phantom pipes assigned
0594  *
0595  * Return:
0596  * True if a valid pipe assignment was found for Sub-VP. Otherwise false.
0597  */
0598 static bool dcn32_assign_subvp_pipe(struct dc *dc,
0599                     struct dc_state *context,
0600                     unsigned int *index)
0601 {
0602     unsigned int i, pipe_idx;
0603     unsigned int max_frame_time = 0;
0604     bool valid_assignment_found = false;
0605     unsigned int free_pipes = dcn32_get_num_free_pipes(dc, context);
0606     bool current_assignment_freesync = false;
0607 
0608     for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
0609         struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
0610         unsigned int num_pipes = 0;
0611         unsigned int refresh_rate = 0;
0612 
0613         if (!pipe->stream)
0614             continue;
0615 
0616         // Round up
0617         refresh_rate = (pipe->stream->timing.pix_clk_100hz * 100 +
0618                 pipe->stream->timing.v_total * pipe->stream->timing.h_total - 1)
0619                 / (double)(pipe->stream->timing.v_total * pipe->stream->timing.h_total);
0620         if (pipe->plane_state && !pipe->top_pipe &&
0621                 pipe->stream->mall_stream_config.type == SUBVP_NONE && refresh_rate < 120) {
0622             while (pipe) {
0623                 num_pipes++;
0624                 pipe = pipe->bottom_pipe;
0625             }
0626 
0627             pipe = &context->res_ctx.pipe_ctx[i];
0628             if (num_pipes <= free_pipes) {
0629                 struct dc_stream_state *stream = pipe->stream;
0630                 unsigned int frame_us = (stream->timing.v_total * stream->timing.h_total /
0631                         (double)(stream->timing.pix_clk_100hz * 100)) * 1000000;
0632                 if (frame_us > max_frame_time && !stream->ignore_msa_timing_param) {
0633                     *index = i;
0634                     max_frame_time = frame_us;
0635                     valid_assignment_found = true;
0636                     current_assignment_freesync = false;
0637                 /* For the 2-Freesync display case, still choose the one with the
0638                  * longest frame time
0639                  */
0640                 } else if (stream->ignore_msa_timing_param && (!valid_assignment_found ||
0641                         (current_assignment_freesync && frame_us > max_frame_time))) {
0642                     *index = i;
0643                     valid_assignment_found = true;
0644                     current_assignment_freesync = true;
0645                 }
0646             }
0647         }
0648         pipe_idx++;
0649     }
0650     return valid_assignment_found;
0651 }
0652 
0653 /**
0654  * dcn32_enough_pipes_for_subvp: Function to check if there are "enough" pipes for SubVP.
0655  *
0656  * This function returns true if there are enough free pipes
0657  * to create the required phantom pipes for any given stream
0658  * (that does not already have phantom pipe assigned).
0659  *
0660  * e.g. For a 2 stream config where the first stream uses one
0661  * pipe and the second stream uses 2 pipes (i.e. pipe split),
0662  * this function will return true because there is 1 remaining
0663  * pipe which can be used as the phantom pipe for the non pipe
0664  * split pipe.
0665  *
0666  * @dc: current dc state
0667  * @context: new dc state
0668  *
0669  * Return:
0670  * True if there are enough free pipes to assign phantom pipes to at least one
0671  * stream that does not already have phantom pipes assigned. Otherwise false.
0672  */
0673 static bool dcn32_enough_pipes_for_subvp(struct dc *dc, struct dc_state *context)
0674 {
0675     unsigned int i, split_cnt, free_pipes;
0676     unsigned int min_pipe_split = dc->res_pool->pipe_count + 1; // init as max number of pipes + 1
0677     bool subvp_possible = false;
0678 
0679     for (i = 0; i < dc->res_pool->pipe_count; i++) {
0680         struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
0681 
0682         // Find the minimum pipe split count for non SubVP pipes
0683         if (pipe->stream && !pipe->top_pipe &&
0684             pipe->stream->mall_stream_config.type == SUBVP_NONE) {
0685             split_cnt = 0;
0686             while (pipe) {
0687                 split_cnt++;
0688                 pipe = pipe->bottom_pipe;
0689             }
0690 
0691             if (split_cnt < min_pipe_split)
0692                 min_pipe_split = split_cnt;
0693         }
0694     }
0695 
0696     free_pipes = dcn32_get_num_free_pipes(dc, context);
0697 
0698     // SubVP only possible if at least one pipe is being used (i.e. free_pipes
0699     // should not equal to the pipe_count)
0700     if (free_pipes >= min_pipe_split && free_pipes < dc->res_pool->pipe_count)
0701         subvp_possible = true;
0702 
0703     return subvp_possible;
0704 }
0705 
0706 /**
0707  * subvp_subvp_schedulable: Determine if SubVP + SubVP config is schedulable
0708  *
0709  * High level algorithm:
0710  * 1. Find longest microschedule length (in us) between the two SubVP pipes
0711  * 2. Check if the worst case overlap (VBLANK in middle of ACTIVE) for both
0712  * pipes still allows for the maximum microschedule to fit in the active
0713  * region for both pipes.
0714  *
0715  * @dc: current dc state
0716  * @context: new dc state
0717  *
0718  * Return:
0719  * bool - True if the SubVP + SubVP config is schedulable, false otherwise
0720  */
0721 static bool subvp_subvp_schedulable(struct dc *dc, struct dc_state *context)
0722 {
0723     struct pipe_ctx *subvp_pipes[2];
0724     struct dc_stream_state *phantom = NULL;
0725     uint32_t microschedule_lines = 0;
0726     uint32_t index = 0;
0727     uint32_t i;
0728     uint32_t max_microschedule_us = 0;
0729     int32_t vactive1_us, vactive2_us, vblank1_us, vblank2_us;
0730 
0731     for (i = 0; i < dc->res_pool->pipe_count; i++) {
0732         struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
0733         uint32_t time_us = 0;
0734 
0735         /* Loop to calculate the maximum microschedule time between the two SubVP pipes,
0736          * and also to store the two main SubVP pipe pointers in subvp_pipes[2].
0737          */
0738         if (pipe->stream && pipe->plane_state && !pipe->top_pipe &&
0739             pipe->stream->mall_stream_config.type == SUBVP_MAIN) {
0740             phantom = pipe->stream->mall_stream_config.paired_stream;
0741             microschedule_lines = (phantom->timing.v_total - phantom->timing.v_front_porch) +
0742                     phantom->timing.v_addressable;
0743 
0744             // Round up when calculating microschedule time (+ 1 at the end)
0745             time_us = (microschedule_lines * phantom->timing.h_total) /
0746                     (double)(phantom->timing.pix_clk_100hz * 100) * 1000000 +
0747                         dc->caps.subvp_prefetch_end_to_mall_start_us +
0748                         dc->caps.subvp_fw_processing_delay_us + 1;
0749             if (time_us > max_microschedule_us)
0750                 max_microschedule_us = time_us;
0751 
0752             subvp_pipes[index] = pipe;
0753             index++;
0754 
0755             // Maximum 2 SubVP pipes
0756             if (index == 2)
0757                 break;
0758         }
0759     }
0760     vactive1_us = ((subvp_pipes[0]->stream->timing.v_addressable * subvp_pipes[0]->stream->timing.h_total) /
0761             (double)(subvp_pipes[0]->stream->timing.pix_clk_100hz * 100)) * 1000000;
0762     vactive2_us = ((subvp_pipes[1]->stream->timing.v_addressable * subvp_pipes[1]->stream->timing.h_total) /
0763                 (double)(subvp_pipes[1]->stream->timing.pix_clk_100hz * 100)) * 1000000;
0764     vblank1_us = (((subvp_pipes[0]->stream->timing.v_total - subvp_pipes[0]->stream->timing.v_addressable) *
0765             subvp_pipes[0]->stream->timing.h_total) /
0766             (double)(subvp_pipes[0]->stream->timing.pix_clk_100hz * 100)) * 1000000;
0767     vblank2_us = (((subvp_pipes[1]->stream->timing.v_total - subvp_pipes[1]->stream->timing.v_addressable) *
0768             subvp_pipes[1]->stream->timing.h_total) /
0769             (double)(subvp_pipes[1]->stream->timing.pix_clk_100hz * 100)) * 1000000;
0770 
0771     if ((vactive1_us - vblank2_us) / 2 > max_microschedule_us &&
0772         (vactive2_us - vblank1_us) / 2 > max_microschedule_us)
0773         return true;
0774 
0775     return false;
0776 }
0777 
0778 /**
0779  * subvp_drr_schedulable: Determine if SubVP + DRR config is schedulable
0780  *
0781  * High level algorithm:
0782  * 1. Get timing for SubVP pipe, phantom pipe, and DRR pipe
0783  * 2. Determine the frame time for the DRR display when adding required margin for MCLK switching
0784  * (the margin is equal to the MALL region + DRR margin (500us))
0785  * 3.If (SubVP Active - Prefetch > Stretched DRR frame + max(MALL region, Stretched DRR frame))
0786  * then report the configuration as supported
0787  *
0788  * @dc: current dc state
0789  * @context: new dc state
0790  * @drr_pipe: DRR pipe_ctx for the SubVP + DRR config
0791  *
0792  * Return:
0793  * bool - True if the SubVP + DRR config is schedulable, false otherwise
0794  */
0795 static bool subvp_drr_schedulable(struct dc *dc, struct dc_state *context, struct pipe_ctx *drr_pipe)
0796 {
0797     bool schedulable = false;
0798     uint32_t i;
0799     struct pipe_ctx *pipe = NULL;
0800     struct dc_crtc_timing *main_timing = NULL;
0801     struct dc_crtc_timing *phantom_timing = NULL;
0802     struct dc_crtc_timing *drr_timing = NULL;
0803     int16_t prefetch_us = 0;
0804     int16_t mall_region_us = 0;
0805     int16_t drr_frame_us = 0;   // nominal frame time
0806     int16_t subvp_active_us = 0;
0807     int16_t stretched_drr_us = 0;
0808     int16_t drr_stretched_vblank_us = 0;
0809     int16_t max_vblank_mallregion = 0;
0810 
0811     // Find SubVP pipe
0812     for (i = 0; i < dc->res_pool->pipe_count; i++) {
0813         pipe = &context->res_ctx.pipe_ctx[i];
0814 
0815         // We check for master pipe, but it shouldn't matter since we only need
0816         // the pipe for timing info (stream should be same for any pipe splits)
0817         if (!pipe->stream || !pipe->plane_state || pipe->top_pipe || pipe->prev_odm_pipe)
0818             continue;
0819 
0820         // Find the SubVP pipe
0821         if (pipe->stream->mall_stream_config.type == SUBVP_MAIN)
0822             break;
0823     }
0824 
0825     main_timing = &pipe->stream->timing;
0826     phantom_timing = &pipe->stream->mall_stream_config.paired_stream->timing;
0827     drr_timing = &drr_pipe->stream->timing;
0828     prefetch_us = (phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total /
0829             (double)(phantom_timing->pix_clk_100hz * 100) * 1000000 +
0830             dc->caps.subvp_prefetch_end_to_mall_start_us;
0831     subvp_active_us = main_timing->v_addressable * main_timing->h_total /
0832             (double)(main_timing->pix_clk_100hz * 100) * 1000000;
0833     drr_frame_us = drr_timing->v_total * drr_timing->h_total /
0834             (double)(drr_timing->pix_clk_100hz * 100) * 1000000;
0835     // P-State allow width and FW delays already included phantom_timing->v_addressable
0836     mall_region_us = phantom_timing->v_addressable * phantom_timing->h_total /
0837             (double)(phantom_timing->pix_clk_100hz * 100) * 1000000;
0838     stretched_drr_us = drr_frame_us + mall_region_us + SUBVP_DRR_MARGIN_US;
0839     drr_stretched_vblank_us = (drr_timing->v_total - drr_timing->v_addressable) * drr_timing->h_total /
0840             (double)(drr_timing->pix_clk_100hz * 100) * 1000000 + (stretched_drr_us - drr_frame_us);
0841     max_vblank_mallregion = drr_stretched_vblank_us > mall_region_us ? drr_stretched_vblank_us : mall_region_us;
0842 
0843     /* We consider SubVP + DRR schedulable if the stretched frame duration of the DRR display (i.e. the
0844      * highest refresh rate + margin that can support UCLK P-State switch) passes the static analysis
0845      * for VBLANK: (VACTIVE region of the SubVP pipe can fit the MALL prefetch, VBLANK frame time,
0846      * and the max of (VBLANK blanking time, MALL region)).
0847      */
0848     if (stretched_drr_us < (1 / (double)drr_timing->min_refresh_in_uhz) * 1000000 * 1000000 &&
0849             subvp_active_us - prefetch_us - stretched_drr_us - max_vblank_mallregion > 0)
0850         schedulable = true;
0851 
0852     return schedulable;
0853 }
0854 
0855 
0856 /**
0857  * subvp_vblank_schedulable: Determine if SubVP + VBLANK config is schedulable
0858  *
0859  * High level algorithm:
0860  * 1. Get timing for SubVP pipe, phantom pipe, and VBLANK pipe
0861  * 2. If (SubVP Active - Prefetch > Vblank Frame Time + max(MALL region, Vblank blanking time))
0862  * then report the configuration as supported
0863  * 3. If the VBLANK display is DRR, then take the DRR static schedulability path
0864  *
0865  * @dc: current dc state
0866  * @context: new dc state
0867  *
0868  * Return:
0869  * bool - True if the SubVP + VBLANK/DRR config is schedulable, false otherwise
0870  */
0871 static bool subvp_vblank_schedulable(struct dc *dc, struct dc_state *context)
0872 {
0873     struct pipe_ctx *pipe = NULL;
0874     struct pipe_ctx *subvp_pipe = NULL;
0875     bool found = false;
0876     bool schedulable = false;
0877     uint32_t i = 0;
0878     uint8_t vblank_index = 0;
0879     uint16_t prefetch_us = 0;
0880     uint16_t mall_region_us = 0;
0881     uint16_t vblank_frame_us = 0;
0882     uint16_t subvp_active_us = 0;
0883     uint16_t vblank_blank_us = 0;
0884     uint16_t max_vblank_mallregion = 0;
0885     struct dc_crtc_timing *main_timing = NULL;
0886     struct dc_crtc_timing *phantom_timing = NULL;
0887     struct dc_crtc_timing *vblank_timing = NULL;
0888 
0889     /* For SubVP + VBLANK/DRR cases, we assume there can only be
0890      * a single VBLANK/DRR display. If DML outputs SubVP + VBLANK
0891      * is supported, it is either a single VBLANK case or two VBLANK
0892      * displays which are synchronized (in which case they have identical
0893      * timings).
0894      */
0895     for (i = 0; i < dc->res_pool->pipe_count; i++) {
0896         pipe = &context->res_ctx.pipe_ctx[i];
0897 
0898         // We check for master pipe, but it shouldn't matter since we only need
0899         // the pipe for timing info (stream should be same for any pipe splits)
0900         if (!pipe->stream || !pipe->plane_state || pipe->top_pipe || pipe->prev_odm_pipe)
0901             continue;
0902 
0903         if (!found && pipe->stream->mall_stream_config.type == SUBVP_NONE) {
0904             // Found pipe which is not SubVP or Phantom (i.e. the VBLANK pipe).
0905             vblank_index = i;
0906             found = true;
0907         }
0908 
0909         if (!subvp_pipe && pipe->stream->mall_stream_config.type == SUBVP_MAIN)
0910             subvp_pipe = pipe;
0911     }
0912     // Use ignore_msa_timing_param flag to identify as DRR
0913     if (found && context->res_ctx.pipe_ctx[vblank_index].stream->ignore_msa_timing_param) {
0914         // SUBVP + DRR case
0915         schedulable = subvp_drr_schedulable(dc, context, &context->res_ctx.pipe_ctx[vblank_index]);
0916     } else if (found) {
0917         main_timing = &subvp_pipe->stream->timing;
0918         phantom_timing = &subvp_pipe->stream->mall_stream_config.paired_stream->timing;
0919         vblank_timing = &context->res_ctx.pipe_ctx[vblank_index].stream->timing;
0920         // Prefetch time is equal to VACTIVE + BP + VSYNC of the phantom pipe
0921         // Also include the prefetch end to mallstart delay time
0922         prefetch_us = (phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total /
0923                 (double)(phantom_timing->pix_clk_100hz * 100) * 1000000 +
0924                 dc->caps.subvp_prefetch_end_to_mall_start_us;
0925         // P-State allow width and FW delays already included phantom_timing->v_addressable
0926         mall_region_us = phantom_timing->v_addressable * phantom_timing->h_total /
0927                 (double)(phantom_timing->pix_clk_100hz * 100) * 1000000;
0928         vblank_frame_us = vblank_timing->v_total * vblank_timing->h_total /
0929                 (double)(vblank_timing->pix_clk_100hz * 100) * 1000000;
0930         vblank_blank_us =  (vblank_timing->v_total - vblank_timing->v_addressable) * vblank_timing->h_total /
0931                 (double)(vblank_timing->pix_clk_100hz * 100) * 1000000;
0932         subvp_active_us = main_timing->v_addressable * main_timing->h_total /
0933                 (double)(main_timing->pix_clk_100hz * 100) * 1000000;
0934         max_vblank_mallregion = vblank_blank_us > mall_region_us ? vblank_blank_us : mall_region_us;
0935 
0936         // Schedulable if VACTIVE region of the SubVP pipe can fit the MALL prefetch, VBLANK frame time,
0937         // and the max of (VBLANK blanking time, MALL region)
0938         // TODO: Possibly add some margin (i.e. the below conditions should be [...] > X instead of [...] > 0)
0939         if (subvp_active_us - prefetch_us - vblank_frame_us - max_vblank_mallregion > 0)
0940             schedulable = true;
0941     }
0942     return schedulable;
0943 }
0944 
0945 /**
0946  * subvp_validate_static_schedulability: Check which SubVP case is calculated and handle
0947  * static analysis based on the case.
0948  *
0949  * Three cases:
0950  * 1. SubVP + SubVP
0951  * 2. SubVP + VBLANK (DRR checked internally)
0952  * 3. SubVP + VACTIVE (currently unsupported)
0953  *
0954  * @dc: current dc state
0955  * @context: new dc state
0956  * @vlevel: Voltage level calculated by DML
0957  *
0958  * Return:
0959  * bool - True if statically schedulable, false otherwise
0960  */
0961 static bool subvp_validate_static_schedulability(struct dc *dc,
0962                 struct dc_state *context,
0963                 int vlevel)
0964 {
0965     bool schedulable = true;    // true by default for single display case
0966     struct vba_vars_st *vba = &context->bw_ctx.dml.vba;
0967     uint32_t i, pipe_idx;
0968     uint8_t subvp_count = 0;
0969     uint8_t vactive_count = 0;
0970 
0971     for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
0972         struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
0973 
0974         if (!pipe->stream)
0975             continue;
0976 
0977         if (pipe->plane_state && !pipe->top_pipe &&
0978                 pipe->stream->mall_stream_config.type == SUBVP_MAIN)
0979             subvp_count++;
0980 
0981         // Count how many planes that aren't SubVP/phantom are capable of VACTIVE
0982         // switching (SubVP + VACTIVE unsupported). In situations where we force
0983         // SubVP for a VACTIVE plane, we don't want to increment the vactive_count.
0984         if (vba->ActiveDRAMClockChangeLatencyMargin[vba->pipe_plane[pipe_idx]] > 0 &&
0985             pipe->stream->mall_stream_config.type == SUBVP_NONE) {
0986             vactive_count++;
0987         }
0988         pipe_idx++;
0989     }
0990 
0991     if (subvp_count == 2) {
0992         // Static schedulability check for SubVP + SubVP case
0993         schedulable = subvp_subvp_schedulable(dc, context);
0994     } else if (vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_vblank_w_mall_sub_vp) {
0995         // Static schedulability check for SubVP + VBLANK case. Also handle the case where
0996         // DML outputs SubVP + VBLANK + VACTIVE (DML will report as SubVP + VBLANK)
0997         if (vactive_count > 0)
0998             schedulable = false;
0999         else
1000             schedulable = subvp_vblank_schedulable(dc, context);
1001     } else if (vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_vactive_w_mall_sub_vp &&
1002             vactive_count > 0) {
1003         // For single display SubVP cases, DML will output dm_dram_clock_change_vactive_w_mall_sub_vp by default.
1004         // We tell the difference between SubVP vs. SubVP + VACTIVE by checking the vactive_count.
1005         // SubVP + VACTIVE currently unsupported
1006         schedulable = false;
1007     }
1008     return schedulable;
1009 }
1010 
1011 static void dcn32_full_validate_bw_helper(struct dc *dc,
1012                    struct dc_state *context,
1013                    display_e2e_pipe_params_st *pipes,
1014                    int *vlevel,
1015                    int *split,
1016                    bool *merge,
1017                    int *pipe_cnt)
1018 {
1019     struct vba_vars_st *vba = &context->bw_ctx.dml.vba;
1020     unsigned int dc_pipe_idx = 0;
1021     bool found_supported_config = false;
1022     struct pipe_ctx *pipe = NULL;
1023     uint32_t non_subvp_pipes = 0;
1024     bool drr_pipe_found = false;
1025     uint32_t drr_pipe_index = 0;
1026     uint32_t i = 0;
1027 
1028     dc_assert_fp_enabled();
1029 
1030     /*
1031      * DML favors voltage over p-state, but we're more interested in
1032      * supporting p-state over voltage. We can't support p-state in
1033      * prefetch mode > 0 so try capping the prefetch mode to start.
1034      * Override present for testing.
1035      */
1036     if (dc->debug.dml_disallow_alternate_prefetch_modes)
1037         context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =
1038             dm_prefetch_support_uclk_fclk_and_stutter;
1039     else
1040         context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =
1041             dm_prefetch_support_uclk_fclk_and_stutter_if_possible;
1042 
1043     *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt);
1044     /* This may adjust vlevel and maxMpcComb */
1045     if (*vlevel < context->bw_ctx.dml.soc.num_states)
1046         *vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, *vlevel, split, merge);
1047 
1048     /* Conditions for setting up phantom pipes for SubVP:
1049      * 1. Not force disable SubVP
1050      * 2. Full update (i.e. !fast_validate)
1051      * 3. Enough pipes are available to support SubVP (TODO: Which pipes will use VACTIVE / VBLANK / SUBVP?)
1052      * 4. Display configuration passes validation
1053      * 5. (Config doesn't support MCLK in VACTIVE/VBLANK || dc->debug.force_subvp_mclk_switch)
1054      */
1055     if (!dc->debug.force_disable_subvp && dcn32_all_pipes_have_stream_and_plane(dc, context) &&
1056         !dcn32_mpo_in_use(context) && (*vlevel == context->bw_ctx.dml.soc.num_states ||
1057         vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] == dm_dram_clock_change_unsupported ||
1058         dc->debug.force_subvp_mclk_switch)) {
1059 
1060         dcn32_merge_pipes_for_subvp(dc, context);
1061         // to re-initialize viewport after the pipe merge
1062         for (int i = 0; i < dc->res_pool->pipe_count; i++) {
1063             struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
1064 
1065             if (!pipe_ctx->plane_state || !pipe_ctx->stream)
1066                 continue;
1067 
1068             resource_build_scaling_params(pipe_ctx);
1069         }
1070 
1071         while (!found_supported_config && dcn32_enough_pipes_for_subvp(dc, context) &&
1072             dcn32_assign_subvp_pipe(dc, context, &dc_pipe_idx)) {
1073             /* For the case where *vlevel = num_states, bandwidth validation has failed for this config.
1074              * Adding phantom pipes won't change the validation result, so change the DML input param
1075              * for P-State support before adding phantom pipes and recalculating the DML result.
1076              * However, this case is only applicable for SubVP + DRR cases because the prefetch mode
1077              * will not allow for switch in VBLANK. The DRR display must have it's VBLANK stretched
1078              * enough to support MCLK switching.
1079              */
1080             if (*vlevel == context->bw_ctx.dml.soc.num_states &&
1081                 context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final ==
1082                     dm_prefetch_support_uclk_fclk_and_stutter) {
1083                 context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =
1084                                 dm_prefetch_support_stutter;
1085                 /* There are params (such as FabricClock) that need to be recalculated
1086                  * after validation fails (otherwise it will be 0). Calculation for
1087                  * phantom vactive requires call into DML, so we must ensure all the
1088                  * vba params are valid otherwise we'll get incorrect phantom vactive.
1089                  */
1090                 *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt);
1091             }
1092 
1093             dc->res_pool->funcs->add_phantom_pipes(dc, context, pipes, *pipe_cnt, dc_pipe_idx);
1094 
1095             *pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, false);
1096             // Populate dppclk to trigger a recalculate in dml_get_voltage_level
1097             // so the phantom pipe DLG params can be assigned correctly.
1098             pipes[0].clks_cfg.dppclk_mhz = get_dppclk_calculated(&context->bw_ctx.dml, pipes, *pipe_cnt, 0);
1099             *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt);
1100 
1101             if (*vlevel < context->bw_ctx.dml.soc.num_states &&
1102                 vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] != dm_dram_clock_change_unsupported
1103                 && subvp_validate_static_schedulability(dc, context, *vlevel)) {
1104                 found_supported_config = true;
1105             } else if (*vlevel < context->bw_ctx.dml.soc.num_states &&
1106                     vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] == dm_dram_clock_change_unsupported) {
1107                 /* Case where 1 SubVP is added, and DML reports MCLK unsupported. This handles
1108                  * the case for SubVP + DRR, where the DRR display does not support MCLK switch
1109                  * at it's native refresh rate / timing.
1110                  */
1111                 for (i = 0; i < dc->res_pool->pipe_count; i++) {
1112                     pipe = &context->res_ctx.pipe_ctx[i];
1113                     if (pipe->stream && pipe->plane_state && !pipe->top_pipe &&
1114                         pipe->stream->mall_stream_config.type == SUBVP_NONE) {
1115                         non_subvp_pipes++;
1116                         // Use ignore_msa_timing_param flag to identify as DRR
1117                         if (pipe->stream->ignore_msa_timing_param) {
1118                             drr_pipe_found = true;
1119                             drr_pipe_index = i;
1120                         }
1121                     }
1122                 }
1123                 // If there is only 1 remaining non SubVP pipe that is DRR, check static
1124                 // schedulability for SubVP + DRR.
1125                 if (non_subvp_pipes == 1 && drr_pipe_found) {
1126                     found_supported_config = subvp_drr_schedulable(dc, context,
1127                                                &context->res_ctx.pipe_ctx[drr_pipe_index]);
1128                 }
1129             }
1130         }
1131 
1132         // If SubVP pipe config is unsupported (or cannot be used for UCLK switching)
1133         // remove phantom pipes and repopulate dml pipes
1134         if (!found_supported_config) {
1135             dc->res_pool->funcs->remove_phantom_pipes(dc, context);
1136             vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] = dm_dram_clock_change_unsupported;
1137             *pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, false);
1138         } else {
1139             // only call dcn20_validate_apply_pipe_split_flags if we found a supported config
1140             memset(split, 0, MAX_PIPES * sizeof(int));
1141             memset(merge, 0, MAX_PIPES * sizeof(bool));
1142             *vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, *vlevel, split, merge);
1143 
1144             // Most populate phantom DLG params before programming hardware / timing for phantom pipe
1145             DC_FP_START();
1146             dcn32_helper_populate_phantom_dlg_params(dc, context, pipes, *pipe_cnt);
1147             DC_FP_END();
1148 
1149             // Note: We can't apply the phantom pipes to hardware at this time. We have to wait
1150             // until driver has acquired the DMCUB lock to do it safely.
1151         }
1152     }
1153 }
1154 
1155 static bool is_dtbclk_required(struct dc *dc, struct dc_state *context)
1156 {
1157     int i;
1158 
1159     for (i = 0; i < dc->res_pool->pipe_count; i++) {
1160         if (!context->res_ctx.pipe_ctx[i].stream)
1161             continue;
1162         if (is_dp_128b_132b_signal(&context->res_ctx.pipe_ctx[i]))
1163             return true;
1164     }
1165     return false;
1166 }
1167 
1168 static void dcn32_calculate_dlg_params(struct dc *dc, struct dc_state *context,
1169                        display_e2e_pipe_params_st *pipes,
1170                        int pipe_cnt, int vlevel)
1171 {
1172     int i, pipe_idx;
1173     bool usr_retraining_support = false;
1174     bool unbounded_req_enabled = false;
1175 
1176     dc_assert_fp_enabled();
1177 
1178     /* Writeback MCIF_WB arbitration parameters */
1179     dc->res_pool->funcs->set_mcif_arb_params(dc, context, pipes, pipe_cnt);
1180 
1181     context->bw_ctx.bw.dcn.clk.dispclk_khz = context->bw_ctx.dml.vba.DISPCLK * 1000;
1182     context->bw_ctx.bw.dcn.clk.dcfclk_khz = context->bw_ctx.dml.vba.DCFCLK * 1000;
1183     context->bw_ctx.bw.dcn.clk.socclk_khz = context->bw_ctx.dml.vba.SOCCLK * 1000;
1184     context->bw_ctx.bw.dcn.clk.dramclk_khz = context->bw_ctx.dml.vba.DRAMSpeed * 1000 / 16;
1185     context->bw_ctx.bw.dcn.clk.dcfclk_deep_sleep_khz = context->bw_ctx.dml.vba.DCFCLKDeepSleep * 1000;
1186     context->bw_ctx.bw.dcn.clk.fclk_khz = context->bw_ctx.dml.vba.FabricClock * 1000;
1187     context->bw_ctx.bw.dcn.clk.p_state_change_support =
1188             context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb]
1189                     != dm_dram_clock_change_unsupported;
1190     context->bw_ctx.bw.dcn.clk.num_ways = dcn32_helper_calculate_num_ways_for_subvp(dc, context);
1191 
1192     context->bw_ctx.bw.dcn.clk.dppclk_khz = 0;
1193     context->bw_ctx.bw.dcn.clk.dtbclk_en = is_dtbclk_required(dc, context);
1194     context->bw_ctx.bw.dcn.clk.ref_dtbclk_khz = context->bw_ctx.dml.vba.DTBCLKPerState[vlevel] * 1000;
1195     if (context->bw_ctx.dml.vba.FCLKChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] == dm_fclock_change_unsupported)
1196         context->bw_ctx.bw.dcn.clk.fclk_p_state_change_support = false;
1197     else
1198         context->bw_ctx.bw.dcn.clk.fclk_p_state_change_support = true;
1199 
1200     usr_retraining_support = context->bw_ctx.dml.vba.USRRetrainingSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb];
1201     ASSERT(usr_retraining_support);
1202 
1203     if (context->bw_ctx.bw.dcn.clk.dispclk_khz < dc->debug.min_disp_clk_khz)
1204         context->bw_ctx.bw.dcn.clk.dispclk_khz = dc->debug.min_disp_clk_khz;
1205 
1206     unbounded_req_enabled = get_unbounded_request_enabled(&context->bw_ctx.dml, pipes, pipe_cnt);
1207 
1208     if (unbounded_req_enabled && pipe_cnt > 1) {
1209         // Unbounded requesting should not ever be used when more than 1 pipe is enabled.
1210         ASSERT(false);
1211         unbounded_req_enabled = false;
1212     }
1213 
1214     for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
1215         if (!context->res_ctx.pipe_ctx[i].stream)
1216             continue;
1217         pipes[pipe_idx].pipe.dest.vstartup_start = get_vstartup(&context->bw_ctx.dml, pipes, pipe_cnt,
1218                 pipe_idx);
1219         pipes[pipe_idx].pipe.dest.vupdate_offset = get_vupdate_offset(&context->bw_ctx.dml, pipes, pipe_cnt,
1220                 pipe_idx);
1221         pipes[pipe_idx].pipe.dest.vupdate_width = get_vupdate_width(&context->bw_ctx.dml, pipes, pipe_cnt,
1222                 pipe_idx);
1223         pipes[pipe_idx].pipe.dest.vready_offset = get_vready_offset(&context->bw_ctx.dml, pipes, pipe_cnt,
1224                 pipe_idx);
1225 
1226         if (context->res_ctx.pipe_ctx[i].stream->mall_stream_config.type == SUBVP_PHANTOM) {
1227             // Phantom pipe requires that DET_SIZE = 0 and no unbounded requests
1228             context->res_ctx.pipe_ctx[i].det_buffer_size_kb = 0;
1229             context->res_ctx.pipe_ctx[i].unbounded_req = false;
1230         } else {
1231             context->res_ctx.pipe_ctx[i].det_buffer_size_kb = get_det_buffer_size_kbytes(&context->bw_ctx.dml, pipes, pipe_cnt,
1232                             pipe_idx);
1233             context->res_ctx.pipe_ctx[i].unbounded_req = unbounded_req_enabled;
1234         }
1235 
1236         if (context->bw_ctx.bw.dcn.clk.dppclk_khz < pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000)
1237             context->bw_ctx.bw.dcn.clk.dppclk_khz = pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000;
1238         context->res_ctx.pipe_ctx[i].plane_res.bw.dppclk_khz = pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000;
1239         context->res_ctx.pipe_ctx[i].pipe_dlg_param = pipes[pipe_idx].pipe.dest;
1240         pipe_idx++;
1241     }
1242     /*save a original dppclock copy*/
1243     context->bw_ctx.bw.dcn.clk.bw_dppclk_khz = context->bw_ctx.bw.dcn.clk.dppclk_khz;
1244     context->bw_ctx.bw.dcn.clk.bw_dispclk_khz = context->bw_ctx.bw.dcn.clk.dispclk_khz;
1245     context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz = context->bw_ctx.dml.soc.clock_limits[vlevel].dppclk_mhz
1246             * 1000;
1247     context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz = context->bw_ctx.dml.soc.clock_limits[vlevel].dispclk_mhz
1248             * 1000;
1249 
1250     context->bw_ctx.bw.dcn.compbuf_size_kb = context->bw_ctx.dml.ip.config_return_buffer_size_in_kbytes;
1251 
1252     for (i = 0; i < dc->res_pool->pipe_count; i++) {
1253         if (context->res_ctx.pipe_ctx[i].stream)
1254             context->bw_ctx.bw.dcn.compbuf_size_kb -= context->res_ctx.pipe_ctx[i].det_buffer_size_kb;
1255     }
1256 
1257     for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
1258 
1259         if (!context->res_ctx.pipe_ctx[i].stream)
1260             continue;
1261 
1262         context->bw_ctx.dml.funcs.rq_dlg_get_dlg_reg_v2(&context->bw_ctx.dml,
1263                 &context->res_ctx.pipe_ctx[i].dlg_regs, &context->res_ctx.pipe_ctx[i].ttu_regs, pipes,
1264                 pipe_cnt, pipe_idx);
1265 
1266         context->bw_ctx.dml.funcs.rq_dlg_get_rq_reg_v2(&context->res_ctx.pipe_ctx[i].rq_regs,
1267                 &context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);
1268         pipe_idx++;
1269     }
1270 }
1271 
1272 static struct pipe_ctx *dcn32_find_split_pipe(
1273         struct dc *dc,
1274         struct dc_state *context,
1275         int old_index)
1276 {
1277     struct pipe_ctx *pipe = NULL;
1278     int i;
1279 
1280     if (old_index >= 0 && context->res_ctx.pipe_ctx[old_index].stream == NULL) {
1281         pipe = &context->res_ctx.pipe_ctx[old_index];
1282         pipe->pipe_idx = old_index;
1283     }
1284 
1285     if (!pipe)
1286         for (i = dc->res_pool->pipe_count - 1; i >= 0; i--) {
1287             if (dc->current_state->res_ctx.pipe_ctx[i].top_pipe == NULL
1288                     && dc->current_state->res_ctx.pipe_ctx[i].prev_odm_pipe == NULL) {
1289                 if (context->res_ctx.pipe_ctx[i].stream == NULL) {
1290                     pipe = &context->res_ctx.pipe_ctx[i];
1291                     pipe->pipe_idx = i;
1292                     break;
1293                 }
1294             }
1295         }
1296 
1297     /*
1298      * May need to fix pipes getting tossed from 1 opp to another on flip
1299      * Add for debugging transient underflow during topology updates:
1300      * ASSERT(pipe);
1301      */
1302     if (!pipe)
1303         for (i = dc->res_pool->pipe_count - 1; i >= 0; i--) {
1304             if (context->res_ctx.pipe_ctx[i].stream == NULL) {
1305                 pipe = &context->res_ctx.pipe_ctx[i];
1306                 pipe->pipe_idx = i;
1307                 break;
1308             }
1309         }
1310 
1311     return pipe;
1312 }
1313 
1314 static bool dcn32_split_stream_for_mpc_or_odm(
1315         const struct dc *dc,
1316         struct resource_context *res_ctx,
1317         struct pipe_ctx *pri_pipe,
1318         struct pipe_ctx *sec_pipe,
1319         bool odm)
1320 {
1321     int pipe_idx = sec_pipe->pipe_idx;
1322     const struct resource_pool *pool = dc->res_pool;
1323 
1324     DC_LOGGER_INIT(dc->ctx->logger);
1325 
1326     if (odm && pri_pipe->plane_state) {
1327         /* ODM + window MPO, where MPO window is on left half only */
1328         if (pri_pipe->plane_state->clip_rect.x + pri_pipe->plane_state->clip_rect.width <=
1329                 pri_pipe->stream->src.x + pri_pipe->stream->src.width/2) {
1330 
1331             DC_LOG_SCALER("%s - ODM + window MPO(left). pri_pipe:%d\n",
1332                     __func__,
1333                     pri_pipe->pipe_idx);
1334             return true;
1335         }
1336 
1337         /* ODM + window MPO, where MPO window is on right half only */
1338         if (pri_pipe->plane_state->clip_rect.x >= pri_pipe->stream->src.x +  pri_pipe->stream->src.width/2) {
1339 
1340             DC_LOG_SCALER("%s - ODM + window MPO(right). pri_pipe:%d\n",
1341                     __func__,
1342                     pri_pipe->pipe_idx);
1343             return true;
1344         }
1345     }
1346 
1347     *sec_pipe = *pri_pipe;
1348 
1349     sec_pipe->pipe_idx = pipe_idx;
1350     sec_pipe->plane_res.mi = pool->mis[pipe_idx];
1351     sec_pipe->plane_res.hubp = pool->hubps[pipe_idx];
1352     sec_pipe->plane_res.ipp = pool->ipps[pipe_idx];
1353     sec_pipe->plane_res.xfm = pool->transforms[pipe_idx];
1354     sec_pipe->plane_res.dpp = pool->dpps[pipe_idx];
1355     sec_pipe->plane_res.mpcc_inst = pool->dpps[pipe_idx]->inst;
1356     sec_pipe->stream_res.dsc = NULL;
1357     if (odm) {
1358         if (pri_pipe->next_odm_pipe) {
1359             ASSERT(pri_pipe->next_odm_pipe != sec_pipe);
1360             sec_pipe->next_odm_pipe = pri_pipe->next_odm_pipe;
1361             sec_pipe->next_odm_pipe->prev_odm_pipe = sec_pipe;
1362         }
1363         if (pri_pipe->top_pipe && pri_pipe->top_pipe->next_odm_pipe) {
1364             pri_pipe->top_pipe->next_odm_pipe->bottom_pipe = sec_pipe;
1365             sec_pipe->top_pipe = pri_pipe->top_pipe->next_odm_pipe;
1366         }
1367         if (pri_pipe->bottom_pipe && pri_pipe->bottom_pipe->next_odm_pipe) {
1368             pri_pipe->bottom_pipe->next_odm_pipe->top_pipe = sec_pipe;
1369             sec_pipe->bottom_pipe = pri_pipe->bottom_pipe->next_odm_pipe;
1370         }
1371         pri_pipe->next_odm_pipe = sec_pipe;
1372         sec_pipe->prev_odm_pipe = pri_pipe;
1373         ASSERT(sec_pipe->top_pipe == NULL);
1374 
1375         if (!sec_pipe->top_pipe)
1376             sec_pipe->stream_res.opp = pool->opps[pipe_idx];
1377         else
1378             sec_pipe->stream_res.opp = sec_pipe->top_pipe->stream_res.opp;
1379         if (sec_pipe->stream->timing.flags.DSC == 1) {
1380             dcn20_acquire_dsc(dc, res_ctx, &sec_pipe->stream_res.dsc, pipe_idx);
1381             ASSERT(sec_pipe->stream_res.dsc);
1382             if (sec_pipe->stream_res.dsc == NULL)
1383                 return false;
1384         }
1385     } else {
1386         if (pri_pipe->bottom_pipe) {
1387             ASSERT(pri_pipe->bottom_pipe != sec_pipe);
1388             sec_pipe->bottom_pipe = pri_pipe->bottom_pipe;
1389             sec_pipe->bottom_pipe->top_pipe = sec_pipe;
1390         }
1391         pri_pipe->bottom_pipe = sec_pipe;
1392         sec_pipe->top_pipe = pri_pipe;
1393 
1394         ASSERT(pri_pipe->plane_state);
1395     }
1396 
1397     return true;
1398 }
1399 
1400 bool dcn32_internal_validate_bw(struct dc *dc,
1401                 struct dc_state *context,
1402                 display_e2e_pipe_params_st *pipes,
1403                 int *pipe_cnt_out,
1404                 int *vlevel_out,
1405                 bool fast_validate)
1406 {
1407     bool out = false;
1408     bool repopulate_pipes = false;
1409     int split[MAX_PIPES] = { 0 };
1410     bool merge[MAX_PIPES] = { false };
1411     bool newly_split[MAX_PIPES] = { false };
1412     int pipe_cnt, i, pipe_idx;
1413     int vlevel = context->bw_ctx.dml.soc.num_states;
1414     struct vba_vars_st *vba = &context->bw_ctx.dml.vba;
1415 
1416     dc_assert_fp_enabled();
1417 
1418     ASSERT(pipes);
1419     if (!pipes)
1420         return false;
1421 
1422     // For each full update, remove all existing phantom pipes first
1423     dc->res_pool->funcs->remove_phantom_pipes(dc, context);
1424 
1425     dc->res_pool->funcs->update_soc_for_wm_a(dc, context);
1426 
1427     pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, fast_validate);
1428 
1429     if (!pipe_cnt) {
1430         out = true;
1431         goto validate_out;
1432     }
1433 
1434     dml_log_pipe_params(&context->bw_ctx.dml, pipes, pipe_cnt);
1435 
1436     if (!fast_validate) {
1437         DC_FP_START();
1438         dcn32_full_validate_bw_helper(dc, context, pipes, &vlevel, split, merge, &pipe_cnt);
1439         DC_FP_END();
1440     }
1441 
1442     if (fast_validate ||
1443             (dc->debug.dml_disallow_alternate_prefetch_modes &&
1444             (vlevel == context->bw_ctx.dml.soc.num_states ||
1445                 vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_unsupported))) {
1446         /*
1447          * If dml_disallow_alternate_prefetch_modes is false, then we have already
1448          * tried alternate prefetch modes during full validation.
1449          *
1450          * If mode is unsupported or there is no p-state support, then
1451          * fall back to favouring voltage.
1452          *
1453          * If Prefetch mode 0 failed for this config, or passed with Max UCLK, then try
1454          * to support with Prefetch mode 1 (dm_prefetch_support_fclk_and_stutter == 2)
1455          */
1456         context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =
1457             dm_prefetch_support_fclk_and_stutter;
1458 
1459         vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, pipe_cnt);
1460 
1461         /* Last attempt with Prefetch mode 2 (dm_prefetch_support_stutter == 3) */
1462         if (vlevel == context->bw_ctx.dml.soc.num_states) {
1463             context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =
1464                 dm_prefetch_support_stutter;
1465             vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, pipe_cnt);
1466         }
1467 
1468         if (vlevel < context->bw_ctx.dml.soc.num_states) {
1469             memset(split, 0, sizeof(split));
1470             memset(merge, 0, sizeof(merge));
1471             vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, vlevel, split, merge);
1472         }
1473     }
1474 
1475     dml_log_mode_support_params(&context->bw_ctx.dml);
1476 
1477     if (vlevel == context->bw_ctx.dml.soc.num_states)
1478         goto validate_fail;
1479 
1480     for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
1481         struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
1482         struct pipe_ctx *mpo_pipe = pipe->bottom_pipe;
1483 
1484         if (!pipe->stream)
1485             continue;
1486 
1487         if (vba->ODMCombineEnabled[vba->pipe_plane[pipe_idx]] != dm_odm_combine_mode_disabled
1488                 && !dc->config.enable_windowed_mpo_odm
1489                 && pipe->plane_state && mpo_pipe
1490                 && memcmp(&mpo_pipe->plane_res.scl_data.recout,
1491                         &pipe->plane_res.scl_data.recout,
1492                         sizeof(struct rect)) != 0) {
1493             ASSERT(mpo_pipe->plane_state != pipe->plane_state);
1494             goto validate_fail;
1495         }
1496         pipe_idx++;
1497     }
1498 
1499     /* merge pipes if necessary */
1500     for (i = 0; i < dc->res_pool->pipe_count; i++) {
1501         struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
1502 
1503         /*skip pipes that don't need merging*/
1504         if (!merge[i])
1505             continue;
1506 
1507         /* if ODM merge we ignore mpc tree, mpo pipes will have their own flags */
1508         if (pipe->prev_odm_pipe) {
1509             /*split off odm pipe*/
1510             pipe->prev_odm_pipe->next_odm_pipe = pipe->next_odm_pipe;
1511             if (pipe->next_odm_pipe)
1512                 pipe->next_odm_pipe->prev_odm_pipe = pipe->prev_odm_pipe;
1513 
1514             pipe->bottom_pipe = NULL;
1515             pipe->next_odm_pipe = NULL;
1516             pipe->plane_state = NULL;
1517             pipe->stream = NULL;
1518             pipe->top_pipe = NULL;
1519             pipe->prev_odm_pipe = NULL;
1520             if (pipe->stream_res.dsc)
1521                 dcn20_release_dsc(&context->res_ctx, dc->res_pool, &pipe->stream_res.dsc);
1522             memset(&pipe->plane_res, 0, sizeof(pipe->plane_res));
1523             memset(&pipe->stream_res, 0, sizeof(pipe->stream_res));
1524             repopulate_pipes = true;
1525         } else if (pipe->top_pipe && pipe->top_pipe->plane_state == pipe->plane_state) {
1526             struct pipe_ctx *top_pipe = pipe->top_pipe;
1527             struct pipe_ctx *bottom_pipe = pipe->bottom_pipe;
1528 
1529             top_pipe->bottom_pipe = bottom_pipe;
1530             if (bottom_pipe)
1531                 bottom_pipe->top_pipe = top_pipe;
1532 
1533             pipe->top_pipe = NULL;
1534             pipe->bottom_pipe = NULL;
1535             pipe->plane_state = NULL;
1536             pipe->stream = NULL;
1537             memset(&pipe->plane_res, 0, sizeof(pipe->plane_res));
1538             memset(&pipe->stream_res, 0, sizeof(pipe->stream_res));
1539             repopulate_pipes = true;
1540         } else
1541             ASSERT(0); /* Should never try to merge master pipe */
1542 
1543     }
1544 
1545     for (i = 0, pipe_idx = -1; i < dc->res_pool->pipe_count; i++) {
1546         struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
1547         struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i];
1548         struct pipe_ctx *hsplit_pipe = NULL;
1549         bool odm;
1550         int old_index = -1;
1551 
1552         if (!pipe->stream || newly_split[i])
1553             continue;
1554 
1555         pipe_idx++;
1556         odm = vba->ODMCombineEnabled[vba->pipe_plane[pipe_idx]] != dm_odm_combine_mode_disabled;
1557 
1558         if (!pipe->plane_state && !odm)
1559             continue;
1560 
1561         if (split[i]) {
1562             if (odm) {
1563                 if (split[i] == 4 && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe)
1564                     old_index = old_pipe->next_odm_pipe->next_odm_pipe->pipe_idx;
1565                 else if (old_pipe->next_odm_pipe)
1566                     old_index = old_pipe->next_odm_pipe->pipe_idx;
1567             } else {
1568                 if (split[i] == 4 && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe &&
1569                         old_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state)
1570                     old_index = old_pipe->bottom_pipe->bottom_pipe->pipe_idx;
1571                 else if (old_pipe->bottom_pipe &&
1572                         old_pipe->bottom_pipe->plane_state == old_pipe->plane_state)
1573                     old_index = old_pipe->bottom_pipe->pipe_idx;
1574             }
1575             hsplit_pipe = dcn32_find_split_pipe(dc, context, old_index);
1576             ASSERT(hsplit_pipe);
1577             if (!hsplit_pipe)
1578                 goto validate_fail;
1579 
1580             if (!dcn32_split_stream_for_mpc_or_odm(
1581                     dc, &context->res_ctx,
1582                     pipe, hsplit_pipe, odm))
1583                 goto validate_fail;
1584 
1585             newly_split[hsplit_pipe->pipe_idx] = true;
1586             repopulate_pipes = true;
1587         }
1588         if (split[i] == 4) {
1589             struct pipe_ctx *pipe_4to1;
1590 
1591             if (odm && old_pipe->next_odm_pipe)
1592                 old_index = old_pipe->next_odm_pipe->pipe_idx;
1593             else if (!odm && old_pipe->bottom_pipe &&
1594                         old_pipe->bottom_pipe->plane_state == old_pipe->plane_state)
1595                 old_index = old_pipe->bottom_pipe->pipe_idx;
1596             else
1597                 old_index = -1;
1598             pipe_4to1 = dcn32_find_split_pipe(dc, context, old_index);
1599             ASSERT(pipe_4to1);
1600             if (!pipe_4to1)
1601                 goto validate_fail;
1602             if (!dcn32_split_stream_for_mpc_or_odm(
1603                     dc, &context->res_ctx,
1604                     pipe, pipe_4to1, odm))
1605                 goto validate_fail;
1606             newly_split[pipe_4to1->pipe_idx] = true;
1607 
1608             if (odm && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe
1609                     && old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe)
1610                 old_index = old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe->pipe_idx;
1611             else if (!odm && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe &&
1612                     old_pipe->bottom_pipe->bottom_pipe->bottom_pipe &&
1613                     old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state)
1614                 old_index = old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->pipe_idx;
1615             else
1616                 old_index = -1;
1617             pipe_4to1 = dcn32_find_split_pipe(dc, context, old_index);
1618             ASSERT(pipe_4to1);
1619             if (!pipe_4to1)
1620                 goto validate_fail;
1621             if (!dcn32_split_stream_for_mpc_or_odm(
1622                     dc, &context->res_ctx,
1623                     hsplit_pipe, pipe_4to1, odm))
1624                 goto validate_fail;
1625             newly_split[pipe_4to1->pipe_idx] = true;
1626         }
1627         if (odm)
1628             dcn20_build_mapped_resource(dc, context, pipe->stream);
1629     }
1630 
1631     for (i = 0; i < dc->res_pool->pipe_count; i++) {
1632         struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
1633 
1634         if (pipe->plane_state) {
1635             if (!resource_build_scaling_params(pipe))
1636                 goto validate_fail;
1637         }
1638     }
1639 
1640     /* Actual dsc count per stream dsc validation*/
1641     if (!dcn20_validate_dsc(dc, context)) {
1642         vba->ValidationStatus[vba->soc.num_states] = DML_FAIL_DSC_VALIDATION_FAILURE;
1643         goto validate_fail;
1644     }
1645 
1646     if (repopulate_pipes)
1647         pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, fast_validate);
1648     *vlevel_out = vlevel;
1649     *pipe_cnt_out = pipe_cnt;
1650 
1651     out = true;
1652     goto validate_out;
1653 
1654 validate_fail:
1655     out = false;
1656 
1657 validate_out:
1658     return out;
1659 }
1660 
1661 
1662 void dcn32_calculate_wm_and_dlg_fpu(struct dc *dc, struct dc_state *context,
1663                 display_e2e_pipe_params_st *pipes,
1664                 int pipe_cnt,
1665                 int vlevel)
1666 {
1667     int i, pipe_idx, vlevel_temp = 0;
1668     double dcfclk = dcn3_2_soc.clock_limits[0].dcfclk_mhz;
1669     double dcfclk_from_validation = context->bw_ctx.dml.vba.DCFCLKState[vlevel][context->bw_ctx.dml.vba.maxMpcComb];
1670     bool pstate_en = context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] !=
1671             dm_dram_clock_change_unsupported;
1672     unsigned int dummy_latency_index = 0;
1673     int maxMpcComb = context->bw_ctx.dml.vba.maxMpcComb;
1674     unsigned int min_dram_speed_mts = context->bw_ctx.dml.vba.DRAMSpeed;
1675     unsigned int min_dram_speed_mts_margin;
1676 
1677     dc_assert_fp_enabled();
1678 
1679     // Override DRAMClockChangeSupport for SubVP + DRR case where the DRR cannot switch without stretching it's VBLANK
1680     if (!pstate_en && dcn32_subvp_in_use(dc, context)) {
1681         context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] = dm_dram_clock_change_vblank_w_mall_sub_vp;
1682         pstate_en = true;
1683     }
1684 
1685     context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching = false;
1686 
1687     if (!pstate_en) {
1688         /* only when the mclk switch can not be natural, is the fw based vblank stretch attempted */
1689         context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching =
1690             dcn30_can_support_mclk_switch_using_fw_based_vblank_stretch(dc, context);
1691 
1692         if (context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching) {
1693             dummy_latency_index = dcn32_find_dummy_latency_index_for_fw_based_mclk_switch(dc,
1694                 context, pipes, pipe_cnt, vlevel);
1695 
1696             /* After calling dcn30_find_dummy_latency_index_for_fw_based_mclk_switch
1697              * we reinstate the original dram_clock_change_latency_us on the context
1698              * and all variables that may have changed up to this point, except the
1699              * newly found dummy_latency_index
1700              */
1701             context->bw_ctx.dml.soc.dram_clock_change_latency_us =
1702                     dc->clk_mgr->bw_params->wm_table.nv_entries[WM_A].dml_input.pstate_latency_us;
1703             dcn32_internal_validate_bw(dc, context, pipes, &pipe_cnt, &vlevel, false);
1704             maxMpcComb = context->bw_ctx.dml.vba.maxMpcComb;
1705             dcfclk = context->bw_ctx.dml.vba.DCFCLKState[vlevel][context->bw_ctx.dml.vba.maxMpcComb];
1706             pstate_en = context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][maxMpcComb] !=
1707                     dm_dram_clock_change_unsupported;
1708         }
1709     }
1710 
1711     /* Set B:
1712      * For Set B calculations use clocks from clock_limits[2] when available i.e. when SMU is present,
1713      * otherwise use arbitrary low value from spreadsheet for DCFCLK as lower is safer for watermark
1714      * calculations to cover bootup clocks.
1715      * DCFCLK: soc.clock_limits[2] when available
1716      * UCLK: soc.clock_limits[2] when available
1717      */
1718     if (dcn3_2_soc.num_states > 2) {
1719         vlevel_temp = 2;
1720         dcfclk = dcn3_2_soc.clock_limits[2].dcfclk_mhz;
1721     } else
1722         dcfclk = 615; //DCFCLK Vmin_lv
1723 
1724     pipes[0].clks_cfg.voltage = vlevel_temp;
1725     pipes[0].clks_cfg.dcfclk_mhz = dcfclk;
1726     pipes[0].clks_cfg.socclk_mhz = context->bw_ctx.dml.soc.clock_limits[vlevel_temp].socclk_mhz;
1727 
1728     if (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_B].valid) {
1729         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;
1730         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;
1731         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;
1732         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;
1733     }
1734     context->bw_ctx.bw.dcn.watermarks.b.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1735     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;
1736     context->bw_ctx.bw.dcn.watermarks.b.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1737     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;
1738     context->bw_ctx.bw.dcn.watermarks.b.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1739     context->bw_ctx.bw.dcn.watermarks.b.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1740     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;
1741     context->bw_ctx.bw.dcn.watermarks.b.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1742     context->bw_ctx.bw.dcn.watermarks.b.cstate_pstate.fclk_pstate_change_ns = get_fclk_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1743     context->bw_ctx.bw.dcn.watermarks.b.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1744 
1745     /* Set D:
1746      * All clocks min.
1747      * DCFCLK: Min, as reported by PM FW when available
1748      * UCLK  : Min, as reported by PM FW when available
1749      * sr_enter_exit/sr_exit should be lower than used for DRAM (TBD after bringup or later, use as decided in Clk Mgr)
1750      */
1751 
1752     if (dcn3_2_soc.num_states > 2) {
1753         vlevel_temp = 0;
1754         dcfclk = dc->clk_mgr->bw_params->clk_table.entries[0].dcfclk_mhz;
1755     } else
1756         dcfclk = 615; //DCFCLK Vmin_lv
1757 
1758     pipes[0].clks_cfg.voltage = vlevel_temp;
1759     pipes[0].clks_cfg.dcfclk_mhz = dcfclk;
1760     pipes[0].clks_cfg.socclk_mhz = context->bw_ctx.dml.soc.clock_limits[vlevel_temp].socclk_mhz;
1761 
1762     if (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_D].valid) {
1763         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;
1764         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;
1765         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;
1766         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;
1767     }
1768     context->bw_ctx.bw.dcn.watermarks.d.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1769     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;
1770     context->bw_ctx.bw.dcn.watermarks.d.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1771     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;
1772     context->bw_ctx.bw.dcn.watermarks.d.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1773     context->bw_ctx.bw.dcn.watermarks.d.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1774     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;
1775     context->bw_ctx.bw.dcn.watermarks.d.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1776     context->bw_ctx.bw.dcn.watermarks.d.cstate_pstate.fclk_pstate_change_ns = get_fclk_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1777     context->bw_ctx.bw.dcn.watermarks.d.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1778 
1779     /* Set C, for Dummy P-State:
1780      * All clocks min.
1781      * DCFCLK: Min, as reported by PM FW, when available
1782      * UCLK  : Min,  as reported by PM FW, when available
1783      * pstate latency as per UCLK state dummy pstate latency
1784      */
1785 
1786     // For Set A and Set C use values from validation
1787     pipes[0].clks_cfg.voltage = vlevel;
1788     pipes[0].clks_cfg.dcfclk_mhz = dcfclk_from_validation;
1789     pipes[0].clks_cfg.socclk_mhz = context->bw_ctx.dml.soc.clock_limits[vlevel].socclk_mhz;
1790 
1791     if (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_C].valid) {
1792         min_dram_speed_mts = context->bw_ctx.dml.vba.DRAMSpeed;
1793         min_dram_speed_mts_margin = 160;
1794 
1795         context->bw_ctx.dml.soc.dram_clock_change_latency_us =
1796             dc->clk_mgr->bw_params->dummy_pstate_table[0].dummy_pstate_latency_us;
1797 
1798         if (context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][maxMpcComb] ==
1799             dm_dram_clock_change_unsupported) {
1800             int min_dram_speed_mts_offset = dc->clk_mgr->bw_params->clk_table.num_entries - 1;
1801 
1802             min_dram_speed_mts =
1803                 dc->clk_mgr->bw_params->clk_table.entries[min_dram_speed_mts_offset].memclk_mhz * 16;
1804         }
1805 
1806         if (!context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching) {
1807             /* find largest table entry that is lower than dram speed,
1808              * but lower than DPM0 still uses DPM0
1809              */
1810             for (dummy_latency_index = 3; dummy_latency_index > 0; dummy_latency_index--)
1811                 if (min_dram_speed_mts + min_dram_speed_mts_margin >
1812                     dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dram_speed_mts)
1813                     break;
1814         }
1815 
1816         context->bw_ctx.dml.soc.dram_clock_change_latency_us =
1817             dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dummy_pstate_latency_us;
1818 
1819         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;
1820         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;
1821         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;
1822     }
1823 
1824     context->bw_ctx.bw.dcn.watermarks.c.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1825     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;
1826     context->bw_ctx.bw.dcn.watermarks.c.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1827     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;
1828     context->bw_ctx.bw.dcn.watermarks.c.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1829     context->bw_ctx.bw.dcn.watermarks.c.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1830     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;
1831     context->bw_ctx.bw.dcn.watermarks.c.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1832     context->bw_ctx.bw.dcn.watermarks.c.cstate_pstate.fclk_pstate_change_ns = get_fclk_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1833     context->bw_ctx.bw.dcn.watermarks.c.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1834 
1835     if ((!pstate_en) && (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_C].valid)) {
1836         /* The only difference between A and C is p-state latency, if p-state is not supported
1837          * with full p-state latency we want to calculate DLG based on dummy p-state latency,
1838          * Set A p-state watermark set to 0 on DCN30, when p-state unsupported, for now keep as DCN30.
1839          */
1840         context->bw_ctx.bw.dcn.watermarks.a = context->bw_ctx.bw.dcn.watermarks.c;
1841         context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns = 0;
1842     } else {
1843         /* Set A:
1844          * All clocks min.
1845          * DCFCLK: Min, as reported by PM FW, when available
1846          * UCLK: Min, as reported by PM FW, when available
1847          */
1848         dc->res_pool->funcs->update_soc_for_wm_a(dc, context);
1849         context->bw_ctx.bw.dcn.watermarks.a.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1850         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;
1851         context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1852         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;
1853         context->bw_ctx.bw.dcn.watermarks.a.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1854         context->bw_ctx.bw.dcn.watermarks.a.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1855         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;
1856         context->bw_ctx.bw.dcn.watermarks.a.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1857         context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.fclk_pstate_change_ns = get_fclk_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1858         context->bw_ctx.bw.dcn.watermarks.a.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
1859     }
1860 
1861     for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
1862         if (!context->res_ctx.pipe_ctx[i].stream)
1863             continue;
1864 
1865         pipes[pipe_idx].clks_cfg.dispclk_mhz = get_dispclk_calculated(&context->bw_ctx.dml, pipes, pipe_cnt);
1866         pipes[pipe_idx].clks_cfg.dppclk_mhz = get_dppclk_calculated(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);
1867 
1868         if (dc->config.forced_clocks) {
1869             pipes[pipe_idx].clks_cfg.dispclk_mhz = context->bw_ctx.dml.soc.clock_limits[0].dispclk_mhz;
1870             pipes[pipe_idx].clks_cfg.dppclk_mhz = context->bw_ctx.dml.soc.clock_limits[0].dppclk_mhz;
1871         }
1872         if (dc->debug.min_disp_clk_khz > pipes[pipe_idx].clks_cfg.dispclk_mhz * 1000)
1873             pipes[pipe_idx].clks_cfg.dispclk_mhz = dc->debug.min_disp_clk_khz / 1000.0;
1874         if (dc->debug.min_dpp_clk_khz > pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000)
1875             pipes[pipe_idx].clks_cfg.dppclk_mhz = dc->debug.min_dpp_clk_khz / 1000.0;
1876 
1877         pipe_idx++;
1878     }
1879 
1880     context->perf_params.stutter_period_us = context->bw_ctx.dml.vba.StutterPeriod;
1881 
1882     dcn32_calculate_dlg_params(dc, context, pipes, pipe_cnt, vlevel);
1883 
1884     if (!pstate_en)
1885         /* Restore full p-state latency */
1886         context->bw_ctx.dml.soc.dram_clock_change_latency_us =
1887                 dc->clk_mgr->bw_params->wm_table.nv_entries[WM_A].dml_input.pstate_latency_us;
1888 
1889     if (context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching)
1890         dcn30_setup_mclk_switch_using_fw_based_vblank_stretch(dc, context);
1891 }
1892 
1893 static void dcn32_get_optimal_dcfclk_fclk_for_uclk(unsigned int uclk_mts,
1894         unsigned int *optimal_dcfclk,
1895         unsigned int *optimal_fclk)
1896 {
1897     double bw_from_dram, bw_from_dram1, bw_from_dram2;
1898 
1899     bw_from_dram1 = uclk_mts * dcn3_2_soc.num_chans *
1900         dcn3_2_soc.dram_channel_width_bytes * (dcn3_2_soc.max_avg_dram_bw_use_normal_percent / 100);
1901     bw_from_dram2 = uclk_mts * dcn3_2_soc.num_chans *
1902         dcn3_2_soc.dram_channel_width_bytes * (dcn3_2_soc.max_avg_sdp_bw_use_normal_percent / 100);
1903 
1904     bw_from_dram = (bw_from_dram1 < bw_from_dram2) ? bw_from_dram1 : bw_from_dram2;
1905 
1906     if (optimal_fclk)
1907         *optimal_fclk = bw_from_dram /
1908         (dcn3_2_soc.fabric_datapath_to_dcn_data_return_bytes * (dcn3_2_soc.max_avg_sdp_bw_use_normal_percent / 100));
1909 
1910     if (optimal_dcfclk)
1911         *optimal_dcfclk =  bw_from_dram /
1912         (dcn3_2_soc.return_bus_width_bytes * (dcn3_2_soc.max_avg_sdp_bw_use_normal_percent / 100));
1913 }
1914 
1915 static void remove_entry_from_table_at_index(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries,
1916         unsigned int index)
1917 {
1918     int i;
1919 
1920     if (*num_entries == 0)
1921         return;
1922 
1923     for (i = index; i < *num_entries - 1; i++) {
1924         table[i] = table[i + 1];
1925     }
1926     memset(&table[--(*num_entries)], 0, sizeof(struct _vcs_dpi_voltage_scaling_st));
1927 }
1928 
1929 void dcn32_patch_dpm_table(struct clk_bw_params *bw_params)
1930 {
1931     int i;
1932     unsigned int max_dcfclk_mhz = 0, max_dispclk_mhz = 0, max_dppclk_mhz = 0,
1933             max_phyclk_mhz = 0, max_dtbclk_mhz = 0, max_fclk_mhz = 0, max_uclk_mhz = 0;
1934 
1935     for (i = 0; i < MAX_NUM_DPM_LVL; i++) {
1936         if (bw_params->clk_table.entries[i].dcfclk_mhz > max_dcfclk_mhz)
1937             max_dcfclk_mhz = bw_params->clk_table.entries[i].dcfclk_mhz;
1938         if (bw_params->clk_table.entries[i].fclk_mhz > max_fclk_mhz)
1939             max_fclk_mhz = bw_params->clk_table.entries[i].fclk_mhz;
1940         if (bw_params->clk_table.entries[i].memclk_mhz > max_uclk_mhz)
1941             max_uclk_mhz = bw_params->clk_table.entries[i].memclk_mhz;
1942         if (bw_params->clk_table.entries[i].dispclk_mhz > max_dispclk_mhz)
1943             max_dispclk_mhz = bw_params->clk_table.entries[i].dispclk_mhz;
1944         if (bw_params->clk_table.entries[i].dppclk_mhz > max_dppclk_mhz)
1945             max_dppclk_mhz = bw_params->clk_table.entries[i].dppclk_mhz;
1946         if (bw_params->clk_table.entries[i].phyclk_mhz > max_phyclk_mhz)
1947             max_phyclk_mhz = bw_params->clk_table.entries[i].phyclk_mhz;
1948         if (bw_params->clk_table.entries[i].dtbclk_mhz > max_dtbclk_mhz)
1949             max_dtbclk_mhz = bw_params->clk_table.entries[i].dtbclk_mhz;
1950     }
1951 
1952     /* Scan through clock values we currently have and if they are 0,
1953      *  then populate it with dcn3_2_soc.clock_limits[] value.
1954      *
1955      * Do it for DCFCLK, DISPCLK, DTBCLK and UCLK as any of those being
1956      *  0, will cause it to skip building the clock table.
1957      */
1958     if (max_dcfclk_mhz == 0)
1959         bw_params->clk_table.entries[0].dcfclk_mhz = dcn3_2_soc.clock_limits[0].dcfclk_mhz;
1960     if (max_dispclk_mhz == 0)
1961         bw_params->clk_table.entries[0].dispclk_mhz = dcn3_2_soc.clock_limits[0].dispclk_mhz;
1962     if (max_dtbclk_mhz == 0)
1963         bw_params->clk_table.entries[0].dtbclk_mhz = dcn3_2_soc.clock_limits[0].dtbclk_mhz;
1964     if (max_uclk_mhz == 0)
1965         bw_params->clk_table.entries[0].memclk_mhz = dcn3_2_soc.clock_limits[0].dram_speed_mts / 16;
1966 }
1967 
1968 static int build_synthetic_soc_states(struct clk_bw_params *bw_params,
1969         struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries)
1970 {
1971     int i, j;
1972     struct _vcs_dpi_voltage_scaling_st entry = {0};
1973 
1974     unsigned int max_dcfclk_mhz = 0, max_dispclk_mhz = 0, max_dppclk_mhz = 0,
1975             max_phyclk_mhz = 0, max_dtbclk_mhz = 0, max_fclk_mhz = 0, max_uclk_mhz = 0;
1976 
1977     unsigned int min_dcfclk_mhz = 199, min_fclk_mhz = 299;
1978 
1979     static const unsigned int num_dcfclk_stas = 5;
1980     unsigned int dcfclk_sta_targets[DC__VOLTAGE_STATES] = {199, 615, 906, 1324, 1564};
1981 
1982     unsigned int num_uclk_dpms = 0;
1983     unsigned int num_fclk_dpms = 0;
1984     unsigned int num_dcfclk_dpms = 0;
1985 
1986     for (i = 0; i < MAX_NUM_DPM_LVL; i++) {
1987         if (bw_params->clk_table.entries[i].dcfclk_mhz > max_dcfclk_mhz)
1988             max_dcfclk_mhz = bw_params->clk_table.entries[i].dcfclk_mhz;
1989         if (bw_params->clk_table.entries[i].fclk_mhz > max_fclk_mhz)
1990             max_fclk_mhz = bw_params->clk_table.entries[i].fclk_mhz;
1991         if (bw_params->clk_table.entries[i].memclk_mhz > max_uclk_mhz)
1992             max_uclk_mhz = bw_params->clk_table.entries[i].memclk_mhz;
1993         if (bw_params->clk_table.entries[i].dispclk_mhz > max_dispclk_mhz)
1994             max_dispclk_mhz = bw_params->clk_table.entries[i].dispclk_mhz;
1995         if (bw_params->clk_table.entries[i].dppclk_mhz > max_dppclk_mhz)
1996             max_dppclk_mhz = bw_params->clk_table.entries[i].dppclk_mhz;
1997         if (bw_params->clk_table.entries[i].phyclk_mhz > max_phyclk_mhz)
1998             max_phyclk_mhz = bw_params->clk_table.entries[i].phyclk_mhz;
1999         if (bw_params->clk_table.entries[i].dtbclk_mhz > max_dtbclk_mhz)
2000             max_dtbclk_mhz = bw_params->clk_table.entries[i].dtbclk_mhz;
2001 
2002         if (bw_params->clk_table.entries[i].memclk_mhz > 0)
2003             num_uclk_dpms++;
2004         if (bw_params->clk_table.entries[i].fclk_mhz > 0)
2005             num_fclk_dpms++;
2006         if (bw_params->clk_table.entries[i].dcfclk_mhz > 0)
2007             num_dcfclk_dpms++;
2008     }
2009 
2010     if (!max_dcfclk_mhz || !max_dispclk_mhz || !max_dtbclk_mhz)
2011         return -1;
2012 
2013     if (max_dppclk_mhz == 0)
2014         max_dppclk_mhz = max_dispclk_mhz;
2015 
2016     if (max_fclk_mhz == 0)
2017         max_fclk_mhz = max_dcfclk_mhz * dcn3_2_soc.pct_ideal_sdp_bw_after_urgent / dcn3_2_soc.pct_ideal_fabric_bw_after_urgent;
2018 
2019     if (max_phyclk_mhz == 0)
2020         max_phyclk_mhz = dcn3_2_soc.clock_limits[0].phyclk_mhz;
2021 
2022     *num_entries = 0;
2023     entry.dispclk_mhz = max_dispclk_mhz;
2024     entry.dscclk_mhz = max_dispclk_mhz / 3;
2025     entry.dppclk_mhz = max_dppclk_mhz;
2026     entry.dtbclk_mhz = max_dtbclk_mhz;
2027     entry.phyclk_mhz = max_phyclk_mhz;
2028     entry.phyclk_d18_mhz = dcn3_2_soc.clock_limits[0].phyclk_d18_mhz;
2029     entry.phyclk_d32_mhz = dcn3_2_soc.clock_limits[0].phyclk_d32_mhz;
2030 
2031     // Insert all the DCFCLK STAs
2032     for (i = 0; i < num_dcfclk_stas; i++) {
2033         entry.dcfclk_mhz = dcfclk_sta_targets[i];
2034         entry.fabricclk_mhz = 0;
2035         entry.dram_speed_mts = 0;
2036 
2037         DC_FP_START();
2038         insert_entry_into_table_sorted(table, num_entries, &entry);
2039         DC_FP_END();
2040     }
2041 
2042     // Insert the max DCFCLK
2043     entry.dcfclk_mhz = max_dcfclk_mhz;
2044     entry.fabricclk_mhz = 0;
2045     entry.dram_speed_mts = 0;
2046 
2047     DC_FP_START();
2048     insert_entry_into_table_sorted(table, num_entries, &entry);
2049     DC_FP_END();
2050 
2051     // Insert the UCLK DPMS
2052     for (i = 0; i < num_uclk_dpms; i++) {
2053         entry.dcfclk_mhz = 0;
2054         entry.fabricclk_mhz = 0;
2055         entry.dram_speed_mts = bw_params->clk_table.entries[i].memclk_mhz * 16;
2056 
2057         DC_FP_START();
2058         insert_entry_into_table_sorted(table, num_entries, &entry);
2059         DC_FP_END();
2060     }
2061 
2062     // If FCLK is coarse grained, insert individual DPMs.
2063     if (num_fclk_dpms > 2) {
2064         for (i = 0; i < num_fclk_dpms; i++) {
2065             entry.dcfclk_mhz = 0;
2066             entry.fabricclk_mhz = bw_params->clk_table.entries[i].fclk_mhz;
2067             entry.dram_speed_mts = 0;
2068 
2069             DC_FP_START();
2070             insert_entry_into_table_sorted(table, num_entries, &entry);
2071             DC_FP_END();
2072         }
2073     }
2074     // If FCLK fine grained, only insert max
2075     else {
2076         entry.dcfclk_mhz = 0;
2077         entry.fabricclk_mhz = max_fclk_mhz;
2078         entry.dram_speed_mts = 0;
2079 
2080         DC_FP_START();
2081         insert_entry_into_table_sorted(table, num_entries, &entry);
2082         DC_FP_END();
2083     }
2084 
2085     // At this point, the table contains all "points of interest" based on
2086     // DPMs from PMFW, and STAs.  Table is sorted by BW, and all clock
2087     // ratios (by derate, are exact).
2088 
2089     // Remove states that require higher clocks than are supported
2090     for (i = *num_entries - 1; i >= 0 ; i--) {
2091         if (table[i].dcfclk_mhz > max_dcfclk_mhz ||
2092                 table[i].fabricclk_mhz > max_fclk_mhz ||
2093                 table[i].dram_speed_mts > max_uclk_mhz * 16)
2094             remove_entry_from_table_at_index(table, num_entries, i);
2095     }
2096 
2097     // At this point, the table only contains supported points of interest
2098     // it could be used as is, but some states may be redundant due to
2099     // coarse grained nature of some clocks, so we want to round up to
2100     // coarse grained DPMs and remove duplicates.
2101 
2102     // Round up UCLKs
2103     for (i = *num_entries - 1; i >= 0 ; i--) {
2104         for (j = 0; j < num_uclk_dpms; j++) {
2105             if (bw_params->clk_table.entries[j].memclk_mhz * 16 >= table[i].dram_speed_mts) {
2106                 table[i].dram_speed_mts = bw_params->clk_table.entries[j].memclk_mhz * 16;
2107                 break;
2108             }
2109         }
2110     }
2111 
2112     // If FCLK is coarse grained, round up to next DPMs
2113     if (num_fclk_dpms > 2) {
2114         for (i = *num_entries - 1; i >= 0 ; i--) {
2115             for (j = 0; j < num_fclk_dpms; j++) {
2116                 if (bw_params->clk_table.entries[j].fclk_mhz >= table[i].fabricclk_mhz) {
2117                     table[i].fabricclk_mhz = bw_params->clk_table.entries[j].fclk_mhz;
2118                     break;
2119                 }
2120             }
2121         }
2122     }
2123     // Otherwise, round up to minimum.
2124     else {
2125         for (i = *num_entries - 1; i >= 0 ; i--) {
2126             if (table[i].fabricclk_mhz < min_fclk_mhz) {
2127                 table[i].fabricclk_mhz = min_fclk_mhz;
2128                 break;
2129             }
2130         }
2131     }
2132 
2133     // Round DCFCLKs up to minimum
2134     for (i = *num_entries - 1; i >= 0 ; i--) {
2135         if (table[i].dcfclk_mhz < min_dcfclk_mhz) {
2136             table[i].dcfclk_mhz = min_dcfclk_mhz;
2137             break;
2138         }
2139     }
2140 
2141     // Remove duplicate states, note duplicate states are always neighbouring since table is sorted.
2142     i = 0;
2143     while (i < *num_entries - 1) {
2144         if (table[i].dcfclk_mhz == table[i + 1].dcfclk_mhz &&
2145                 table[i].fabricclk_mhz == table[i + 1].fabricclk_mhz &&
2146                 table[i].dram_speed_mts == table[i + 1].dram_speed_mts)
2147             remove_entry_from_table_at_index(table, num_entries, i + 1);
2148         else
2149             i++;
2150     }
2151 
2152     // Fix up the state indicies
2153     for (i = *num_entries - 1; i >= 0 ; i--) {
2154         table[i].state = i;
2155     }
2156 
2157     return 0;
2158 }
2159 
2160 /**
2161  * dcn32_update_bw_bounding_box
2162  *
2163  * This would override some dcn3_2 ip_or_soc initial parameters hardcoded from
2164  * spreadsheet with actual values as per dGPU SKU:
2165  * - with passed few options from dc->config
2166  * - with dentist_vco_frequency from Clk Mgr (currently hardcoded, but might
2167  *   need to get it from PM FW)
2168  * - with passed latency values (passed in ns units) in dc-> bb override for
2169  *   debugging purposes
2170  * - with passed latencies from VBIOS (in 100_ns units) if available for
2171  *   certain dGPU SKU
2172  * - with number of DRAM channels from VBIOS (which differ for certain dGPU SKU
2173  *   of the same ASIC)
2174  * - clocks levels with passed clk_table entries from Clk Mgr as reported by PM
2175  *   FW for different clocks (which might differ for certain dGPU SKU of the
2176  *   same ASIC)
2177  */
2178 void dcn32_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params)
2179 {
2180     dc_assert_fp_enabled();
2181 
2182     if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) {
2183         /* Overrides from dc->config options */
2184         dcn3_2_ip.clamp_min_dcfclk = dc->config.clamp_min_dcfclk;
2185 
2186         /* Override from passed dc->bb_overrides if available*/
2187         if ((int)(dcn3_2_soc.sr_exit_time_us * 1000) != dc->bb_overrides.sr_exit_time_ns
2188                 && dc->bb_overrides.sr_exit_time_ns) {
2189             dcn3_2_soc.sr_exit_time_us = dc->bb_overrides.sr_exit_time_ns / 1000.0;
2190         }
2191 
2192         if ((int)(dcn3_2_soc.sr_enter_plus_exit_time_us * 1000)
2193                 != dc->bb_overrides.sr_enter_plus_exit_time_ns
2194                 && dc->bb_overrides.sr_enter_plus_exit_time_ns) {
2195             dcn3_2_soc.sr_enter_plus_exit_time_us =
2196                 dc->bb_overrides.sr_enter_plus_exit_time_ns / 1000.0;
2197         }
2198 
2199         if ((int)(dcn3_2_soc.urgent_latency_us * 1000) != dc->bb_overrides.urgent_latency_ns
2200             && dc->bb_overrides.urgent_latency_ns) {
2201             dcn3_2_soc.urgent_latency_us = dc->bb_overrides.urgent_latency_ns / 1000.0;
2202         }
2203 
2204         if ((int)(dcn3_2_soc.dram_clock_change_latency_us * 1000)
2205                 != dc->bb_overrides.dram_clock_change_latency_ns
2206                 && dc->bb_overrides.dram_clock_change_latency_ns) {
2207             dcn3_2_soc.dram_clock_change_latency_us =
2208                 dc->bb_overrides.dram_clock_change_latency_ns / 1000.0;
2209         }
2210 
2211         if ((int)(dcn3_2_soc.fclk_change_latency_us * 1000)
2212                 != dc->bb_overrides.fclk_clock_change_latency_ns
2213                 && dc->bb_overrides.fclk_clock_change_latency_ns) {
2214             dcn3_2_soc.fclk_change_latency_us =
2215                 dc->bb_overrides.fclk_clock_change_latency_ns / 1000;
2216         }
2217 
2218         if ((int)(dcn3_2_soc.dummy_pstate_latency_us * 1000)
2219                 != dc->bb_overrides.dummy_clock_change_latency_ns
2220                 && dc->bb_overrides.dummy_clock_change_latency_ns) {
2221             dcn3_2_soc.dummy_pstate_latency_us =
2222                 dc->bb_overrides.dummy_clock_change_latency_ns / 1000.0;
2223         }
2224 
2225         /* Override from VBIOS if VBIOS bb_info available */
2226         if (dc->ctx->dc_bios->funcs->get_soc_bb_info) {
2227             struct bp_soc_bb_info bb_info = {0};
2228 
2229             if (dc->ctx->dc_bios->funcs->get_soc_bb_info(dc->ctx->dc_bios, &bb_info) == BP_RESULT_OK) {
2230                 if (bb_info.dram_clock_change_latency_100ns > 0)
2231                     dcn3_2_soc.dram_clock_change_latency_us = bb_info.dram_clock_change_latency_100ns * 10;
2232 
2233             if (bb_info.dram_sr_enter_exit_latency_100ns > 0)
2234                 dcn3_2_soc.sr_enter_plus_exit_time_us = bb_info.dram_sr_enter_exit_latency_100ns * 10;
2235 
2236             if (bb_info.dram_sr_exit_latency_100ns > 0)
2237                 dcn3_2_soc.sr_exit_time_us = bb_info.dram_sr_exit_latency_100ns * 10;
2238             }
2239         }
2240 
2241         /* Override from VBIOS for num_chan */
2242         if (dc->ctx->dc_bios->vram_info.num_chans)
2243             dcn3_2_soc.num_chans = dc->ctx->dc_bios->vram_info.num_chans;
2244 
2245         if (dc->ctx->dc_bios->vram_info.dram_channel_width_bytes)
2246             dcn3_2_soc.dram_channel_width_bytes = dc->ctx->dc_bios->vram_info.dram_channel_width_bytes;
2247 
2248     }
2249 
2250     /* Override dispclk_dppclk_vco_speed_mhz from Clk Mgr */
2251     dcn3_2_soc.dispclk_dppclk_vco_speed_mhz = dc->clk_mgr->dentist_vco_freq_khz / 1000.0;
2252     dc->dml.soc.dispclk_dppclk_vco_speed_mhz = dc->clk_mgr->dentist_vco_freq_khz / 1000.0;
2253 
2254     /* Overrides Clock levelsfrom CLK Mgr table entries as reported by PM FW */
2255     if ((!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) && (bw_params->clk_table.entries[0].memclk_mhz)) {
2256         if (dc->debug.use_legacy_soc_bb_mechanism) {
2257             unsigned int i = 0, j = 0, num_states = 0;
2258 
2259             unsigned int dcfclk_mhz[DC__VOLTAGE_STATES] = {0};
2260             unsigned int dram_speed_mts[DC__VOLTAGE_STATES] = {0};
2261             unsigned int optimal_uclk_for_dcfclk_sta_targets[DC__VOLTAGE_STATES] = {0};
2262             unsigned int optimal_dcfclk_for_uclk[DC__VOLTAGE_STATES] = {0};
2263             unsigned int min_dcfclk = UINT_MAX;
2264             /* Set 199 as first value in STA target array to have a minimum DCFCLK value.
2265              * For DCN32 we set min to 199 so minimum FCLK DPM0 (300Mhz can be achieved) */
2266             unsigned int dcfclk_sta_targets[DC__VOLTAGE_STATES] = {199, 615, 906, 1324, 1564};
2267             unsigned int num_dcfclk_sta_targets = 4, num_uclk_states = 0;
2268             unsigned int max_dcfclk_mhz = 0, max_dispclk_mhz = 0, max_dppclk_mhz = 0, max_phyclk_mhz = 0;
2269 
2270             for (i = 0; i < MAX_NUM_DPM_LVL; i++) {
2271                 if (bw_params->clk_table.entries[i].dcfclk_mhz > max_dcfclk_mhz)
2272                     max_dcfclk_mhz = bw_params->clk_table.entries[i].dcfclk_mhz;
2273                 if (bw_params->clk_table.entries[i].dcfclk_mhz != 0 &&
2274                         bw_params->clk_table.entries[i].dcfclk_mhz < min_dcfclk)
2275                     min_dcfclk = bw_params->clk_table.entries[i].dcfclk_mhz;
2276                 if (bw_params->clk_table.entries[i].dispclk_mhz > max_dispclk_mhz)
2277                     max_dispclk_mhz = bw_params->clk_table.entries[i].dispclk_mhz;
2278                 if (bw_params->clk_table.entries[i].dppclk_mhz > max_dppclk_mhz)
2279                     max_dppclk_mhz = bw_params->clk_table.entries[i].dppclk_mhz;
2280                 if (bw_params->clk_table.entries[i].phyclk_mhz > max_phyclk_mhz)
2281                     max_phyclk_mhz = bw_params->clk_table.entries[i].phyclk_mhz;
2282             }
2283             if (min_dcfclk > dcfclk_sta_targets[0])
2284                 dcfclk_sta_targets[0] = min_dcfclk;
2285             if (!max_dcfclk_mhz)
2286                 max_dcfclk_mhz = dcn3_2_soc.clock_limits[0].dcfclk_mhz;
2287             if (!max_dispclk_mhz)
2288                 max_dispclk_mhz = dcn3_2_soc.clock_limits[0].dispclk_mhz;
2289             if (!max_dppclk_mhz)
2290                 max_dppclk_mhz = dcn3_2_soc.clock_limits[0].dppclk_mhz;
2291             if (!max_phyclk_mhz)
2292                 max_phyclk_mhz = dcn3_2_soc.clock_limits[0].phyclk_mhz;
2293 
2294             if (max_dcfclk_mhz > dcfclk_sta_targets[num_dcfclk_sta_targets-1]) {
2295                 // If max DCFCLK is greater than the max DCFCLK STA target, insert into the DCFCLK STA target array
2296                 dcfclk_sta_targets[num_dcfclk_sta_targets] = max_dcfclk_mhz;
2297                 num_dcfclk_sta_targets++;
2298             } else if (max_dcfclk_mhz < dcfclk_sta_targets[num_dcfclk_sta_targets-1]) {
2299                 // If max DCFCLK is less than the max DCFCLK STA target, cap values and remove duplicates
2300                 for (i = 0; i < num_dcfclk_sta_targets; i++) {
2301                     if (dcfclk_sta_targets[i] > max_dcfclk_mhz) {
2302                         dcfclk_sta_targets[i] = max_dcfclk_mhz;
2303                         break;
2304                     }
2305                 }
2306                 // Update size of array since we "removed" duplicates
2307                 num_dcfclk_sta_targets = i + 1;
2308             }
2309 
2310             num_uclk_states = bw_params->clk_table.num_entries;
2311 
2312             // Calculate optimal dcfclk for each uclk
2313             for (i = 0; i < num_uclk_states; i++) {
2314                 dcn32_get_optimal_dcfclk_fclk_for_uclk(bw_params->clk_table.entries[i].memclk_mhz * 16,
2315                         &optimal_dcfclk_for_uclk[i], NULL);
2316                 if (optimal_dcfclk_for_uclk[i] < bw_params->clk_table.entries[0].dcfclk_mhz) {
2317                     optimal_dcfclk_for_uclk[i] = bw_params->clk_table.entries[0].dcfclk_mhz;
2318                 }
2319             }
2320 
2321             // Calculate optimal uclk for each dcfclk sta target
2322             for (i = 0; i < num_dcfclk_sta_targets; i++) {
2323                 for (j = 0; j < num_uclk_states; j++) {
2324                     if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j]) {
2325                         optimal_uclk_for_dcfclk_sta_targets[i] =
2326                                 bw_params->clk_table.entries[j].memclk_mhz * 16;
2327                         break;
2328                     }
2329                 }
2330             }
2331 
2332             i = 0;
2333             j = 0;
2334             // create the final dcfclk and uclk table
2335             while (i < num_dcfclk_sta_targets && j < num_uclk_states && num_states < DC__VOLTAGE_STATES) {
2336                 if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j] && i < num_dcfclk_sta_targets) {
2337                     dcfclk_mhz[num_states] = dcfclk_sta_targets[i];
2338                     dram_speed_mts[num_states++] = optimal_uclk_for_dcfclk_sta_targets[i++];
2339                 } else {
2340                     if (j < num_uclk_states && optimal_dcfclk_for_uclk[j] <= max_dcfclk_mhz) {
2341                         dcfclk_mhz[num_states] = optimal_dcfclk_for_uclk[j];
2342                         dram_speed_mts[num_states++] = bw_params->clk_table.entries[j++].memclk_mhz * 16;
2343                     } else {
2344                         j = num_uclk_states;
2345                     }
2346                 }
2347             }
2348 
2349             while (i < num_dcfclk_sta_targets && num_states < DC__VOLTAGE_STATES) {
2350                 dcfclk_mhz[num_states] = dcfclk_sta_targets[i];
2351                 dram_speed_mts[num_states++] = optimal_uclk_for_dcfclk_sta_targets[i++];
2352             }
2353 
2354             while (j < num_uclk_states && num_states < DC__VOLTAGE_STATES &&
2355                     optimal_dcfclk_for_uclk[j] <= max_dcfclk_mhz) {
2356                 dcfclk_mhz[num_states] = optimal_dcfclk_for_uclk[j];
2357                 dram_speed_mts[num_states++] = bw_params->clk_table.entries[j++].memclk_mhz * 16;
2358             }
2359 
2360             dcn3_2_soc.num_states = num_states;
2361             for (i = 0; i < dcn3_2_soc.num_states; i++) {
2362                 dcn3_2_soc.clock_limits[i].state = i;
2363                 dcn3_2_soc.clock_limits[i].dcfclk_mhz = dcfclk_mhz[i];
2364                 dcn3_2_soc.clock_limits[i].fabricclk_mhz = dcfclk_mhz[i];
2365 
2366                 /* Fill all states with max values of all these clocks */
2367                 dcn3_2_soc.clock_limits[i].dispclk_mhz = max_dispclk_mhz;
2368                 dcn3_2_soc.clock_limits[i].dppclk_mhz  = max_dppclk_mhz;
2369                 dcn3_2_soc.clock_limits[i].phyclk_mhz  = max_phyclk_mhz;
2370                 dcn3_2_soc.clock_limits[i].dscclk_mhz  = max_dispclk_mhz / 3;
2371 
2372                 /* Populate from bw_params for DTBCLK, SOCCLK */
2373                 if (i > 0) {
2374                     if (!bw_params->clk_table.entries[i].dtbclk_mhz) {
2375                         dcn3_2_soc.clock_limits[i].dtbclk_mhz  = dcn3_2_soc.clock_limits[i-1].dtbclk_mhz;
2376                     } else {
2377                         dcn3_2_soc.clock_limits[i].dtbclk_mhz  = bw_params->clk_table.entries[i].dtbclk_mhz;
2378                     }
2379                 } else if (bw_params->clk_table.entries[i].dtbclk_mhz) {
2380                     dcn3_2_soc.clock_limits[i].dtbclk_mhz  = bw_params->clk_table.entries[i].dtbclk_mhz;
2381                 }
2382 
2383                 if (!bw_params->clk_table.entries[i].socclk_mhz && i > 0)
2384                     dcn3_2_soc.clock_limits[i].socclk_mhz = dcn3_2_soc.clock_limits[i-1].socclk_mhz;
2385                 else
2386                     dcn3_2_soc.clock_limits[i].socclk_mhz = bw_params->clk_table.entries[i].socclk_mhz;
2387 
2388                 if (!dram_speed_mts[i] && i > 0)
2389                     dcn3_2_soc.clock_limits[i].dram_speed_mts = dcn3_2_soc.clock_limits[i-1].dram_speed_mts;
2390                 else
2391                     dcn3_2_soc.clock_limits[i].dram_speed_mts = dram_speed_mts[i];
2392 
2393                 /* These clocks cannot come from bw_params, always fill from dcn3_2_soc[0] */
2394                 /* PHYCLK_D18, PHYCLK_D32 */
2395                 dcn3_2_soc.clock_limits[i].phyclk_d18_mhz = dcn3_2_soc.clock_limits[0].phyclk_d18_mhz;
2396                 dcn3_2_soc.clock_limits[i].phyclk_d32_mhz = dcn3_2_soc.clock_limits[0].phyclk_d32_mhz;
2397             }
2398         } else {
2399             build_synthetic_soc_states(bw_params, dcn3_2_soc.clock_limits, &dcn3_2_soc.num_states);
2400         }
2401 
2402         /* Re-init DML with updated bb */
2403         dml_init_instance(&dc->dml, &dcn3_2_soc, &dcn3_2_ip, DML_PROJECT_DCN32);
2404         if (dc->current_state)
2405             dml_init_instance(&dc->current_state->bw_ctx.dml, &dcn3_2_soc, &dcn3_2_ip, DML_PROJECT_DCN32);
2406     }
2407 }
2408