LCOV - code coverage report
Current view: top level - drivers/gpu/drm/amd/display/dc/dml/dcn32 - dcn32_fpu.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 1129 0.0 %
Date: 2022-12-09 01:23:36 Functions: 0 24 0.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: MIT
       2             : /*
       3             :  * Copyright 2022 Advanced Micro Devices, Inc.
       4             :  *
       5             :  * Permission is hereby granted, free of charge, to any person obtaining a
       6             :  * copy of this software and associated documentation files (the "Software"),
       7             :  * to deal in the Software without restriction, including without limitation
       8             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
       9             :  * and/or sell copies of the Software, and to permit persons to whom the
      10             :  * Software is furnished to do so, subject to the following conditions:
      11             :  *
      12             :  * The above copyright notice and this permission notice shall be included in
      13             :  * all copies or substantial portions of the Software.
      14             :  *
      15             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      18             :  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
      19             :  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
      20             :  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
      21             :  * OTHER DEALINGS IN THE SOFTWARE.
      22             :  *
      23             :  * Authors: AMD
      24             :  *
      25             :  */
      26             : #include "dcn32_fpu.h"
      27             : #include "dc_link_dp.h"
      28             : #include "dcn32/dcn32_resource.h"
      29             : #include "dcn20/dcn20_resource.h"
      30             : #include "display_mode_vba_util_32.h"
      31             : // We need this includes for WATERMARKS_* defines
      32             : #include "clk_mgr/dcn32/dcn32_smu13_driver_if.h"
      33             : #include "dcn30/dcn30_resource.h"
      34             : 
      35             : #define DC_LOGGER_INIT(logger)
      36             : 
      37             : struct _vcs_dpi_ip_params_st dcn3_2_ip = {
      38             :         .gpuvm_enable = 0,
      39             :         .gpuvm_max_page_table_levels = 4,
      40             :         .hostvm_enable = 0,
      41             :         .rob_buffer_size_kbytes = 128,
      42             :         .det_buffer_size_kbytes = DCN3_2_DEFAULT_DET_SIZE,
      43             :         .config_return_buffer_size_in_kbytes = 1280,
      44             :         .compressed_buffer_segment_size_in_kbytes = 64,
      45             :         .meta_fifo_size_in_kentries = 22,
      46             :         .zero_size_buffer_entries = 512,
      47             :         .compbuf_reserved_space_64b = 256,
      48             :         .compbuf_reserved_space_zs = 64,
      49             :         .dpp_output_buffer_pixels = 2560,
      50             :         .opp_output_buffer_lines = 1,
      51             :         .pixel_chunk_size_kbytes = 8,
      52             :         .alpha_pixel_chunk_size_kbytes = 4,
      53             :         .min_pixel_chunk_size_bytes = 1024,
      54             :         .dcc_meta_buffer_size_bytes = 6272,
      55             :         .meta_chunk_size_kbytes = 2,
      56             :         .min_meta_chunk_size_bytes = 256,
      57             :         .writeback_chunk_size_kbytes = 8,
      58             :         .ptoi_supported = false,
      59             :         .num_dsc = 4,
      60             :         .maximum_dsc_bits_per_component = 12,
      61             :         .maximum_pixels_per_line_per_dsc_unit = 6016,
      62             :         .dsc422_native_support = true,
      63             :         .is_line_buffer_bpp_fixed = true,
      64             :         .line_buffer_fixed_bpp = 57,
      65             :         .line_buffer_size_bits = 1171920,
      66             :         .max_line_buffer_lines = 32,
      67             :         .writeback_interface_buffer_size_kbytes = 90,
      68             :         .max_num_dpp = 4,
      69             :         .max_num_otg = 4,
      70             :         .max_num_hdmi_frl_outputs = 1,
      71             :         .max_num_wb = 1,
      72             :         .max_dchub_pscl_bw_pix_per_clk = 4,
      73             :         .max_pscl_lb_bw_pix_per_clk = 2,
      74             :         .max_lb_vscl_bw_pix_per_clk = 4,
      75             :         .max_vscl_hscl_bw_pix_per_clk = 4,
      76             :         .max_hscl_ratio = 6,
      77             :         .max_vscl_ratio = 6,
      78             :         .max_hscl_taps = 8,
      79             :         .max_vscl_taps = 8,
      80             :         .dpte_buffer_size_in_pte_reqs_luma = 64,
      81             :         .dpte_buffer_size_in_pte_reqs_chroma = 34,
      82             :         .dispclk_ramp_margin_percent = 1,
      83             :         .max_inter_dcn_tile_repeaters = 8,
      84             :         .cursor_buffer_size = 16,
      85             :         .cursor_chunk_size = 2,
      86             :         .writeback_line_buffer_buffer_size = 0,
      87             :         .writeback_min_hscl_ratio = 1,
      88             :         .writeback_min_vscl_ratio = 1,
      89             :         .writeback_max_hscl_ratio = 1,
      90             :         .writeback_max_vscl_ratio = 1,
      91             :         .writeback_max_hscl_taps = 1,
      92             :         .writeback_max_vscl_taps = 1,
      93             :         .dppclk_delay_subtotal = 47,
      94             :         .dppclk_delay_scl = 50,
      95             :         .dppclk_delay_scl_lb_only = 16,
      96             :         .dppclk_delay_cnvc_formatter = 28,
      97             :         .dppclk_delay_cnvc_cursor = 6,
      98             :         .dispclk_delay_subtotal = 125,
      99             :         .dynamic_metadata_vm_enabled = false,
     100             :         .odm_combine_4to1_supported = false,
     101             :         .dcc_supported = true,
     102             :         .max_num_dp2p0_outputs = 2,
     103             :         .max_num_dp2p0_streams = 4,
     104             : };
     105             : 
     106             : struct _vcs_dpi_soc_bounding_box_st dcn3_2_soc = {
     107             :         .clock_limits = {
     108             :                 {
     109             :                         .state = 0,
     110             :                         .dcfclk_mhz = 1564.0,
     111             :                         .fabricclk_mhz = 400.0,
     112             :                         .dispclk_mhz = 2150.0,
     113             :                         .dppclk_mhz = 2150.0,
     114             :                         .phyclk_mhz = 810.0,
     115             :                         .phyclk_d18_mhz = 667.0,
     116             :                         .phyclk_d32_mhz = 625.0,
     117             :                         .socclk_mhz = 1200.0,
     118             :                         .dscclk_mhz = 716.667,
     119             :                         .dram_speed_mts = 16000.0,
     120             :                         .dtbclk_mhz = 1564.0,
     121             :                 },
     122             :         },
     123             :         .num_states = 1,
     124             :         .sr_exit_time_us = 20.16,
     125             :         .sr_enter_plus_exit_time_us = 27.13,
     126             :         .sr_exit_z8_time_us = 285.0,
     127             :         .sr_enter_plus_exit_z8_time_us = 320,
     128             :         .writeback_latency_us = 12.0,
     129             :         .round_trip_ping_latency_dcfclk_cycles = 263,
     130             :         .urgent_latency_pixel_data_only_us = 4.0,
     131             :         .urgent_latency_pixel_mixed_with_vm_data_us = 4.0,
     132             :         .urgent_latency_vm_data_only_us = 4.0,
     133             :         .fclk_change_latency_us = 20,
     134             :         .usr_retraining_latency_us = 2,
     135             :         .smn_latency_us = 2,
     136             :         .mall_allocated_for_dcn_mbytes = 64,
     137             :         .urgent_out_of_order_return_per_channel_pixel_only_bytes = 4096,
     138             :         .urgent_out_of_order_return_per_channel_pixel_and_vm_bytes = 4096,
     139             :         .urgent_out_of_order_return_per_channel_vm_only_bytes = 4096,
     140             :         .pct_ideal_sdp_bw_after_urgent = 100.0,
     141             :         .pct_ideal_fabric_bw_after_urgent = 67.0,
     142             :         .pct_ideal_dram_sdp_bw_after_urgent_pixel_only = 20.0,
     143             :         .pct_ideal_dram_sdp_bw_after_urgent_pixel_and_vm = 60.0, // N/A, for now keep as is until DML implemented
     144             :         .pct_ideal_dram_sdp_bw_after_urgent_vm_only = 30.0, // N/A, for now keep as is until DML implemented
     145             :         .pct_ideal_dram_bw_after_urgent_strobe = 67.0,
     146             :         .max_avg_sdp_bw_use_normal_percent = 80.0,
     147             :         .max_avg_fabric_bw_use_normal_percent = 60.0,
     148             :         .max_avg_dram_bw_use_normal_strobe_percent = 50.0,
     149             :         .max_avg_dram_bw_use_normal_percent = 15.0,
     150             :         .num_chans = 8,
     151             :         .dram_channel_width_bytes = 2,
     152             :         .fabric_datapath_to_dcn_data_return_bytes = 64,
     153             :         .return_bus_width_bytes = 64,
     154             :         .downspread_percent = 0.38,
     155             :         .dcn_downspread_percent = 0.5,
     156             :         .dram_clock_change_latency_us = 400,
     157             :         .dispclk_dppclk_vco_speed_mhz = 4300.0,
     158             :         .do_urgent_latency_adjustment = true,
     159             :         .urgent_latency_adjustment_fabric_clock_component_us = 1.0,
     160             :         .urgent_latency_adjustment_fabric_clock_reference_mhz = 1000,
     161             : };
     162             : 
     163           0 : void dcn32_build_wm_range_table_fpu(struct clk_mgr_internal *clk_mgr)
     164             : {
     165             :         /* defaults */
     166           0 :         double pstate_latency_us = clk_mgr->base.ctx->dc->dml.soc.dram_clock_change_latency_us;
     167           0 :         double fclk_change_latency_us = clk_mgr->base.ctx->dc->dml.soc.fclk_change_latency_us;
     168           0 :         double sr_exit_time_us = clk_mgr->base.ctx->dc->dml.soc.sr_exit_time_us;
     169           0 :         double sr_enter_plus_exit_time_us = clk_mgr->base.ctx->dc->dml.soc.sr_enter_plus_exit_time_us;
     170             :         /* For min clocks use as reported by PM FW and report those as min */
     171           0 :         uint16_t min_uclk_mhz                   = clk_mgr->base.bw_params->clk_table.entries[0].memclk_mhz;
     172           0 :         uint16_t min_dcfclk_mhz                 = clk_mgr->base.bw_params->clk_table.entries[0].dcfclk_mhz;
     173           0 :         uint16_t setb_min_uclk_mhz              = min_uclk_mhz;
     174           0 :         uint16_t dcfclk_mhz_for_the_second_state = clk_mgr->base.ctx->dc->dml.soc.clock_limits[2].dcfclk_mhz;
     175             : 
     176           0 :         dc_assert_fp_enabled();
     177             : 
     178             :         /* For Set B ranges use min clocks state 2 when available, and report those to PM FW */
     179           0 :         if (dcfclk_mhz_for_the_second_state)
     180           0 :                 clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.min_dcfclk = dcfclk_mhz_for_the_second_state;
     181             :         else
     182           0 :                 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;
     183             : 
     184           0 :         if (clk_mgr->base.bw_params->clk_table.entries[2].memclk_mhz)
     185           0 :                 setb_min_uclk_mhz = clk_mgr->base.bw_params->clk_table.entries[2].memclk_mhz;
     186             : 
     187             :         /* Set A - Normal - default values */
     188           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].valid = true;
     189           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].dml_input.pstate_latency_us = pstate_latency_us;
     190           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].dml_input.fclk_change_latency_us = fclk_change_latency_us;
     191           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].dml_input.sr_exit_time_us = sr_exit_time_us;
     192           0 :         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;
     193           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.wm_type = WATERMARKS_CLOCK_RANGE;
     194           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.min_dcfclk = min_dcfclk_mhz;
     195           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.max_dcfclk = 0xFFFF;
     196           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.min_uclk = min_uclk_mhz;
     197           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_A].pmfw_breakdown.max_uclk = 0xFFFF;
     198             : 
     199             :         /* Set B - Performance - higher clocks, using DPM[2] DCFCLK and UCLK */
     200           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].valid = true;
     201           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].dml_input.pstate_latency_us = pstate_latency_us;
     202           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].dml_input.fclk_change_latency_us = fclk_change_latency_us;
     203           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].dml_input.sr_exit_time_us = sr_exit_time_us;
     204           0 :         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;
     205           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.wm_type = WATERMARKS_CLOCK_RANGE;
     206           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.max_dcfclk = 0xFFFF;
     207           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.min_uclk = setb_min_uclk_mhz;
     208           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_B].pmfw_breakdown.max_uclk = 0xFFFF;
     209             : 
     210             :         /* Set C - Dummy P-State - P-State latency set to "dummy p-state" value */
     211             :         /* 'DalDummyClockChangeLatencyNs' registry key option set to 0x7FFFFFFF can be used to disable Set C for dummy p-state */
     212           0 :         if (clk_mgr->base.ctx->dc->bb_overrides.dummy_clock_change_latency_ns != 0x7FFFFFFF) {
     213           0 :                 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].valid = true;
     214           0 :                 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].dml_input.pstate_latency_us = 38;
     215           0 :                 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].dml_input.fclk_change_latency_us = fclk_change_latency_us;
     216           0 :                 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].dml_input.sr_exit_time_us = sr_exit_time_us;
     217           0 :                 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;
     218           0 :                 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.wm_type = WATERMARKS_DUMMY_PSTATE;
     219           0 :                 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.min_dcfclk = min_dcfclk_mhz;
     220           0 :                 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.max_dcfclk = 0xFFFF;
     221           0 :                 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.min_uclk = min_uclk_mhz;
     222           0 :                 clk_mgr->base.bw_params->wm_table.nv_entries[WM_C].pmfw_breakdown.max_uclk = 0xFFFF;
     223           0 :                 clk_mgr->base.bw_params->dummy_pstate_table[0].dram_speed_mts = clk_mgr->base.bw_params->clk_table.entries[0].memclk_mhz * 16;
     224           0 :                 clk_mgr->base.bw_params->dummy_pstate_table[0].dummy_pstate_latency_us = 38;
     225           0 :                 clk_mgr->base.bw_params->dummy_pstate_table[1].dram_speed_mts = clk_mgr->base.bw_params->clk_table.entries[1].memclk_mhz * 16;
     226           0 :                 clk_mgr->base.bw_params->dummy_pstate_table[1].dummy_pstate_latency_us = 9;
     227           0 :                 clk_mgr->base.bw_params->dummy_pstate_table[2].dram_speed_mts = clk_mgr->base.bw_params->clk_table.entries[2].memclk_mhz * 16;
     228           0 :                 clk_mgr->base.bw_params->dummy_pstate_table[2].dummy_pstate_latency_us = 8;
     229           0 :                 clk_mgr->base.bw_params->dummy_pstate_table[3].dram_speed_mts = clk_mgr->base.bw_params->clk_table.entries[3].memclk_mhz * 16;
     230           0 :                 clk_mgr->base.bw_params->dummy_pstate_table[3].dummy_pstate_latency_us = 5;
     231             :         }
     232             :         /* Set D - MALL - SR enter and exit time specific to MALL, TBD after bringup or later phase for now use DRAM values / 2 */
     233             :         /* For MALL DRAM clock change latency is N/A, for watermak calculations use lowest value dummy P state latency */
     234           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].valid = true;
     235           0 :         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;
     236           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].dml_input.fclk_change_latency_us = fclk_change_latency_us;
     237           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].dml_input.sr_exit_time_us = sr_exit_time_us / 2; // TBD
     238           0 :         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
     239           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.wm_type = WATERMARKS_MALL;
     240           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.min_dcfclk = min_dcfclk_mhz;
     241           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.max_dcfclk = 0xFFFF;
     242           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.min_uclk = min_uclk_mhz;
     243           0 :         clk_mgr->base.bw_params->wm_table.nv_entries[WM_D].pmfw_breakdown.max_uclk = 0xFFFF;
     244           0 : }
     245             : 
     246             : /**
     247             :  * dcn32_helper_populate_phantom_dlg_params - Get DLG params for phantom pipes
     248             :  * and populate pipe_ctx with those params.
     249             :  *
     250             :  * This function must be called AFTER the phantom pipes are added to context
     251             :  * and run through DML (so that the DLG params for the phantom pipes can be
     252             :  * populated), and BEFORE we program the timing for the phantom pipes.
     253             :  *
     254             :  * @dc: [in] current dc state
     255             :  * @context: [in] new dc state
     256             :  * @pipes: [in] DML pipe params array
     257             :  * @pipe_cnt: [in] DML pipe count
     258             :  */
     259           0 : void dcn32_helper_populate_phantom_dlg_params(struct dc *dc,
     260             :                                               struct dc_state *context,
     261             :                                               display_e2e_pipe_params_st *pipes,
     262             :                                               int pipe_cnt)
     263             : {
     264             :         uint32_t i, pipe_idx;
     265             : 
     266           0 :         dc_assert_fp_enabled();
     267             : 
     268           0 :         for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
     269           0 :                 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
     270             : 
     271           0 :                 if (!pipe->stream)
     272           0 :                         continue;
     273             : 
     274           0 :                 if (pipe->plane_state && pipe->stream->mall_stream_config.type == SUBVP_PHANTOM) {
     275           0 :                         pipes[pipe_idx].pipe.dest.vstartup_start =
     276           0 :                                 get_vstartup(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);
     277           0 :                         pipes[pipe_idx].pipe.dest.vupdate_offset =
     278           0 :                                 get_vupdate_offset(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);
     279           0 :                         pipes[pipe_idx].pipe.dest.vupdate_width =
     280           0 :                                 get_vupdate_width(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);
     281           0 :                         pipes[pipe_idx].pipe.dest.vready_offset =
     282           0 :                                 get_vready_offset(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);
     283           0 :                         pipe->pipe_dlg_param = pipes[pipe_idx].pipe.dest;
     284             :                 }
     285           0 :                 pipe_idx++;
     286             :         }
     287           0 : }
     288             : 
     289           0 : bool dcn32_predict_pipe_split(struct dc_state *context, display_pipe_params_st pipe, int index)
     290             : {
     291             :         double pscl_throughput;
     292             :         double pscl_throughput_chroma;
     293             :         double dpp_clk_single_dpp, clock;
     294           0 :         double clk_frequency = 0.0;
     295           0 :         double vco_speed = context->bw_ctx.dml.soc.dispclk_dppclk_vco_speed_mhz;
     296             : 
     297           0 :         dc_assert_fp_enabled();
     298             : 
     299           0 :         dml32_CalculateSinglePipeDPPCLKAndSCLThroughput(pipe.scale_ratio_depth.hscl_ratio,
     300             :                                                         pipe.scale_ratio_depth.hscl_ratio_c,
     301             :                                                         pipe.scale_ratio_depth.vscl_ratio,
     302             :                                                         pipe.scale_ratio_depth.vscl_ratio_c,
     303           0 :                                                         context->bw_ctx.dml.ip.max_dchub_pscl_bw_pix_per_clk,
     304           0 :                                                         context->bw_ctx.dml.ip.max_pscl_lb_bw_pix_per_clk,
     305             :                                                         pipe.dest.pixel_rate_mhz,
     306           0 :                                                         pipe.src.source_format,
     307             :                                                         pipe.scale_taps.htaps,
     308             :                                                         pipe.scale_taps.htaps_c,
     309             :                                                         pipe.scale_taps.vtaps,
     310             :                                                         pipe.scale_taps.vtaps_c,
     311             :                                                         /* Output */
     312             :                                                         &pscl_throughput, &pscl_throughput_chroma,
     313             :                                                         &dpp_clk_single_dpp);
     314             : 
     315           0 :         clock = dpp_clk_single_dpp * (1 + context->bw_ctx.dml.soc.dcn_downspread_percent / 100);
     316             : 
     317           0 :         if (clock > 0)
     318           0 :                 clk_frequency = vco_speed * 4.0 / ((int)(vco_speed * 4.0));
     319             : 
     320           0 :         if (clk_frequency > context->bw_ctx.dml.soc.clock_limits[index].dppclk_mhz)
     321             :                 return true;
     322             :         else
     323           0 :                 return false;
     324             : }
     325             : 
     326           0 : static float calculate_net_bw_in_kbytes_sec(struct _vcs_dpi_voltage_scaling_st *entry)
     327             : {
     328             :         float memory_bw_kbytes_sec;
     329             :         float fabric_bw_kbytes_sec;
     330             :         float sdp_bw_kbytes_sec;
     331             :         float limiting_bw_kbytes_sec;
     332             : 
     333           0 :         memory_bw_kbytes_sec = entry->dram_speed_mts *
     334           0 :                                 dcn3_2_soc.num_chans *
     335           0 :                                 dcn3_2_soc.dram_channel_width_bytes *
     336           0 :                                 ((float)dcn3_2_soc.pct_ideal_dram_sdp_bw_after_urgent_pixel_only / 100);
     337             : 
     338           0 :         fabric_bw_kbytes_sec = entry->fabricclk_mhz *
     339           0 :                                 dcn3_2_soc.return_bus_width_bytes *
     340           0 :                                 ((float)dcn3_2_soc.pct_ideal_fabric_bw_after_urgent / 100);
     341             : 
     342           0 :         sdp_bw_kbytes_sec = entry->dcfclk_mhz *
     343           0 :                                 dcn3_2_soc.return_bus_width_bytes *
     344           0 :                                 ((float)dcn3_2_soc.pct_ideal_sdp_bw_after_urgent / 100);
     345             : 
     346           0 :         limiting_bw_kbytes_sec = memory_bw_kbytes_sec;
     347             : 
     348           0 :         if (fabric_bw_kbytes_sec < limiting_bw_kbytes_sec)
     349           0 :                 limiting_bw_kbytes_sec = fabric_bw_kbytes_sec;
     350             : 
     351           0 :         if (sdp_bw_kbytes_sec < limiting_bw_kbytes_sec)
     352           0 :                 limiting_bw_kbytes_sec = sdp_bw_kbytes_sec;
     353             : 
     354           0 :         return limiting_bw_kbytes_sec;
     355             : }
     356             : 
     357           0 : static void get_optimal_ntuple(struct _vcs_dpi_voltage_scaling_st *entry)
     358             : {
     359           0 :         if (entry->dcfclk_mhz > 0) {
     360           0 :                 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);
     361             : 
     362           0 :                 entry->fabricclk_mhz = bw_on_sdp / (dcn3_2_soc.return_bus_width_bytes * ((float)dcn3_2_soc.pct_ideal_fabric_bw_after_urgent / 100));
     363           0 :                 entry->dram_speed_mts = bw_on_sdp / (dcn3_2_soc.num_chans *
     364           0 :                                 dcn3_2_soc.dram_channel_width_bytes * ((float)dcn3_2_soc.pct_ideal_dram_sdp_bw_after_urgent_pixel_only / 100));
     365           0 :         } else if (entry->fabricclk_mhz > 0) {
     366           0 :                 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);
     367             : 
     368           0 :                 entry->dcfclk_mhz = bw_on_fabric / (dcn3_2_soc.return_bus_width_bytes * ((float)dcn3_2_soc.pct_ideal_sdp_bw_after_urgent / 100));
     369           0 :                 entry->dram_speed_mts = bw_on_fabric / (dcn3_2_soc.num_chans *
     370           0 :                                 dcn3_2_soc.dram_channel_width_bytes * ((float)dcn3_2_soc.pct_ideal_dram_sdp_bw_after_urgent_pixel_only / 100));
     371           0 :         } else if (entry->dram_speed_mts > 0) {
     372           0 :                 float bw_on_dram = entry->dram_speed_mts * dcn3_2_soc.num_chans *
     373           0 :                                 dcn3_2_soc.dram_channel_width_bytes * ((float)dcn3_2_soc.pct_ideal_dram_sdp_bw_after_urgent_pixel_only / 100);
     374             : 
     375           0 :                 entry->fabricclk_mhz = bw_on_dram / (dcn3_2_soc.return_bus_width_bytes * ((float)dcn3_2_soc.pct_ideal_fabric_bw_after_urgent / 100));
     376           0 :                 entry->dcfclk_mhz = bw_on_dram / (dcn3_2_soc.return_bus_width_bytes * ((float)dcn3_2_soc.pct_ideal_sdp_bw_after_urgent / 100));
     377             :         }
     378           0 : }
     379             : 
     380           0 : void insert_entry_into_table_sorted(struct _vcs_dpi_voltage_scaling_st *table,
     381             :                                     unsigned int *num_entries,
     382             :                                     struct _vcs_dpi_voltage_scaling_st *entry)
     383             : {
     384           0 :         int i = 0;
     385           0 :         int index = 0;
     386           0 :         float net_bw_of_new_state = 0;
     387             : 
     388           0 :         dc_assert_fp_enabled();
     389             : 
     390           0 :         get_optimal_ntuple(entry);
     391             : 
     392           0 :         if (*num_entries == 0) {
     393           0 :                 table[0] = *entry;
     394           0 :                 (*num_entries)++;
     395             :         } else {
     396           0 :                 net_bw_of_new_state = calculate_net_bw_in_kbytes_sec(entry);
     397           0 :                 while (net_bw_of_new_state > calculate_net_bw_in_kbytes_sec(&table[index])) {
     398           0 :                         index++;
     399           0 :                         if (index >= *num_entries)
     400             :                                 break;
     401             :                 }
     402             : 
     403           0 :                 for (i = *num_entries; i > index; i--)
     404           0 :                         table[i] = table[i - 1];
     405             : 
     406           0 :                 table[index] = *entry;
     407           0 :                 (*num_entries)++;
     408             :         }
     409           0 : }
     410             : 
     411             : /**
     412             :  * dcn32_set_phantom_stream_timing: Set timing params for the phantom stream
     413             :  *
     414             :  * Set timing params of the phantom stream based on calculated output from DML.
     415             :  * This function first gets the DML pipe index using the DC pipe index, then
     416             :  * calls into DML (get_subviewport_lines_needed_in_mall) to get the number of
     417             :  * lines required for SubVP MCLK switching and assigns to the phantom stream
     418             :  * accordingly.
     419             :  *
     420             :  * - The number of SubVP lines calculated in DML does not take into account
     421             :  * FW processing delays and required pstate allow width, so we must include
     422             :  * that separately.
     423             :  *
     424             :  * - Set phantom backporch = vstartup of main pipe
     425             :  *
     426             :  * @dc: current dc state
     427             :  * @context: new dc state
     428             :  * @ref_pipe: Main pipe for the phantom stream
     429             :  * @pipes: DML pipe params
     430             :  * @pipe_cnt: number of DML pipes
     431             :  * @dc_pipe_idx: DC pipe index for the main pipe (i.e. ref_pipe)
     432             :  */
     433           0 : void dcn32_set_phantom_stream_timing(struct dc *dc,
     434             :                                      struct dc_state *context,
     435             :                                      struct pipe_ctx *ref_pipe,
     436             :                                      struct dc_stream_state *phantom_stream,
     437             :                                      display_e2e_pipe_params_st *pipes,
     438             :                                      unsigned int pipe_cnt,
     439             :                                      unsigned int dc_pipe_idx)
     440             : {
     441             :         unsigned int i, pipe_idx;
     442             :         struct pipe_ctx *pipe;
     443             :         uint32_t phantom_vactive, phantom_bp, pstate_width_fw_delay_lines;
     444           0 :         unsigned int vlevel = context->bw_ctx.dml.vba.VoltageLevel;
     445           0 :         unsigned int dcfclk = context->bw_ctx.dml.vba.DCFCLKState[vlevel][context->bw_ctx.dml.vba.maxMpcComb];
     446           0 :         unsigned int socclk = context->bw_ctx.dml.vba.SOCCLKPerState[vlevel];
     447             : 
     448           0 :         dc_assert_fp_enabled();
     449             : 
     450             :         // Find DML pipe index (pipe_idx) using dc_pipe_idx
     451           0 :         for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
     452           0 :                 pipe = &context->res_ctx.pipe_ctx[i];
     453             : 
     454           0 :                 if (!pipe->stream)
     455           0 :                         continue;
     456             : 
     457           0 :                 if (i == dc_pipe_idx)
     458             :                         break;
     459             : 
     460           0 :                 pipe_idx++;
     461             :         }
     462             : 
     463             :         // Calculate lines required for pstate allow width and FW processing delays
     464           0 :         pstate_width_fw_delay_lines = ((double)(dc->caps.subvp_fw_processing_delay_us +
     465           0 :                         dc->caps.subvp_pstate_allow_width_us) / 1000000) *
     466           0 :                         (ref_pipe->stream->timing.pix_clk_100hz * 100) /
     467           0 :                         (double)ref_pipe->stream->timing.h_total;
     468             : 
     469             :         // Update clks_cfg for calling into recalculate
     470           0 :         pipes[0].clks_cfg.voltage = vlevel;
     471           0 :         pipes[0].clks_cfg.dcfclk_mhz = dcfclk;
     472           0 :         pipes[0].clks_cfg.socclk_mhz = socclk;
     473             : 
     474             :         // DML calculation for MALL region doesn't take into account FW delay
     475             :         // and required pstate allow width for multi-display cases
     476             :         /* Add 16 lines margin to the MALL REGION because SUB_VP_START_LINE must be aligned
     477             :          * to 2 swaths (i.e. 16 lines)
     478             :          */
     479           0 :         phantom_vactive = get_subviewport_lines_needed_in_mall(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx) +
     480           0 :                                 pstate_width_fw_delay_lines + dc->caps.subvp_swath_height_margin_lines;
     481             : 
     482             :         // For backporch of phantom pipe, use vstartup of the main pipe
     483           0 :         phantom_bp = get_vstartup(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);
     484             : 
     485           0 :         phantom_stream->dst.y = 0;
     486           0 :         phantom_stream->dst.height = phantom_vactive;
     487           0 :         phantom_stream->src.y = 0;
     488           0 :         phantom_stream->src.height = phantom_vactive;
     489             : 
     490           0 :         phantom_stream->timing.v_addressable = phantom_vactive;
     491           0 :         phantom_stream->timing.v_front_porch = 1;
     492           0 :         phantom_stream->timing.v_total = phantom_stream->timing.v_addressable +
     493           0 :                                                 phantom_stream->timing.v_front_porch +
     494           0 :                                                 phantom_stream->timing.v_sync_width +
     495             :                                                 phantom_bp;
     496           0 :         phantom_stream->timing.flags.DSC = 0; // Don't need DSC for phantom timing
     497           0 : }
     498             : 
     499             : /**
     500             :  * dcn32_get_num_free_pipes: Calculate number of free pipes
     501             :  *
     502             :  * This function assumes that a "used" pipe is a pipe that has
     503             :  * both a stream and a plane assigned to it.
     504             :  *
     505             :  * @dc: current dc state
     506             :  * @context: new dc state
     507             :  *
     508             :  * Return:
     509             :  * Number of free pipes available in the context
     510             :  */
     511             : static unsigned int dcn32_get_num_free_pipes(struct dc *dc, struct dc_state *context)
     512             : {
     513             :         unsigned int i;
     514             :         unsigned int free_pipes = 0;
     515             :         unsigned int num_pipes = 0;
     516             : 
     517           0 :         for (i = 0; i < dc->res_pool->pipe_count; i++) {
     518           0 :                 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
     519             : 
     520           0 :                 if (pipe->stream && !pipe->top_pipe) {
     521           0 :                         while (pipe) {
     522           0 :                                 num_pipes++;
     523           0 :                                 pipe = pipe->bottom_pipe;
     524             :                         }
     525             :                 }
     526             :         }
     527             : 
     528           0 :         free_pipes = dc->res_pool->pipe_count - num_pipes;
     529             :         return free_pipes;
     530             : }
     531             : 
     532             : /**
     533             :  * dcn32_assign_subvp_pipe: Function to decide which pipe will use Sub-VP.
     534             :  *
     535             :  * We enter this function if we are Sub-VP capable (i.e. enough pipes available)
     536             :  * and regular P-State switching (i.e. VACTIVE/VBLANK) is not supported, or if
     537             :  * we are forcing SubVP P-State switching on the current config.
     538             :  *
     539             :  * The number of pipes used for the chosen surface must be less than or equal to the
     540             :  * number of free pipes available.
     541             :  *
     542             :  * In general we choose surfaces with the longest frame time first (better for SubVP + VBLANK).
     543             :  * For multi-display cases the ActiveDRAMClockChangeMargin doesn't provide enough info on its own
     544             :  * for determining which should be the SubVP pipe (need a way to determine if a pipe / plane doesn't
     545             :  * support MCLK switching naturally [i.e. ACTIVE or VBLANK]).
     546             :  *
     547             :  * @param dc: current dc state
     548             :  * @param context: new dc state
     549             :  * @param index: [out] dc pipe index for the pipe chosen to have phantom pipes assigned
     550             :  *
     551             :  * Return:
     552             :  * True if a valid pipe assignment was found for Sub-VP. Otherwise false.
     553             :  */
     554           0 : static bool dcn32_assign_subvp_pipe(struct dc *dc,
     555             :                                     struct dc_state *context,
     556             :                                     unsigned int *index)
     557             : {
     558             :         unsigned int i, pipe_idx;
     559           0 :         unsigned int max_frame_time = 0;
     560           0 :         bool valid_assignment_found = false;
     561           0 :         unsigned int free_pipes = dcn32_get_num_free_pipes(dc, context);
     562           0 :         bool current_assignment_freesync = false;
     563           0 :         struct vba_vars_st *vba = &context->bw_ctx.dml.vba;
     564             : 
     565           0 :         for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
     566           0 :                 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
     567           0 :                 unsigned int num_pipes = 0;
     568           0 :                 unsigned int refresh_rate = 0;
     569             : 
     570           0 :                 if (!pipe->stream)
     571           0 :                         continue;
     572             : 
     573             :                 // Round up
     574           0 :                 refresh_rate = (pipe->stream->timing.pix_clk_100hz * 100 +
     575           0 :                                 pipe->stream->timing.v_total * pipe->stream->timing.h_total - 1)
     576           0 :                                 / (double)(pipe->stream->timing.v_total * pipe->stream->timing.h_total);
     577             :                 /* SubVP pipe candidate requirements:
     578             :                  * - Refresh rate < 120hz
     579             :                  * - Not able to switch in vactive naturally (switching in active means the
     580             :                  *   DET provides enough buffer to hide the P-State switch latency -- trying
     581             :                  *   to combine this with SubVP can cause issues with the scheduling).
     582             :                  */
     583           0 :                 if (pipe->plane_state && !pipe->top_pipe &&
     584           0 :                                 pipe->stream->mall_stream_config.type == SUBVP_NONE && refresh_rate < 120 &&
     585           0 :                                 vba->ActiveDRAMClockChangeLatencyMarginPerState[vba->VoltageLevel][vba->maxMpcComb][vba->pipe_plane[pipe_idx]] <= 0) {
     586           0 :                         while (pipe) {
     587           0 :                                 num_pipes++;
     588           0 :                                 pipe = pipe->bottom_pipe;
     589             :                         }
     590             : 
     591           0 :                         pipe = &context->res_ctx.pipe_ctx[i];
     592           0 :                         if (num_pipes <= free_pipes) {
     593           0 :                                 struct dc_stream_state *stream = pipe->stream;
     594           0 :                                 unsigned int frame_us = (stream->timing.v_total * stream->timing.h_total /
     595           0 :                                                 (double)(stream->timing.pix_clk_100hz * 100)) * 1000000;
     596           0 :                                 if (frame_us > max_frame_time && !stream->ignore_msa_timing_param) {
     597           0 :                                         *index = i;
     598           0 :                                         max_frame_time = frame_us;
     599           0 :                                         valid_assignment_found = true;
     600           0 :                                         current_assignment_freesync = false;
     601             :                                 /* For the 2-Freesync display case, still choose the one with the
     602             :                              * longest frame time
     603             :                              */
     604           0 :                                 } else if (stream->ignore_msa_timing_param && (!valid_assignment_found ||
     605           0 :                                                 (current_assignment_freesync && frame_us > max_frame_time))) {
     606           0 :                                         *index = i;
     607           0 :                                         valid_assignment_found = true;
     608           0 :                                         current_assignment_freesync = true;
     609             :                                 }
     610             :                         }
     611             :                 }
     612           0 :                 pipe_idx++;
     613             :         }
     614           0 :         return valid_assignment_found;
     615             : }
     616             : 
     617             : /**
     618             :  * dcn32_enough_pipes_for_subvp: Function to check if there are "enough" pipes for SubVP.
     619             :  *
     620             :  * This function returns true if there are enough free pipes
     621             :  * to create the required phantom pipes for any given stream
     622             :  * (that does not already have phantom pipe assigned).
     623             :  *
     624             :  * e.g. For a 2 stream config where the first stream uses one
     625             :  * pipe and the second stream uses 2 pipes (i.e. pipe split),
     626             :  * this function will return true because there is 1 remaining
     627             :  * pipe which can be used as the phantom pipe for the non pipe
     628             :  * split pipe.
     629             :  *
     630             :  * @dc: current dc state
     631             :  * @context: new dc state
     632             :  *
     633             :  * Return:
     634             :  * True if there are enough free pipes to assign phantom pipes to at least one
     635             :  * stream that does not already have phantom pipes assigned. Otherwise false.
     636             :  */
     637           0 : static bool dcn32_enough_pipes_for_subvp(struct dc *dc, struct dc_state *context)
     638             : {
     639             :         unsigned int i, split_cnt, free_pipes;
     640           0 :         unsigned int min_pipe_split = dc->res_pool->pipe_count + 1; // init as max number of pipes + 1
     641           0 :         bool subvp_possible = false;
     642             : 
     643           0 :         for (i = 0; i < dc->res_pool->pipe_count; i++) {
     644           0 :                 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
     645             : 
     646             :                 // Find the minimum pipe split count for non SubVP pipes
     647           0 :                 if (pipe->stream && !pipe->top_pipe &&
     648           0 :                     pipe->stream->mall_stream_config.type == SUBVP_NONE) {
     649             :                         split_cnt = 0;
     650           0 :                         while (pipe) {
     651           0 :                                 split_cnt++;
     652           0 :                                 pipe = pipe->bottom_pipe;
     653             :                         }
     654             : 
     655           0 :                         if (split_cnt < min_pipe_split)
     656           0 :                                 min_pipe_split = split_cnt;
     657             :                 }
     658             :         }
     659             : 
     660           0 :         free_pipes = dcn32_get_num_free_pipes(dc, context);
     661             : 
     662             :         // SubVP only possible if at least one pipe is being used (i.e. free_pipes
     663             :         // should not equal to the pipe_count)
     664           0 :         if (free_pipes >= min_pipe_split && free_pipes < dc->res_pool->pipe_count)
     665           0 :                 subvp_possible = true;
     666             : 
     667           0 :         return subvp_possible;
     668             : }
     669             : 
     670             : /**
     671             :  * subvp_subvp_schedulable: Determine if SubVP + SubVP config is schedulable
     672             :  *
     673             :  * High level algorithm:
     674             :  * 1. Find longest microschedule length (in us) between the two SubVP pipes
     675             :  * 2. Check if the worst case overlap (VBLANK in middle of ACTIVE) for both
     676             :  * pipes still allows for the maximum microschedule to fit in the active
     677             :  * region for both pipes.
     678             :  *
     679             :  * @dc: current dc state
     680             :  * @context: new dc state
     681             :  *
     682             :  * Return:
     683             :  * bool - True if the SubVP + SubVP config is schedulable, false otherwise
     684             :  */
     685           0 : static bool subvp_subvp_schedulable(struct dc *dc, struct dc_state *context)
     686             : {
     687             :         struct pipe_ctx *subvp_pipes[2];
     688           0 :         struct dc_stream_state *phantom = NULL;
     689           0 :         uint32_t microschedule_lines = 0;
     690           0 :         uint32_t index = 0;
     691             :         uint32_t i;
     692           0 :         uint32_t max_microschedule_us = 0;
     693             :         int32_t vactive1_us, vactive2_us, vblank1_us, vblank2_us;
     694             : 
     695           0 :         for (i = 0; i < dc->res_pool->pipe_count; i++) {
     696           0 :                 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
     697           0 :                 uint32_t time_us = 0;
     698             : 
     699             :                 /* Loop to calculate the maximum microschedule time between the two SubVP pipes,
     700             :                  * and also to store the two main SubVP pipe pointers in subvp_pipes[2].
     701             :                  */
     702           0 :                 if (pipe->stream && pipe->plane_state && !pipe->top_pipe &&
     703           0 :                     pipe->stream->mall_stream_config.type == SUBVP_MAIN) {
     704           0 :                         phantom = pipe->stream->mall_stream_config.paired_stream;
     705           0 :                         microschedule_lines = (phantom->timing.v_total - phantom->timing.v_front_porch) +
     706           0 :                                         phantom->timing.v_addressable;
     707             : 
     708             :                         // Round up when calculating microschedule time (+ 1 at the end)
     709           0 :                         time_us = (microschedule_lines * phantom->timing.h_total) /
     710           0 :                                         (double)(phantom->timing.pix_clk_100hz * 100) * 1000000 +
     711           0 :                                                 dc->caps.subvp_prefetch_end_to_mall_start_us +
     712           0 :                                                 dc->caps.subvp_fw_processing_delay_us + 1;
     713           0 :                         if (time_us > max_microschedule_us)
     714           0 :                                 max_microschedule_us = time_us;
     715             : 
     716           0 :                         subvp_pipes[index] = pipe;
     717           0 :                         index++;
     718             : 
     719             :                         // Maximum 2 SubVP pipes
     720           0 :                         if (index == 2)
     721             :                                 break;
     722             :                 }
     723             :         }
     724           0 :         vactive1_us = ((subvp_pipes[0]->stream->timing.v_addressable * subvp_pipes[0]->stream->timing.h_total) /
     725           0 :                         (double)(subvp_pipes[0]->stream->timing.pix_clk_100hz * 100)) * 1000000;
     726           0 :         vactive2_us = ((subvp_pipes[1]->stream->timing.v_addressable * subvp_pipes[1]->stream->timing.h_total) /
     727           0 :                                 (double)(subvp_pipes[1]->stream->timing.pix_clk_100hz * 100)) * 1000000;
     728           0 :         vblank1_us = (((subvp_pipes[0]->stream->timing.v_total - subvp_pipes[0]->stream->timing.v_addressable) *
     729           0 :                         subvp_pipes[0]->stream->timing.h_total) /
     730           0 :                         (double)(subvp_pipes[0]->stream->timing.pix_clk_100hz * 100)) * 1000000;
     731           0 :         vblank2_us = (((subvp_pipes[1]->stream->timing.v_total - subvp_pipes[1]->stream->timing.v_addressable) *
     732           0 :                         subvp_pipes[1]->stream->timing.h_total) /
     733           0 :                         (double)(subvp_pipes[1]->stream->timing.pix_clk_100hz * 100)) * 1000000;
     734             : 
     735           0 :         if ((vactive1_us - vblank2_us) / 2 > max_microschedule_us &&
     736           0 :             (vactive2_us - vblank1_us) / 2 > max_microschedule_us)
     737             :                 return true;
     738             : 
     739             :         return false;
     740             : }
     741             : 
     742             : /**
     743             :  * subvp_drr_schedulable: Determine if SubVP + DRR config is schedulable
     744             :  *
     745             :  * High level algorithm:
     746             :  * 1. Get timing for SubVP pipe, phantom pipe, and DRR pipe
     747             :  * 2. Determine the frame time for the DRR display when adding required margin for MCLK switching
     748             :  * (the margin is equal to the MALL region + DRR margin (500us))
     749             :  * 3.If (SubVP Active - Prefetch > Stretched DRR frame + max(MALL region, Stretched DRR frame))
     750             :  * then report the configuration as supported
     751             :  *
     752             :  * @dc: current dc state
     753             :  * @context: new dc state
     754             :  * @drr_pipe: DRR pipe_ctx for the SubVP + DRR config
     755             :  *
     756             :  * Return:
     757             :  * bool - True if the SubVP + DRR config is schedulable, false otherwise
     758             :  */
     759           0 : static bool subvp_drr_schedulable(struct dc *dc, struct dc_state *context, struct pipe_ctx *drr_pipe)
     760             : {
     761           0 :         bool schedulable = false;
     762             :         uint32_t i;
     763           0 :         struct pipe_ctx *pipe = NULL;
     764           0 :         struct dc_crtc_timing *main_timing = NULL;
     765           0 :         struct dc_crtc_timing *phantom_timing = NULL;
     766           0 :         struct dc_crtc_timing *drr_timing = NULL;
     767           0 :         int16_t prefetch_us = 0;
     768           0 :         int16_t mall_region_us = 0;
     769           0 :         int16_t drr_frame_us = 0;       // nominal frame time
     770           0 :         int16_t subvp_active_us = 0;
     771           0 :         int16_t stretched_drr_us = 0;
     772           0 :         int16_t drr_stretched_vblank_us = 0;
     773           0 :         int16_t max_vblank_mallregion = 0;
     774             : 
     775             :         // Find SubVP pipe
     776           0 :         for (i = 0; i < dc->res_pool->pipe_count; i++) {
     777           0 :                 pipe = &context->res_ctx.pipe_ctx[i];
     778             : 
     779             :                 // We check for master pipe, but it shouldn't matter since we only need
     780             :                 // the pipe for timing info (stream should be same for any pipe splits)
     781           0 :                 if (!pipe->stream || !pipe->plane_state || pipe->top_pipe || pipe->prev_odm_pipe)
     782           0 :                         continue;
     783             : 
     784             :                 // Find the SubVP pipe
     785           0 :                 if (pipe->stream->mall_stream_config.type == SUBVP_MAIN)
     786             :                         break;
     787             :         }
     788             : 
     789           0 :         main_timing = &pipe->stream->timing;
     790           0 :         phantom_timing = &pipe->stream->mall_stream_config.paired_stream->timing;
     791           0 :         drr_timing = &drr_pipe->stream->timing;
     792           0 :         prefetch_us = (phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total /
     793           0 :                         (double)(phantom_timing->pix_clk_100hz * 100) * 1000000 +
     794           0 :                         dc->caps.subvp_prefetch_end_to_mall_start_us;
     795           0 :         subvp_active_us = main_timing->v_addressable * main_timing->h_total /
     796           0 :                         (double)(main_timing->pix_clk_100hz * 100) * 1000000;
     797           0 :         drr_frame_us = drr_timing->v_total * drr_timing->h_total /
     798           0 :                         (double)(drr_timing->pix_clk_100hz * 100) * 1000000;
     799             :         // P-State allow width and FW delays already included phantom_timing->v_addressable
     800           0 :         mall_region_us = phantom_timing->v_addressable * phantom_timing->h_total /
     801           0 :                         (double)(phantom_timing->pix_clk_100hz * 100) * 1000000;
     802           0 :         stretched_drr_us = drr_frame_us + mall_region_us + SUBVP_DRR_MARGIN_US;
     803           0 :         drr_stretched_vblank_us = (drr_timing->v_total - drr_timing->v_addressable) * drr_timing->h_total /
     804           0 :                         (double)(drr_timing->pix_clk_100hz * 100) * 1000000 + (stretched_drr_us - drr_frame_us);
     805           0 :         max_vblank_mallregion = drr_stretched_vblank_us > mall_region_us ? drr_stretched_vblank_us : mall_region_us;
     806             : 
     807             :         /* We consider SubVP + DRR schedulable if the stretched frame duration of the DRR display (i.e. the
     808             :          * highest refresh rate + margin that can support UCLK P-State switch) passes the static analysis
     809             :          * for VBLANK: (VACTIVE region of the SubVP pipe can fit the MALL prefetch, VBLANK frame time,
     810             :          * and the max of (VBLANK blanking time, MALL region)).
     811             :          */
     812           0 :         if (stretched_drr_us < (1 / (double)drr_timing->min_refresh_in_uhz) * 1000000 * 1000000 &&
     813           0 :                         subvp_active_us - prefetch_us - stretched_drr_us - max_vblank_mallregion > 0)
     814           0 :                 schedulable = true;
     815             : 
     816           0 :         return schedulable;
     817             : }
     818             : 
     819             : 
     820             : /**
     821             :  * subvp_vblank_schedulable: Determine if SubVP + VBLANK config is schedulable
     822             :  *
     823             :  * High level algorithm:
     824             :  * 1. Get timing for SubVP pipe, phantom pipe, and VBLANK pipe
     825             :  * 2. If (SubVP Active - Prefetch > Vblank Frame Time + max(MALL region, Vblank blanking time))
     826             :  * then report the configuration as supported
     827             :  * 3. If the VBLANK display is DRR, then take the DRR static schedulability path
     828             :  *
     829             :  * @dc: current dc state
     830             :  * @context: new dc state
     831             :  *
     832             :  * Return:
     833             :  * bool - True if the SubVP + VBLANK/DRR config is schedulable, false otherwise
     834             :  */
     835           0 : static bool subvp_vblank_schedulable(struct dc *dc, struct dc_state *context)
     836             : {
     837           0 :         struct pipe_ctx *pipe = NULL;
     838           0 :         struct pipe_ctx *subvp_pipe = NULL;
     839           0 :         bool found = false;
     840           0 :         bool schedulable = false;
     841           0 :         uint32_t i = 0;
     842           0 :         uint8_t vblank_index = 0;
     843           0 :         uint16_t prefetch_us = 0;
     844           0 :         uint16_t mall_region_us = 0;
     845           0 :         uint16_t vblank_frame_us = 0;
     846           0 :         uint16_t subvp_active_us = 0;
     847           0 :         uint16_t vblank_blank_us = 0;
     848           0 :         uint16_t max_vblank_mallregion = 0;
     849           0 :         struct dc_crtc_timing *main_timing = NULL;
     850           0 :         struct dc_crtc_timing *phantom_timing = NULL;
     851           0 :         struct dc_crtc_timing *vblank_timing = NULL;
     852             : 
     853             :         /* For SubVP + VBLANK/DRR cases, we assume there can only be
     854             :          * a single VBLANK/DRR display. If DML outputs SubVP + VBLANK
     855             :          * is supported, it is either a single VBLANK case or two VBLANK
     856             :          * displays which are synchronized (in which case they have identical
     857             :          * timings).
     858             :          */
     859           0 :         for (i = 0; i < dc->res_pool->pipe_count; i++) {
     860           0 :                 pipe = &context->res_ctx.pipe_ctx[i];
     861             : 
     862             :                 // We check for master pipe, but it shouldn't matter since we only need
     863             :                 // the pipe for timing info (stream should be same for any pipe splits)
     864           0 :                 if (!pipe->stream || !pipe->plane_state || pipe->top_pipe || pipe->prev_odm_pipe)
     865           0 :                         continue;
     866             : 
     867           0 :                 if (!found && pipe->stream->mall_stream_config.type == SUBVP_NONE) {
     868             :                         // Found pipe which is not SubVP or Phantom (i.e. the VBLANK pipe).
     869           0 :                         vblank_index = i;
     870           0 :                         found = true;
     871             :                 }
     872             : 
     873           0 :                 if (!subvp_pipe && pipe->stream->mall_stream_config.type == SUBVP_MAIN)
     874           0 :                         subvp_pipe = pipe;
     875             :         }
     876             :         // Use ignore_msa_timing_param flag to identify as DRR
     877           0 :         if (found && context->res_ctx.pipe_ctx[vblank_index].stream->ignore_msa_timing_param) {
     878             :                 // SUBVP + DRR case
     879           0 :                 schedulable = subvp_drr_schedulable(dc, context, &context->res_ctx.pipe_ctx[vblank_index]);
     880           0 :         } else if (found) {
     881           0 :                 main_timing = &subvp_pipe->stream->timing;
     882           0 :                 phantom_timing = &subvp_pipe->stream->mall_stream_config.paired_stream->timing;
     883           0 :                 vblank_timing = &context->res_ctx.pipe_ctx[vblank_index].stream->timing;
     884             :                 // Prefetch time is equal to VACTIVE + BP + VSYNC of the phantom pipe
     885             :                 // Also include the prefetch end to mallstart delay time
     886           0 :                 prefetch_us = (phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total /
     887           0 :                                 (double)(phantom_timing->pix_clk_100hz * 100) * 1000000 +
     888           0 :                                 dc->caps.subvp_prefetch_end_to_mall_start_us;
     889             :                 // P-State allow width and FW delays already included phantom_timing->v_addressable
     890           0 :                 mall_region_us = phantom_timing->v_addressable * phantom_timing->h_total /
     891           0 :                                 (double)(phantom_timing->pix_clk_100hz * 100) * 1000000;
     892           0 :                 vblank_frame_us = vblank_timing->v_total * vblank_timing->h_total /
     893           0 :                                 (double)(vblank_timing->pix_clk_100hz * 100) * 1000000;
     894           0 :                 vblank_blank_us =  (vblank_timing->v_total - vblank_timing->v_addressable) * vblank_timing->h_total /
     895           0 :                                 (double)(vblank_timing->pix_clk_100hz * 100) * 1000000;
     896           0 :                 subvp_active_us = main_timing->v_addressable * main_timing->h_total /
     897           0 :                                 (double)(main_timing->pix_clk_100hz * 100) * 1000000;
     898           0 :                 max_vblank_mallregion = vblank_blank_us > mall_region_us ? vblank_blank_us : mall_region_us;
     899             : 
     900             :                 // Schedulable if VACTIVE region of the SubVP pipe can fit the MALL prefetch, VBLANK frame time,
     901             :                 // and the max of (VBLANK blanking time, MALL region)
     902             :                 // TODO: Possibly add some margin (i.e. the below conditions should be [...] > X instead of [...] > 0)
     903           0 :                 if (subvp_active_us - prefetch_us - vblank_frame_us - max_vblank_mallregion > 0)
     904           0 :                         schedulable = true;
     905             :         }
     906           0 :         return schedulable;
     907             : }
     908             : 
     909             : /**
     910             :  * subvp_validate_static_schedulability: Check which SubVP case is calculated and handle
     911             :  * static analysis based on the case.
     912             :  *
     913             :  * Three cases:
     914             :  * 1. SubVP + SubVP
     915             :  * 2. SubVP + VBLANK (DRR checked internally)
     916             :  * 3. SubVP + VACTIVE (currently unsupported)
     917             :  *
     918             :  * @dc: current dc state
     919             :  * @context: new dc state
     920             :  * @vlevel: Voltage level calculated by DML
     921             :  *
     922             :  * Return:
     923             :  * bool - True if statically schedulable, false otherwise
     924             :  */
     925           0 : static bool subvp_validate_static_schedulability(struct dc *dc,
     926             :                                 struct dc_state *context,
     927             :                                 int vlevel)
     928             : {
     929           0 :         bool schedulable = true;        // true by default for single display case
     930           0 :         struct vba_vars_st *vba = &context->bw_ctx.dml.vba;
     931             :         uint32_t i, pipe_idx;
     932           0 :         uint8_t subvp_count = 0;
     933           0 :         uint8_t vactive_count = 0;
     934             : 
     935           0 :         for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
     936           0 :                 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
     937             : 
     938           0 :                 if (!pipe->stream)
     939           0 :                         continue;
     940             : 
     941           0 :                 if (pipe->plane_state && !pipe->top_pipe &&
     942           0 :                                 pipe->stream->mall_stream_config.type == SUBVP_MAIN)
     943           0 :                         subvp_count++;
     944             : 
     945             :                 // Count how many planes that aren't SubVP/phantom are capable of VACTIVE
     946             :                 // switching (SubVP + VACTIVE unsupported). In situations where we force
     947             :                 // SubVP for a VACTIVE plane, we don't want to increment the vactive_count.
     948           0 :                 if (vba->ActiveDRAMClockChangeLatencyMargin[vba->pipe_plane[pipe_idx]] > 0 &&
     949           0 :                     pipe->stream->mall_stream_config.type == SUBVP_NONE) {
     950           0 :                         vactive_count++;
     951             :                 }
     952           0 :                 pipe_idx++;
     953             :         }
     954             : 
     955           0 :         if (subvp_count == 2) {
     956             :                 // Static schedulability check for SubVP + SubVP case
     957           0 :                 schedulable = subvp_subvp_schedulable(dc, context);
     958           0 :         } else if (vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_vblank_w_mall_sub_vp) {
     959             :                 // Static schedulability check for SubVP + VBLANK case. Also handle the case where
     960             :                 // DML outputs SubVP + VBLANK + VACTIVE (DML will report as SubVP + VBLANK)
     961           0 :                 if (vactive_count > 0)
     962             :                         schedulable = false;
     963             :                 else
     964           0 :                         schedulable = subvp_vblank_schedulable(dc, context);
     965           0 :         } else if (vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_vactive_w_mall_sub_vp &&
     966             :                         vactive_count > 0) {
     967             :                 // For single display SubVP cases, DML will output dm_dram_clock_change_vactive_w_mall_sub_vp by default.
     968             :                 // We tell the difference between SubVP vs. SubVP + VACTIVE by checking the vactive_count.
     969             :                 // SubVP + VACTIVE currently unsupported
     970           0 :                 schedulable = false;
     971             :         }
     972           0 :         return schedulable;
     973             : }
     974             : 
     975           0 : static void dcn32_full_validate_bw_helper(struct dc *dc,
     976             :                                    struct dc_state *context,
     977             :                                    display_e2e_pipe_params_st *pipes,
     978             :                                    int *vlevel,
     979             :                                    int *split,
     980             :                                    bool *merge,
     981             :                                    int *pipe_cnt)
     982             : {
     983           0 :         struct vba_vars_st *vba = &context->bw_ctx.dml.vba;
     984           0 :         unsigned int dc_pipe_idx = 0;
     985           0 :         bool found_supported_config = false;
     986           0 :         struct pipe_ctx *pipe = NULL;
     987           0 :         uint32_t non_subvp_pipes = 0;
     988           0 :         bool drr_pipe_found = false;
     989           0 :         uint32_t drr_pipe_index = 0;
     990           0 :         uint32_t i = 0;
     991             : 
     992           0 :         dc_assert_fp_enabled();
     993             : 
     994             :         /*
     995             :          * DML favors voltage over p-state, but we're more interested in
     996             :          * supporting p-state over voltage. We can't support p-state in
     997             :          * prefetch mode > 0 so try capping the prefetch mode to start.
     998             :          * Override present for testing.
     999             :          */
    1000           0 :         if (dc->debug.dml_disallow_alternate_prefetch_modes)
    1001           0 :                 context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =
    1002             :                         dm_prefetch_support_uclk_fclk_and_stutter;
    1003             :         else
    1004           0 :                 context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =
    1005             :                         dm_prefetch_support_uclk_fclk_and_stutter_if_possible;
    1006             : 
    1007           0 :         *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt);
    1008             :         /* This may adjust vlevel and maxMpcComb */
    1009           0 :         if (*vlevel < context->bw_ctx.dml.soc.num_states) {
    1010           0 :                 *vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, *vlevel, split, merge);
    1011           0 :                 vba->VoltageLevel = *vlevel;
    1012             :         }
    1013             : 
    1014             :         /* Conditions for setting up phantom pipes for SubVP:
    1015             :          * 1. Not force disable SubVP
    1016             :          * 2. Full update (i.e. !fast_validate)
    1017             :          * 3. Enough pipes are available to support SubVP (TODO: Which pipes will use VACTIVE / VBLANK / SUBVP?)
    1018             :          * 4. Display configuration passes validation
    1019             :          * 5. (Config doesn't support MCLK in VACTIVE/VBLANK || dc->debug.force_subvp_mclk_switch)
    1020             :          */
    1021           0 :         if (!dc->debug.force_disable_subvp && dcn32_all_pipes_have_stream_and_plane(dc, context) &&
    1022           0 :             !dcn32_mpo_in_use(context) && (*vlevel == context->bw_ctx.dml.soc.num_states ||
    1023           0 :             vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] == dm_dram_clock_change_unsupported ||
    1024           0 :             dc->debug.force_subvp_mclk_switch)) {
    1025             : 
    1026           0 :                 dcn32_merge_pipes_for_subvp(dc, context);
    1027             :                 // to re-initialize viewport after the pipe merge
    1028           0 :                 for (i = 0; i < dc->res_pool->pipe_count; i++) {
    1029           0 :                         struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
    1030             : 
    1031           0 :                         if (!pipe_ctx->plane_state || !pipe_ctx->stream)
    1032           0 :                                 continue;
    1033             : 
    1034           0 :                         resource_build_scaling_params(pipe_ctx);
    1035             :                 }
    1036             : 
    1037           0 :                 while (!found_supported_config && dcn32_enough_pipes_for_subvp(dc, context) &&
    1038           0 :                         dcn32_assign_subvp_pipe(dc, context, &dc_pipe_idx)) {
    1039             :                         /* For the case where *vlevel = num_states, bandwidth validation has failed for this config.
    1040             :                          * Adding phantom pipes won't change the validation result, so change the DML input param
    1041             :                          * for P-State support before adding phantom pipes and recalculating the DML result.
    1042             :                          * However, this case is only applicable for SubVP + DRR cases because the prefetch mode
    1043             :                          * will not allow for switch in VBLANK. The DRR display must have it's VBLANK stretched
    1044             :                          * enough to support MCLK switching.
    1045             :                          */
    1046           0 :                         if (*vlevel == context->bw_ctx.dml.soc.num_states &&
    1047           0 :                                 context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final ==
    1048             :                                         dm_prefetch_support_uclk_fclk_and_stutter) {
    1049           0 :                                 context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =
    1050             :                                                                 dm_prefetch_support_stutter;
    1051             :                                 /* There are params (such as FabricClock) that need to be recalculated
    1052             :                                  * after validation fails (otherwise it will be 0). Calculation for
    1053             :                                  * phantom vactive requires call into DML, so we must ensure all the
    1054             :                                  * vba params are valid otherwise we'll get incorrect phantom vactive.
    1055             :                                  */
    1056           0 :                                 *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt);
    1057             :                         }
    1058             : 
    1059           0 :                         dc->res_pool->funcs->add_phantom_pipes(dc, context, pipes, *pipe_cnt, dc_pipe_idx);
    1060             : 
    1061           0 :                         *pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, false);
    1062             :                         // Populate dppclk to trigger a recalculate in dml_get_voltage_level
    1063             :                         // so the phantom pipe DLG params can be assigned correctly.
    1064           0 :                         pipes[0].clks_cfg.dppclk_mhz = get_dppclk_calculated(&context->bw_ctx.dml, pipes, *pipe_cnt, 0);
    1065           0 :                         *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt);
    1066             : 
    1067           0 :                         if (*vlevel < context->bw_ctx.dml.soc.num_states &&
    1068           0 :                             vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] != dm_dram_clock_change_unsupported
    1069           0 :                             && subvp_validate_static_schedulability(dc, context, *vlevel)) {
    1070             :                                 found_supported_config = true;
    1071           0 :                         } else if (*vlevel < context->bw_ctx.dml.soc.num_states &&
    1072           0 :                                         vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] == dm_dram_clock_change_unsupported) {
    1073             :                                 /* Case where 1 SubVP is added, and DML reports MCLK unsupported. This handles
    1074             :                                  * the case for SubVP + DRR, where the DRR display does not support MCLK switch
    1075             :                                  * at it's native refresh rate / timing.
    1076             :                                  */
    1077           0 :                                 for (i = 0; i < dc->res_pool->pipe_count; i++) {
    1078           0 :                                         pipe = &context->res_ctx.pipe_ctx[i];
    1079           0 :                                         if (pipe->stream && pipe->plane_state && !pipe->top_pipe &&
    1080           0 :                                             pipe->stream->mall_stream_config.type == SUBVP_NONE) {
    1081           0 :                                                 non_subvp_pipes++;
    1082             :                                                 // Use ignore_msa_timing_param flag to identify as DRR
    1083           0 :                                                 if (pipe->stream->ignore_msa_timing_param) {
    1084           0 :                                                         drr_pipe_found = true;
    1085           0 :                                                         drr_pipe_index = i;
    1086             :                                                 }
    1087             :                                         }
    1088             :                                 }
    1089             :                                 // If there is only 1 remaining non SubVP pipe that is DRR, check static
    1090             :                                 // schedulability for SubVP + DRR.
    1091           0 :                                 if (non_subvp_pipes == 1 && drr_pipe_found) {
    1092           0 :                                         found_supported_config = subvp_drr_schedulable(dc, context,
    1093             :                                                                                        &context->res_ctx.pipe_ctx[drr_pipe_index]);
    1094             :                                 }
    1095             :                         }
    1096             :                 }
    1097             : 
    1098             :                 // If SubVP pipe config is unsupported (or cannot be used for UCLK switching)
    1099             :                 // remove phantom pipes and repopulate dml pipes
    1100           0 :                 if (!found_supported_config) {
    1101           0 :                         dc->res_pool->funcs->remove_phantom_pipes(dc, context);
    1102           0 :                         vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] = dm_dram_clock_change_unsupported;
    1103           0 :                         *pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, false);
    1104             : 
    1105           0 :                         *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt);
    1106             :                         /* This may adjust vlevel and maxMpcComb */
    1107           0 :                         if (*vlevel < context->bw_ctx.dml.soc.num_states) {
    1108           0 :                                 *vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, *vlevel, split, merge);
    1109           0 :                                 vba->VoltageLevel = *vlevel;
    1110             :                         }
    1111             :                 } else {
    1112             :                         // only call dcn20_validate_apply_pipe_split_flags if we found a supported config
    1113           0 :                         memset(split, 0, MAX_PIPES * sizeof(int));
    1114           0 :                         memset(merge, 0, MAX_PIPES * sizeof(bool));
    1115           0 :                         *vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, *vlevel, split, merge);
    1116           0 :                         vba->VoltageLevel = *vlevel;
    1117             : 
    1118             :                         // Most populate phantom DLG params before programming hardware / timing for phantom pipe
    1119           0 :                         DC_FP_START();
    1120           0 :                         dcn32_helper_populate_phantom_dlg_params(dc, context, pipes, *pipe_cnt);
    1121           0 :                         DC_FP_END();
    1122             : 
    1123             :                         // Note: We can't apply the phantom pipes to hardware at this time. We have to wait
    1124             :                         // until driver has acquired the DMCUB lock to do it safely.
    1125             :                 }
    1126             :         }
    1127           0 : }
    1128             : 
    1129           0 : static bool is_dtbclk_required(struct dc *dc, struct dc_state *context)
    1130             : {
    1131             :         int i;
    1132             : 
    1133           0 :         for (i = 0; i < dc->res_pool->pipe_count; i++) {
    1134           0 :                 if (!context->res_ctx.pipe_ctx[i].stream)
    1135           0 :                         continue;
    1136           0 :                 if (is_dp_128b_132b_signal(&context->res_ctx.pipe_ctx[i]))
    1137             :                         return true;
    1138             :         }
    1139             :         return false;
    1140             : }
    1141             : 
    1142           0 : static void dcn32_calculate_dlg_params(struct dc *dc, struct dc_state *context,
    1143             :                                        display_e2e_pipe_params_st *pipes,
    1144             :                                        int pipe_cnt, int vlevel)
    1145             : {
    1146             :         int i, pipe_idx;
    1147           0 :         bool usr_retraining_support = false;
    1148           0 :         bool unbounded_req_enabled = false;
    1149             : 
    1150           0 :         dc_assert_fp_enabled();
    1151             : 
    1152             :         /* Writeback MCIF_WB arbitration parameters */
    1153           0 :         dc->res_pool->funcs->set_mcif_arb_params(dc, context, pipes, pipe_cnt);
    1154             : 
    1155           0 :         context->bw_ctx.bw.dcn.clk.dispclk_khz = context->bw_ctx.dml.vba.DISPCLK * 1000;
    1156           0 :         context->bw_ctx.bw.dcn.clk.dcfclk_khz = context->bw_ctx.dml.vba.DCFCLK * 1000;
    1157           0 :         context->bw_ctx.bw.dcn.clk.socclk_khz = context->bw_ctx.dml.vba.SOCCLK * 1000;
    1158           0 :         context->bw_ctx.bw.dcn.clk.dramclk_khz = context->bw_ctx.dml.vba.DRAMSpeed * 1000 / 16;
    1159           0 :         context->bw_ctx.bw.dcn.clk.dcfclk_deep_sleep_khz = context->bw_ctx.dml.vba.DCFCLKDeepSleep * 1000;
    1160           0 :         context->bw_ctx.bw.dcn.clk.fclk_khz = context->bw_ctx.dml.vba.FabricClock * 1000;
    1161           0 :         context->bw_ctx.bw.dcn.clk.p_state_change_support =
    1162           0 :                         context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb]
    1163           0 :                                         != dm_dram_clock_change_unsupported;
    1164           0 :         context->bw_ctx.bw.dcn.clk.num_ways = dcn32_helper_calculate_num_ways_for_subvp(dc, context);
    1165             : 
    1166           0 :         context->bw_ctx.bw.dcn.clk.dppclk_khz = 0;
    1167           0 :         context->bw_ctx.bw.dcn.clk.dtbclk_en = is_dtbclk_required(dc, context);
    1168           0 :         context->bw_ctx.bw.dcn.clk.ref_dtbclk_khz = context->bw_ctx.dml.vba.DTBCLKPerState[vlevel] * 1000;
    1169           0 :         if (context->bw_ctx.dml.vba.FCLKChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] == dm_fclock_change_unsupported)
    1170           0 :                 context->bw_ctx.bw.dcn.clk.fclk_p_state_change_support = false;
    1171             :         else
    1172           0 :                 context->bw_ctx.bw.dcn.clk.fclk_p_state_change_support = true;
    1173             : 
    1174           0 :         usr_retraining_support = context->bw_ctx.dml.vba.USRRetrainingSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb];
    1175           0 :         ASSERT(usr_retraining_support);
    1176             : 
    1177           0 :         if (context->bw_ctx.bw.dcn.clk.dispclk_khz < dc->debug.min_disp_clk_khz)
    1178           0 :                 context->bw_ctx.bw.dcn.clk.dispclk_khz = dc->debug.min_disp_clk_khz;
    1179             : 
    1180           0 :         unbounded_req_enabled = get_unbounded_request_enabled(&context->bw_ctx.dml, pipes, pipe_cnt);
    1181             : 
    1182           0 :         if (unbounded_req_enabled && pipe_cnt > 1) {
    1183             :                 // Unbounded requesting should not ever be used when more than 1 pipe is enabled.
    1184           0 :                 ASSERT(false);
    1185             :                 unbounded_req_enabled = false;
    1186             :         }
    1187             : 
    1188           0 :         for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
    1189           0 :                 if (!context->res_ctx.pipe_ctx[i].stream)
    1190           0 :                         continue;
    1191           0 :                 pipes[pipe_idx].pipe.dest.vstartup_start = get_vstartup(&context->bw_ctx.dml, pipes, pipe_cnt,
    1192             :                                 pipe_idx);
    1193           0 :                 pipes[pipe_idx].pipe.dest.vupdate_offset = get_vupdate_offset(&context->bw_ctx.dml, pipes, pipe_cnt,
    1194             :                                 pipe_idx);
    1195           0 :                 pipes[pipe_idx].pipe.dest.vupdate_width = get_vupdate_width(&context->bw_ctx.dml, pipes, pipe_cnt,
    1196             :                                 pipe_idx);
    1197           0 :                 pipes[pipe_idx].pipe.dest.vready_offset = get_vready_offset(&context->bw_ctx.dml, pipes, pipe_cnt,
    1198             :                                 pipe_idx);
    1199             : 
    1200           0 :                 if (context->res_ctx.pipe_ctx[i].stream->mall_stream_config.type == SUBVP_PHANTOM) {
    1201             :                         // Phantom pipe requires that DET_SIZE = 0 and no unbounded requests
    1202           0 :                         context->res_ctx.pipe_ctx[i].det_buffer_size_kb = 0;
    1203           0 :                         context->res_ctx.pipe_ctx[i].unbounded_req = false;
    1204             :                 } else {
    1205           0 :                         context->res_ctx.pipe_ctx[i].det_buffer_size_kb = get_det_buffer_size_kbytes(&context->bw_ctx.dml, pipes, pipe_cnt,
    1206             :                                                         pipe_idx);
    1207           0 :                         context->res_ctx.pipe_ctx[i].unbounded_req = unbounded_req_enabled;
    1208             :                 }
    1209             : 
    1210           0 :                 if (context->bw_ctx.bw.dcn.clk.dppclk_khz < pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000)
    1211           0 :                         context->bw_ctx.bw.dcn.clk.dppclk_khz = pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000;
    1212           0 :                 context->res_ctx.pipe_ctx[i].plane_res.bw.dppclk_khz = pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000;
    1213           0 :                 context->res_ctx.pipe_ctx[i].pipe_dlg_param = pipes[pipe_idx].pipe.dest;
    1214           0 :                 pipe_idx++;
    1215             :         }
    1216             :         /*save a original dppclock copy*/
    1217           0 :         context->bw_ctx.bw.dcn.clk.bw_dppclk_khz = context->bw_ctx.bw.dcn.clk.dppclk_khz;
    1218           0 :         context->bw_ctx.bw.dcn.clk.bw_dispclk_khz = context->bw_ctx.bw.dcn.clk.dispclk_khz;
    1219           0 :         context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz = context->bw_ctx.dml.soc.clock_limits[vlevel].dppclk_mhz
    1220           0 :                         * 1000;
    1221           0 :         context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz = context->bw_ctx.dml.soc.clock_limits[vlevel].dispclk_mhz
    1222           0 :                         * 1000;
    1223             : 
    1224           0 :         context->bw_ctx.bw.dcn.compbuf_size_kb = context->bw_ctx.dml.ip.config_return_buffer_size_in_kbytes;
    1225             : 
    1226           0 :         for (i = 0; i < dc->res_pool->pipe_count; i++) {
    1227           0 :                 if (context->res_ctx.pipe_ctx[i].stream)
    1228           0 :                         context->bw_ctx.bw.dcn.compbuf_size_kb -= context->res_ctx.pipe_ctx[i].det_buffer_size_kb;
    1229             :         }
    1230             : 
    1231           0 :         for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
    1232             : 
    1233           0 :                 if (!context->res_ctx.pipe_ctx[i].stream)
    1234           0 :                         continue;
    1235             : 
    1236           0 :                 context->bw_ctx.dml.funcs.rq_dlg_get_dlg_reg_v2(&context->bw_ctx.dml,
    1237           0 :                                 &context->res_ctx.pipe_ctx[i].dlg_regs, &context->res_ctx.pipe_ctx[i].ttu_regs, pipes,
    1238             :                                 pipe_cnt, pipe_idx);
    1239             : 
    1240           0 :                 context->bw_ctx.dml.funcs.rq_dlg_get_rq_reg_v2(&context->res_ctx.pipe_ctx[i].rq_regs,
    1241             :                                 &context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);
    1242           0 :                 pipe_idx++;
    1243             :         }
    1244           0 : }
    1245             : 
    1246           0 : static struct pipe_ctx *dcn32_find_split_pipe(
    1247             :                 struct dc *dc,
    1248             :                 struct dc_state *context,
    1249             :                 int old_index)
    1250             : {
    1251           0 :         struct pipe_ctx *pipe = NULL;
    1252             :         int i;
    1253             : 
    1254           0 :         if (old_index >= 0 && context->res_ctx.pipe_ctx[old_index].stream == NULL) {
    1255           0 :                 pipe = &context->res_ctx.pipe_ctx[old_index];
    1256           0 :                 pipe->pipe_idx = old_index;
    1257             :         }
    1258             : 
    1259           0 :         if (!pipe)
    1260           0 :                 for (i = dc->res_pool->pipe_count - 1; i >= 0; i--) {
    1261           0 :                         if (dc->current_state->res_ctx.pipe_ctx[i].top_pipe == NULL
    1262           0 :                                         && dc->current_state->res_ctx.pipe_ctx[i].prev_odm_pipe == NULL) {
    1263           0 :                                 if (context->res_ctx.pipe_ctx[i].stream == NULL) {
    1264           0 :                                         pipe = &context->res_ctx.pipe_ctx[i];
    1265           0 :                                         pipe->pipe_idx = i;
    1266             :                                         break;
    1267             :                                 }
    1268             :                         }
    1269             :                 }
    1270             : 
    1271             :         /*
    1272             :          * May need to fix pipes getting tossed from 1 opp to another on flip
    1273             :          * Add for debugging transient underflow during topology updates:
    1274             :          * ASSERT(pipe);
    1275             :          */
    1276           0 :         if (!pipe)
    1277           0 :                 for (i = dc->res_pool->pipe_count - 1; i >= 0; i--) {
    1278           0 :                         if (context->res_ctx.pipe_ctx[i].stream == NULL) {
    1279           0 :                                 pipe = &context->res_ctx.pipe_ctx[i];
    1280           0 :                                 pipe->pipe_idx = i;
    1281             :                                 break;
    1282             :                         }
    1283             :                 }
    1284             : 
    1285           0 :         return pipe;
    1286             : }
    1287             : 
    1288           0 : static bool dcn32_split_stream_for_mpc_or_odm(
    1289             :                 const struct dc *dc,
    1290             :                 struct resource_context *res_ctx,
    1291             :                 struct pipe_ctx *pri_pipe,
    1292             :                 struct pipe_ctx *sec_pipe,
    1293             :                 bool odm)
    1294             : {
    1295           0 :         int pipe_idx = sec_pipe->pipe_idx;
    1296           0 :         const struct resource_pool *pool = dc->res_pool;
    1297             : 
    1298             :         DC_LOGGER_INIT(dc->ctx->logger);
    1299             : 
    1300           0 :         if (odm && pri_pipe->plane_state) {
    1301             :                 /* ODM + window MPO, where MPO window is on left half only */
    1302           0 :                 if (pri_pipe->plane_state->clip_rect.x + pri_pipe->plane_state->clip_rect.width <=
    1303           0 :                                 pri_pipe->stream->src.x + pri_pipe->stream->src.width/2) {
    1304             : 
    1305             :                         DC_LOG_SCALER("%s - ODM + window MPO(left). pri_pipe:%d\n",
    1306             :                                         __func__,
    1307             :                                         pri_pipe->pipe_idx);
    1308             :                         return true;
    1309             :                 }
    1310             : 
    1311             :                 /* ODM + window MPO, where MPO window is on right half only */
    1312           0 :                 if (pri_pipe->plane_state->clip_rect.x >= pri_pipe->stream->src.x +  pri_pipe->stream->src.width/2) {
    1313             : 
    1314             :                         DC_LOG_SCALER("%s - ODM + window MPO(right). pri_pipe:%d\n",
    1315             :                                         __func__,
    1316             :                                         pri_pipe->pipe_idx);
    1317             :                         return true;
    1318             :                 }
    1319             :         }
    1320             : 
    1321           0 :         *sec_pipe = *pri_pipe;
    1322             : 
    1323           0 :         sec_pipe->pipe_idx = pipe_idx;
    1324           0 :         sec_pipe->plane_res.mi = pool->mis[pipe_idx];
    1325           0 :         sec_pipe->plane_res.hubp = pool->hubps[pipe_idx];
    1326           0 :         sec_pipe->plane_res.ipp = pool->ipps[pipe_idx];
    1327           0 :         sec_pipe->plane_res.xfm = pool->transforms[pipe_idx];
    1328           0 :         sec_pipe->plane_res.dpp = pool->dpps[pipe_idx];
    1329           0 :         sec_pipe->plane_res.mpcc_inst = pool->dpps[pipe_idx]->inst;
    1330           0 :         sec_pipe->stream_res.dsc = NULL;
    1331           0 :         if (odm) {
    1332           0 :                 if (pri_pipe->next_odm_pipe) {
    1333           0 :                         ASSERT(pri_pipe->next_odm_pipe != sec_pipe);
    1334           0 :                         sec_pipe->next_odm_pipe = pri_pipe->next_odm_pipe;
    1335           0 :                         sec_pipe->next_odm_pipe->prev_odm_pipe = sec_pipe;
    1336             :                 }
    1337           0 :                 if (pri_pipe->top_pipe && pri_pipe->top_pipe->next_odm_pipe) {
    1338           0 :                         pri_pipe->top_pipe->next_odm_pipe->bottom_pipe = sec_pipe;
    1339           0 :                         sec_pipe->top_pipe = pri_pipe->top_pipe->next_odm_pipe;
    1340             :                 }
    1341           0 :                 if (pri_pipe->bottom_pipe && pri_pipe->bottom_pipe->next_odm_pipe) {
    1342           0 :                         pri_pipe->bottom_pipe->next_odm_pipe->top_pipe = sec_pipe;
    1343           0 :                         sec_pipe->bottom_pipe = pri_pipe->bottom_pipe->next_odm_pipe;
    1344             :                 }
    1345           0 :                 pri_pipe->next_odm_pipe = sec_pipe;
    1346           0 :                 sec_pipe->prev_odm_pipe = pri_pipe;
    1347           0 :                 ASSERT(sec_pipe->top_pipe == NULL);
    1348             : 
    1349           0 :                 if (!sec_pipe->top_pipe)
    1350           0 :                         sec_pipe->stream_res.opp = pool->opps[pipe_idx];
    1351             :                 else
    1352           0 :                         sec_pipe->stream_res.opp = sec_pipe->top_pipe->stream_res.opp;
    1353           0 :                 if (sec_pipe->stream->timing.flags.DSC == 1) {
    1354           0 :                         dcn20_acquire_dsc(dc, res_ctx, &sec_pipe->stream_res.dsc, pipe_idx);
    1355           0 :                         ASSERT(sec_pipe->stream_res.dsc);
    1356           0 :                         if (sec_pipe->stream_res.dsc == NULL)
    1357             :                                 return false;
    1358             :                 }
    1359             :         } else {
    1360           0 :                 if (pri_pipe->bottom_pipe) {
    1361           0 :                         ASSERT(pri_pipe->bottom_pipe != sec_pipe);
    1362           0 :                         sec_pipe->bottom_pipe = pri_pipe->bottom_pipe;
    1363           0 :                         sec_pipe->bottom_pipe->top_pipe = sec_pipe;
    1364             :                 }
    1365           0 :                 pri_pipe->bottom_pipe = sec_pipe;
    1366           0 :                 sec_pipe->top_pipe = pri_pipe;
    1367             : 
    1368           0 :                 ASSERT(pri_pipe->plane_state);
    1369             :         }
    1370             : 
    1371             :         return true;
    1372             : }
    1373             : 
    1374           0 : bool dcn32_internal_validate_bw(struct dc *dc,
    1375             :                                 struct dc_state *context,
    1376             :                                 display_e2e_pipe_params_st *pipes,
    1377             :                                 int *pipe_cnt_out,
    1378             :                                 int *vlevel_out,
    1379             :                                 bool fast_validate)
    1380             : {
    1381           0 :         bool out = false;
    1382           0 :         bool repopulate_pipes = false;
    1383           0 :         int split[MAX_PIPES] = { 0 };
    1384           0 :         bool merge[MAX_PIPES] = { false };
    1385           0 :         bool newly_split[MAX_PIPES] = { false };
    1386             :         int pipe_cnt, i, pipe_idx;
    1387           0 :         int vlevel = context->bw_ctx.dml.soc.num_states;
    1388           0 :         struct vba_vars_st *vba = &context->bw_ctx.dml.vba;
    1389             : 
    1390           0 :         dc_assert_fp_enabled();
    1391             : 
    1392           0 :         ASSERT(pipes);
    1393           0 :         if (!pipes)
    1394             :                 return false;
    1395             : 
    1396             :         // For each full update, remove all existing phantom pipes first
    1397           0 :         dc->res_pool->funcs->remove_phantom_pipes(dc, context);
    1398             : 
    1399           0 :         dc->res_pool->funcs->update_soc_for_wm_a(dc, context);
    1400             : 
    1401           0 :         pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, fast_validate);
    1402             : 
    1403           0 :         if (!pipe_cnt) {
    1404             :                 out = true;
    1405             :                 goto validate_out;
    1406             :         }
    1407             : 
    1408           0 :         dml_log_pipe_params(&context->bw_ctx.dml, pipes, pipe_cnt);
    1409             : 
    1410           0 :         if (!fast_validate) {
    1411           0 :                 DC_FP_START();
    1412           0 :                 dcn32_full_validate_bw_helper(dc, context, pipes, &vlevel, split, merge, &pipe_cnt);
    1413           0 :                 DC_FP_END();
    1414             :         }
    1415             : 
    1416           0 :         if (fast_validate ||
    1417           0 :                         (dc->debug.dml_disallow_alternate_prefetch_modes &&
    1418           0 :                         (vlevel == context->bw_ctx.dml.soc.num_states ||
    1419           0 :                                 vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_unsupported))) {
    1420             :                 /*
    1421             :                  * If dml_disallow_alternate_prefetch_modes is false, then we have already
    1422             :                  * tried alternate prefetch modes during full validation.
    1423             :                  *
    1424             :                  * If mode is unsupported or there is no p-state support, then
    1425             :                  * fall back to favouring voltage.
    1426             :                  *
    1427             :                  * If Prefetch mode 0 failed for this config, or passed with Max UCLK, then try
    1428             :                  * to support with Prefetch mode 1 (dm_prefetch_support_fclk_and_stutter == 2)
    1429             :                  */
    1430           0 :                 context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =
    1431             :                         dm_prefetch_support_fclk_and_stutter;
    1432             : 
    1433           0 :                 vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, pipe_cnt);
    1434             : 
    1435             :                 /* Last attempt with Prefetch mode 2 (dm_prefetch_support_stutter == 3) */
    1436           0 :                 if (vlevel == context->bw_ctx.dml.soc.num_states) {
    1437           0 :                         context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final =
    1438             :                                 dm_prefetch_support_stutter;
    1439           0 :                         vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, pipe_cnt);
    1440             :                 }
    1441             : 
    1442           0 :                 if (vlevel < context->bw_ctx.dml.soc.num_states) {
    1443           0 :                         memset(split, 0, sizeof(split));
    1444           0 :                         memset(merge, 0, sizeof(merge));
    1445           0 :                         vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, vlevel, split, merge);
    1446             :                         // dcn20_validate_apply_pipe_split_flags can modify voltage level outside of DML
    1447           0 :                         vba->VoltageLevel = vlevel;
    1448             :                 }
    1449             :         }
    1450             : 
    1451           0 :         dml_log_mode_support_params(&context->bw_ctx.dml);
    1452             : 
    1453           0 :         if (vlevel == context->bw_ctx.dml.soc.num_states)
    1454             :                 goto validate_fail;
    1455             : 
    1456           0 :         for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
    1457           0 :                 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
    1458           0 :                 struct pipe_ctx *mpo_pipe = pipe->bottom_pipe;
    1459             : 
    1460           0 :                 if (!pipe->stream)
    1461           0 :                         continue;
    1462             : 
    1463           0 :                 if (vba->ODMCombineEnabled[vba->pipe_plane[pipe_idx]] != dm_odm_combine_mode_disabled
    1464           0 :                                 && !dc->config.enable_windowed_mpo_odm
    1465           0 :                                 && pipe->plane_state && mpo_pipe
    1466           0 :                                 && memcmp(&mpo_pipe->plane_res.scl_data.recout,
    1467           0 :                                                 &pipe->plane_res.scl_data.recout,
    1468             :                                                 sizeof(struct rect)) != 0) {
    1469           0 :                         ASSERT(mpo_pipe->plane_state != pipe->plane_state);
    1470             :                         goto validate_fail;
    1471             :                 }
    1472           0 :                 pipe_idx++;
    1473             :         }
    1474             : 
    1475             :         /* merge pipes if necessary */
    1476           0 :         for (i = 0; i < dc->res_pool->pipe_count; i++) {
    1477           0 :                 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
    1478             : 
    1479             :                 /*skip pipes that don't need merging*/
    1480           0 :                 if (!merge[i])
    1481           0 :                         continue;
    1482             : 
    1483             :                 /* if ODM merge we ignore mpc tree, mpo pipes will have their own flags */
    1484           0 :                 if (pipe->prev_odm_pipe) {
    1485             :                         /*split off odm pipe*/
    1486           0 :                         pipe->prev_odm_pipe->next_odm_pipe = pipe->next_odm_pipe;
    1487           0 :                         if (pipe->next_odm_pipe)
    1488           0 :                                 pipe->next_odm_pipe->prev_odm_pipe = pipe->prev_odm_pipe;
    1489             : 
    1490           0 :                         pipe->bottom_pipe = NULL;
    1491           0 :                         pipe->next_odm_pipe = NULL;
    1492           0 :                         pipe->plane_state = NULL;
    1493           0 :                         pipe->stream = NULL;
    1494           0 :                         pipe->top_pipe = NULL;
    1495           0 :                         pipe->prev_odm_pipe = NULL;
    1496           0 :                         if (pipe->stream_res.dsc)
    1497           0 :                                 dcn20_release_dsc(&context->res_ctx, dc->res_pool, &pipe->stream_res.dsc);
    1498           0 :                         memset(&pipe->plane_res, 0, sizeof(pipe->plane_res));
    1499           0 :                         memset(&pipe->stream_res, 0, sizeof(pipe->stream_res));
    1500           0 :                         repopulate_pipes = true;
    1501           0 :                 } else if (pipe->top_pipe && pipe->top_pipe->plane_state == pipe->plane_state) {
    1502           0 :                         struct pipe_ctx *top_pipe = pipe->top_pipe;
    1503           0 :                         struct pipe_ctx *bottom_pipe = pipe->bottom_pipe;
    1504             : 
    1505           0 :                         top_pipe->bottom_pipe = bottom_pipe;
    1506           0 :                         if (bottom_pipe)
    1507           0 :                                 bottom_pipe->top_pipe = top_pipe;
    1508             : 
    1509           0 :                         pipe->top_pipe = NULL;
    1510           0 :                         pipe->bottom_pipe = NULL;
    1511           0 :                         pipe->plane_state = NULL;
    1512           0 :                         pipe->stream = NULL;
    1513           0 :                         memset(&pipe->plane_res, 0, sizeof(pipe->plane_res));
    1514           0 :                         memset(&pipe->stream_res, 0, sizeof(pipe->stream_res));
    1515           0 :                         repopulate_pipes = true;
    1516             :                 } else
    1517           0 :                         ASSERT(0); /* Should never try to merge master pipe */
    1518             : 
    1519             :         }
    1520             : 
    1521           0 :         for (i = 0, pipe_idx = -1; i < dc->res_pool->pipe_count; i++) {
    1522           0 :                 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
    1523           0 :                 struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i];
    1524           0 :                 struct pipe_ctx *hsplit_pipe = NULL;
    1525             :                 bool odm;
    1526           0 :                 int old_index = -1;
    1527             : 
    1528           0 :                 if (!pipe->stream || newly_split[i])
    1529           0 :                         continue;
    1530             : 
    1531           0 :                 pipe_idx++;
    1532           0 :                 odm = vba->ODMCombineEnabled[vba->pipe_plane[pipe_idx]] != dm_odm_combine_mode_disabled;
    1533             : 
    1534           0 :                 if (!pipe->plane_state && !odm)
    1535           0 :                         continue;
    1536             : 
    1537           0 :                 if (split[i]) {
    1538           0 :                         if (odm) {
    1539           0 :                                 if (split[i] == 4 && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe)
    1540           0 :                                         old_index = old_pipe->next_odm_pipe->next_odm_pipe->pipe_idx;
    1541           0 :                                 else if (old_pipe->next_odm_pipe)
    1542           0 :                                         old_index = old_pipe->next_odm_pipe->pipe_idx;
    1543             :                         } else {
    1544           0 :                                 if (split[i] == 4 && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe &&
    1545           0 :                                                 old_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state)
    1546           0 :                                         old_index = old_pipe->bottom_pipe->bottom_pipe->pipe_idx;
    1547           0 :                                 else if (old_pipe->bottom_pipe &&
    1548           0 :                                                 old_pipe->bottom_pipe->plane_state == old_pipe->plane_state)
    1549           0 :                                         old_index = old_pipe->bottom_pipe->pipe_idx;
    1550             :                         }
    1551           0 :                         hsplit_pipe = dcn32_find_split_pipe(dc, context, old_index);
    1552           0 :                         ASSERT(hsplit_pipe);
    1553           0 :                         if (!hsplit_pipe)
    1554             :                                 goto validate_fail;
    1555             : 
    1556           0 :                         if (!dcn32_split_stream_for_mpc_or_odm(
    1557             :                                         dc, &context->res_ctx,
    1558             :                                         pipe, hsplit_pipe, odm))
    1559             :                                 goto validate_fail;
    1560             : 
    1561           0 :                         newly_split[hsplit_pipe->pipe_idx] = true;
    1562           0 :                         repopulate_pipes = true;
    1563             :                 }
    1564           0 :                 if (split[i] == 4) {
    1565             :                         struct pipe_ctx *pipe_4to1;
    1566             : 
    1567           0 :                         if (odm && old_pipe->next_odm_pipe)
    1568           0 :                                 old_index = old_pipe->next_odm_pipe->pipe_idx;
    1569           0 :                         else if (!odm && old_pipe->bottom_pipe &&
    1570           0 :                                                 old_pipe->bottom_pipe->plane_state == old_pipe->plane_state)
    1571           0 :                                 old_index = old_pipe->bottom_pipe->pipe_idx;
    1572             :                         else
    1573             :                                 old_index = -1;
    1574           0 :                         pipe_4to1 = dcn32_find_split_pipe(dc, context, old_index);
    1575           0 :                         ASSERT(pipe_4to1);
    1576           0 :                         if (!pipe_4to1)
    1577             :                                 goto validate_fail;
    1578           0 :                         if (!dcn32_split_stream_for_mpc_or_odm(
    1579             :                                         dc, &context->res_ctx,
    1580             :                                         pipe, pipe_4to1, odm))
    1581             :                                 goto validate_fail;
    1582           0 :                         newly_split[pipe_4to1->pipe_idx] = true;
    1583             : 
    1584           0 :                         if (odm && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe
    1585           0 :                                         && old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe)
    1586           0 :                                 old_index = old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe->pipe_idx;
    1587           0 :                         else if (!odm && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe &&
    1588           0 :                                         old_pipe->bottom_pipe->bottom_pipe->bottom_pipe &&
    1589           0 :                                         old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state)
    1590           0 :                                 old_index = old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->pipe_idx;
    1591             :                         else
    1592             :                                 old_index = -1;
    1593           0 :                         pipe_4to1 = dcn32_find_split_pipe(dc, context, old_index);
    1594           0 :                         ASSERT(pipe_4to1);
    1595           0 :                         if (!pipe_4to1)
    1596             :                                 goto validate_fail;
    1597           0 :                         if (!dcn32_split_stream_for_mpc_or_odm(
    1598             :                                         dc, &context->res_ctx,
    1599             :                                         hsplit_pipe, pipe_4to1, odm))
    1600             :                                 goto validate_fail;
    1601           0 :                         newly_split[pipe_4to1->pipe_idx] = true;
    1602             :                 }
    1603           0 :                 if (odm)
    1604           0 :                         dcn20_build_mapped_resource(dc, context, pipe->stream);
    1605             :         }
    1606             : 
    1607           0 :         for (i = 0; i < dc->res_pool->pipe_count; i++) {
    1608           0 :                 struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
    1609             : 
    1610           0 :                 if (pipe->plane_state) {
    1611           0 :                         if (!resource_build_scaling_params(pipe))
    1612             :                                 goto validate_fail;
    1613             :                 }
    1614             :         }
    1615             : 
    1616             :         /* Actual dsc count per stream dsc validation*/
    1617           0 :         if (!dcn20_validate_dsc(dc, context)) {
    1618           0 :                 vba->ValidationStatus[vba->soc.num_states] = DML_FAIL_DSC_VALIDATION_FAILURE;
    1619           0 :                 goto validate_fail;
    1620             :         }
    1621             : 
    1622           0 :         if (repopulate_pipes)
    1623           0 :                 pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, fast_validate);
    1624           0 :         *vlevel_out = vlevel;
    1625           0 :         *pipe_cnt_out = pipe_cnt;
    1626             : 
    1627           0 :         out = true;
    1628           0 :         goto validate_out;
    1629             : 
    1630             : validate_fail:
    1631             :         out = false;
    1632             : 
    1633             : validate_out:
    1634             :         return out;
    1635             : }
    1636             : 
    1637             : 
    1638           0 : void dcn32_calculate_wm_and_dlg_fpu(struct dc *dc, struct dc_state *context,
    1639             :                                 display_e2e_pipe_params_st *pipes,
    1640             :                                 int pipe_cnt,
    1641             :                                 int vlevel)
    1642             : {
    1643           0 :         int i, pipe_idx, vlevel_temp = 0;
    1644           0 :         double dcfclk = dcn3_2_soc.clock_limits[0].dcfclk_mhz;
    1645           0 :         double dcfclk_from_validation = context->bw_ctx.dml.vba.DCFCLKState[vlevel][context->bw_ctx.dml.vba.maxMpcComb];
    1646           0 :         bool pstate_en = context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] !=
    1647             :                         dm_dram_clock_change_unsupported;
    1648           0 :         unsigned int dummy_latency_index = 0;
    1649           0 :         int maxMpcComb = context->bw_ctx.dml.vba.maxMpcComb;
    1650           0 :         unsigned int min_dram_speed_mts = context->bw_ctx.dml.vba.DRAMSpeed;
    1651             :         unsigned int min_dram_speed_mts_margin;
    1652             : 
    1653           0 :         dc_assert_fp_enabled();
    1654             : 
    1655             :         // Override DRAMClockChangeSupport for SubVP + DRR case where the DRR cannot switch without stretching it's VBLANK
    1656           0 :         if (!pstate_en && dcn32_subvp_in_use(dc, context)) {
    1657           0 :                 context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] = dm_dram_clock_change_vblank_w_mall_sub_vp;
    1658           0 :                 pstate_en = true;
    1659             :         }
    1660             : 
    1661           0 :         context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching = false;
    1662             : 
    1663           0 :         if (!pstate_en) {
    1664             :                 /* only when the mclk switch can not be natural, is the fw based vblank stretch attempted */
    1665           0 :                 context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching =
    1666           0 :                         dcn30_can_support_mclk_switch_using_fw_based_vblank_stretch(dc, context);
    1667             : 
    1668           0 :                 if (context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching) {
    1669           0 :                         dummy_latency_index = dcn30_find_dummy_latency_index_for_fw_based_mclk_switch(dc,
    1670             :                                 context, pipes, pipe_cnt, vlevel);
    1671             : 
    1672             :                         /* After calling dcn30_find_dummy_latency_index_for_fw_based_mclk_switch
    1673             :                          * we reinstate the original dram_clock_change_latency_us on the context
    1674             :                          * and all variables that may have changed up to this point, except the
    1675             :                          * newly found dummy_latency_index
    1676             :                          */
    1677           0 :                         context->bw_ctx.dml.soc.dram_clock_change_latency_us =
    1678           0 :                                         dc->clk_mgr->bw_params->wm_table.nv_entries[WM_A].dml_input.pstate_latency_us;
    1679           0 :                         dcn32_internal_validate_bw(dc, context, pipes, &pipe_cnt, &vlevel, false);
    1680           0 :                         maxMpcComb = context->bw_ctx.dml.vba.maxMpcComb;
    1681           0 :                         dcfclk = context->bw_ctx.dml.vba.DCFCLKState[vlevel][context->bw_ctx.dml.vba.maxMpcComb];
    1682           0 :                         pstate_en = context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][maxMpcComb] !=
    1683             :                                         dm_dram_clock_change_unsupported;
    1684             :                 }
    1685             :         }
    1686             : 
    1687             :         /* Set B:
    1688             :          * For Set B calculations use clocks from clock_limits[2] when available i.e. when SMU is present,
    1689             :          * otherwise use arbitrary low value from spreadsheet for DCFCLK as lower is safer for watermark
    1690             :          * calculations to cover bootup clocks.
    1691             :          * DCFCLK: soc.clock_limits[2] when available
    1692             :          * UCLK: soc.clock_limits[2] when available
    1693             :          */
    1694           0 :         if (dcn3_2_soc.num_states > 2) {
    1695           0 :                 vlevel_temp = 2;
    1696           0 :                 dcfclk = dcn3_2_soc.clock_limits[2].dcfclk_mhz;
    1697             :         } else
    1698             :                 dcfclk = 615; //DCFCLK Vmin_lv
    1699             : 
    1700           0 :         pipes[0].clks_cfg.voltage = vlevel_temp;
    1701           0 :         pipes[0].clks_cfg.dcfclk_mhz = dcfclk;
    1702           0 :         pipes[0].clks_cfg.socclk_mhz = context->bw_ctx.dml.soc.clock_limits[vlevel_temp].socclk_mhz;
    1703             : 
    1704           0 :         if (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_B].valid) {
    1705           0 :                 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;
    1706           0 :                 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;
    1707           0 :                 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;
    1708           0 :                 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;
    1709             :         }
    1710           0 :         context->bw_ctx.bw.dcn.watermarks.b.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1711           0 :         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;
    1712           0 :         context->bw_ctx.bw.dcn.watermarks.b.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1713           0 :         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;
    1714           0 :         context->bw_ctx.bw.dcn.watermarks.b.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1715           0 :         context->bw_ctx.bw.dcn.watermarks.b.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1716           0 :         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;
    1717           0 :         context->bw_ctx.bw.dcn.watermarks.b.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1718           0 :         context->bw_ctx.bw.dcn.watermarks.b.cstate_pstate.fclk_pstate_change_ns = get_fclk_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1719           0 :         context->bw_ctx.bw.dcn.watermarks.b.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1720             : 
    1721             :         /* Set D:
    1722             :          * All clocks min.
    1723             :          * DCFCLK: Min, as reported by PM FW when available
    1724             :          * UCLK  : Min, as reported by PM FW when available
    1725             :          * sr_enter_exit/sr_exit should be lower than used for DRAM (TBD after bringup or later, use as decided in Clk Mgr)
    1726             :          */
    1727             : 
    1728           0 :         if (dcn3_2_soc.num_states > 2) {
    1729           0 :                 vlevel_temp = 0;
    1730           0 :                 dcfclk = dc->clk_mgr->bw_params->clk_table.entries[0].dcfclk_mhz;
    1731             :         } else
    1732             :                 dcfclk = 615; //DCFCLK Vmin_lv
    1733             : 
    1734           0 :         pipes[0].clks_cfg.voltage = vlevel_temp;
    1735           0 :         pipes[0].clks_cfg.dcfclk_mhz = dcfclk;
    1736           0 :         pipes[0].clks_cfg.socclk_mhz = context->bw_ctx.dml.soc.clock_limits[vlevel_temp].socclk_mhz;
    1737             : 
    1738           0 :         if (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_D].valid) {
    1739           0 :                 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;
    1740           0 :                 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;
    1741           0 :                 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;
    1742           0 :                 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;
    1743             :         }
    1744           0 :         context->bw_ctx.bw.dcn.watermarks.d.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1745           0 :         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;
    1746           0 :         context->bw_ctx.bw.dcn.watermarks.d.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1747           0 :         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;
    1748           0 :         context->bw_ctx.bw.dcn.watermarks.d.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1749           0 :         context->bw_ctx.bw.dcn.watermarks.d.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1750           0 :         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;
    1751           0 :         context->bw_ctx.bw.dcn.watermarks.d.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1752           0 :         context->bw_ctx.bw.dcn.watermarks.d.cstate_pstate.fclk_pstate_change_ns = get_fclk_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1753           0 :         context->bw_ctx.bw.dcn.watermarks.d.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1754             : 
    1755             :         /* Set C, for Dummy P-State:
    1756             :          * All clocks min.
    1757             :          * DCFCLK: Min, as reported by PM FW, when available
    1758             :          * UCLK  : Min,  as reported by PM FW, when available
    1759             :          * pstate latency as per UCLK state dummy pstate latency
    1760             :          */
    1761             : 
    1762             :         // For Set A and Set C use values from validation
    1763           0 :         pipes[0].clks_cfg.voltage = vlevel;
    1764           0 :         pipes[0].clks_cfg.dcfclk_mhz = dcfclk_from_validation;
    1765           0 :         pipes[0].clks_cfg.socclk_mhz = context->bw_ctx.dml.soc.clock_limits[vlevel].socclk_mhz;
    1766             : 
    1767           0 :         if (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_C].valid) {
    1768           0 :                 min_dram_speed_mts = context->bw_ctx.dml.vba.DRAMSpeed;
    1769           0 :                 min_dram_speed_mts_margin = 160;
    1770             : 
    1771           0 :                 context->bw_ctx.dml.soc.dram_clock_change_latency_us =
    1772           0 :                         dc->clk_mgr->bw_params->dummy_pstate_table[0].dummy_pstate_latency_us;
    1773             : 
    1774           0 :                 if (context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][maxMpcComb] ==
    1775             :                         dm_dram_clock_change_unsupported) {
    1776           0 :                         int min_dram_speed_mts_offset = dc->clk_mgr->bw_params->clk_table.num_entries - 1;
    1777             : 
    1778           0 :                         min_dram_speed_mts =
    1779           0 :                                 dc->clk_mgr->bw_params->clk_table.entries[min_dram_speed_mts_offset].memclk_mhz * 16;
    1780             :                 }
    1781             : 
    1782           0 :                 if (!context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching) {
    1783             :                         /* find largest table entry that is lower than dram speed,
    1784             :                          * but lower than DPM0 still uses DPM0
    1785             :                          */
    1786           0 :                         for (dummy_latency_index = 3; dummy_latency_index > 0; dummy_latency_index--)
    1787           0 :                                 if (min_dram_speed_mts + min_dram_speed_mts_margin >
    1788           0 :                                         dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dram_speed_mts)
    1789             :                                         break;
    1790             :                 }
    1791             : 
    1792           0 :                 context->bw_ctx.dml.soc.dram_clock_change_latency_us =
    1793           0 :                         dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dummy_pstate_latency_us;
    1794             : 
    1795           0 :                 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;
    1796           0 :                 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;
    1797           0 :                 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;
    1798             :         }
    1799             : 
    1800           0 :         context->bw_ctx.bw.dcn.watermarks.c.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1801           0 :         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;
    1802           0 :         context->bw_ctx.bw.dcn.watermarks.c.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1803           0 :         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;
    1804           0 :         context->bw_ctx.bw.dcn.watermarks.c.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1805           0 :         context->bw_ctx.bw.dcn.watermarks.c.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1806           0 :         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;
    1807           0 :         context->bw_ctx.bw.dcn.watermarks.c.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1808             :         /* On DCN32/321, PMFW will set PSTATE_CHANGE_TYPE = 1 (FCLK) for UCLK dummy p-state.
    1809             :          * In this case we must program FCLK WM Set C to use the UCLK dummy p-state WM
    1810             :          * value.
    1811             :          */
    1812           0 :         context->bw_ctx.bw.dcn.watermarks.c.cstate_pstate.fclk_pstate_change_ns = get_wm_dram_clock_change(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1813           0 :         context->bw_ctx.bw.dcn.watermarks.c.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1814             : 
    1815           0 :         if ((!pstate_en) && (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_C].valid)) {
    1816             :                 /* The only difference between A and C is p-state latency, if p-state is not supported
    1817             :                  * with full p-state latency we want to calculate DLG based on dummy p-state latency,
    1818             :                  * Set A p-state watermark set to 0 on DCN30, when p-state unsupported, for now keep as DCN30.
    1819             :                  */
    1820           0 :                 context->bw_ctx.bw.dcn.watermarks.a = context->bw_ctx.bw.dcn.watermarks.c;
    1821           0 :                 context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns = 0;
    1822             :         } else {
    1823             :                 /* Set A:
    1824             :                  * All clocks min.
    1825             :                  * DCFCLK: Min, as reported by PM FW, when available
    1826             :                  * UCLK: Min, as reported by PM FW, when available
    1827             :                  */
    1828           0 :                 dc->res_pool->funcs->update_soc_for_wm_a(dc, context);
    1829           0 :                 context->bw_ctx.bw.dcn.watermarks.a.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1830           0 :                 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;
    1831           0 :                 context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1832           0 :                 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;
    1833           0 :                 context->bw_ctx.bw.dcn.watermarks.a.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1834           0 :                 context->bw_ctx.bw.dcn.watermarks.a.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1835           0 :                 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;
    1836           0 :                 context->bw_ctx.bw.dcn.watermarks.a.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1837           0 :                 context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.fclk_pstate_change_ns = get_fclk_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1838           0 :                 context->bw_ctx.bw.dcn.watermarks.a.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000;
    1839             :         }
    1840             : 
    1841           0 :         for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
    1842           0 :                 if (!context->res_ctx.pipe_ctx[i].stream)
    1843           0 :                         continue;
    1844             : 
    1845           0 :                 pipes[pipe_idx].clks_cfg.dispclk_mhz = get_dispclk_calculated(&context->bw_ctx.dml, pipes, pipe_cnt);
    1846           0 :                 pipes[pipe_idx].clks_cfg.dppclk_mhz = get_dppclk_calculated(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx);
    1847             : 
    1848           0 :                 if (dc->config.forced_clocks) {
    1849           0 :                         pipes[pipe_idx].clks_cfg.dispclk_mhz = context->bw_ctx.dml.soc.clock_limits[0].dispclk_mhz;
    1850           0 :                         pipes[pipe_idx].clks_cfg.dppclk_mhz = context->bw_ctx.dml.soc.clock_limits[0].dppclk_mhz;
    1851             :                 }
    1852           0 :                 if (dc->debug.min_disp_clk_khz > pipes[pipe_idx].clks_cfg.dispclk_mhz * 1000)
    1853           0 :                         pipes[pipe_idx].clks_cfg.dispclk_mhz = dc->debug.min_disp_clk_khz / 1000.0;
    1854           0 :                 if (dc->debug.min_dpp_clk_khz > pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000)
    1855           0 :                         pipes[pipe_idx].clks_cfg.dppclk_mhz = dc->debug.min_dpp_clk_khz / 1000.0;
    1856             : 
    1857           0 :                 pipe_idx++;
    1858             :         }
    1859             : 
    1860           0 :         context->perf_params.stutter_period_us = context->bw_ctx.dml.vba.StutterPeriod;
    1861             : 
    1862           0 :         dcn32_calculate_dlg_params(dc, context, pipes, pipe_cnt, vlevel);
    1863             : 
    1864           0 :         if (!pstate_en)
    1865             :                 /* Restore full p-state latency */
    1866           0 :                 context->bw_ctx.dml.soc.dram_clock_change_latency_us =
    1867           0 :                                 dc->clk_mgr->bw_params->wm_table.nv_entries[WM_A].dml_input.pstate_latency_us;
    1868             : 
    1869           0 :         if (context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching)
    1870           0 :                 dcn30_setup_mclk_switch_using_fw_based_vblank_stretch(dc, context);
    1871           0 : }
    1872             : 
    1873           0 : static void dcn32_get_optimal_dcfclk_fclk_for_uclk(unsigned int uclk_mts,
    1874             :                 unsigned int *optimal_dcfclk,
    1875             :                 unsigned int *optimal_fclk)
    1876             : {
    1877             :         double bw_from_dram, bw_from_dram1, bw_from_dram2;
    1878             : 
    1879           0 :         bw_from_dram1 = uclk_mts * dcn3_2_soc.num_chans *
    1880           0 :                 dcn3_2_soc.dram_channel_width_bytes * (dcn3_2_soc.max_avg_dram_bw_use_normal_percent / 100);
    1881           0 :         bw_from_dram2 = uclk_mts * dcn3_2_soc.num_chans *
    1882           0 :                 dcn3_2_soc.dram_channel_width_bytes * (dcn3_2_soc.max_avg_sdp_bw_use_normal_percent / 100);
    1883             : 
    1884           0 :         bw_from_dram = (bw_from_dram1 < bw_from_dram2) ? bw_from_dram1 : bw_from_dram2;
    1885             : 
    1886           0 :         if (optimal_fclk)
    1887           0 :                 *optimal_fclk = bw_from_dram /
    1888           0 :                 (dcn3_2_soc.fabric_datapath_to_dcn_data_return_bytes * (dcn3_2_soc.max_avg_sdp_bw_use_normal_percent / 100));
    1889             : 
    1890           0 :         if (optimal_dcfclk)
    1891           0 :                 *optimal_dcfclk =  bw_from_dram /
    1892           0 :                 (dcn3_2_soc.return_bus_width_bytes * (dcn3_2_soc.max_avg_sdp_bw_use_normal_percent / 100));
    1893           0 : }
    1894             : 
    1895           0 : static void remove_entry_from_table_at_index(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries,
    1896             :                 unsigned int index)
    1897             : {
    1898             :         int i;
    1899             : 
    1900           0 :         if (*num_entries == 0)
    1901             :                 return;
    1902             : 
    1903           0 :         for (i = index; i < *num_entries - 1; i++) {
    1904           0 :                 table[i] = table[i + 1];
    1905             :         }
    1906           0 :         memset(&table[--(*num_entries)], 0, sizeof(struct _vcs_dpi_voltage_scaling_st));
    1907             : }
    1908             : 
    1909           0 : static int build_synthetic_soc_states(struct clk_bw_params *bw_params,
    1910             :                 struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries)
    1911             : {
    1912             :         int i, j;
    1913           0 :         struct _vcs_dpi_voltage_scaling_st entry = {0};
    1914             : 
    1915           0 :         unsigned int max_dcfclk_mhz = 0, max_dispclk_mhz = 0, max_dppclk_mhz = 0,
    1916           0 :                         max_phyclk_mhz = 0, max_dtbclk_mhz = 0, max_fclk_mhz = 0, max_uclk_mhz = 0;
    1917             : 
    1918           0 :         unsigned int min_dcfclk_mhz = 199, min_fclk_mhz = 299;
    1919             : 
    1920             :         static const unsigned int num_dcfclk_stas = 5;
    1921           0 :         unsigned int dcfclk_sta_targets[DC__VOLTAGE_STATES] = {199, 615, 906, 1324, 1564};
    1922             : 
    1923           0 :         unsigned int num_uclk_dpms = 0;
    1924           0 :         unsigned int num_fclk_dpms = 0;
    1925           0 :         unsigned int num_dcfclk_dpms = 0;
    1926             : 
    1927           0 :         for (i = 0; i < MAX_NUM_DPM_LVL; i++) {
    1928           0 :                 if (bw_params->clk_table.entries[i].dcfclk_mhz > max_dcfclk_mhz)
    1929           0 :                         max_dcfclk_mhz = bw_params->clk_table.entries[i].dcfclk_mhz;
    1930           0 :                 if (bw_params->clk_table.entries[i].fclk_mhz > max_fclk_mhz)
    1931           0 :                         max_fclk_mhz = bw_params->clk_table.entries[i].fclk_mhz;
    1932           0 :                 if (bw_params->clk_table.entries[i].memclk_mhz > max_uclk_mhz)
    1933           0 :                         max_uclk_mhz = bw_params->clk_table.entries[i].memclk_mhz;
    1934           0 :                 if (bw_params->clk_table.entries[i].dispclk_mhz > max_dispclk_mhz)
    1935           0 :                         max_dispclk_mhz = bw_params->clk_table.entries[i].dispclk_mhz;
    1936           0 :                 if (bw_params->clk_table.entries[i].dppclk_mhz > max_dppclk_mhz)
    1937           0 :                         max_dppclk_mhz = bw_params->clk_table.entries[i].dppclk_mhz;
    1938           0 :                 if (bw_params->clk_table.entries[i].phyclk_mhz > max_phyclk_mhz)
    1939           0 :                         max_phyclk_mhz = bw_params->clk_table.entries[i].phyclk_mhz;
    1940           0 :                 if (bw_params->clk_table.entries[i].dtbclk_mhz > max_dtbclk_mhz)
    1941           0 :                         max_dtbclk_mhz = bw_params->clk_table.entries[i].dtbclk_mhz;
    1942             : 
    1943           0 :                 if (bw_params->clk_table.entries[i].memclk_mhz > 0)
    1944           0 :                         num_uclk_dpms++;
    1945           0 :                 if (bw_params->clk_table.entries[i].fclk_mhz > 0)
    1946           0 :                         num_fclk_dpms++;
    1947             :                 if (bw_params->clk_table.entries[i].dcfclk_mhz > 0)
    1948             :                         num_dcfclk_dpms++;
    1949             :         }
    1950             : 
    1951           0 :         if (!max_dcfclk_mhz || !max_dispclk_mhz || !max_dtbclk_mhz)
    1952             :                 return -1;
    1953             : 
    1954           0 :         if (max_dppclk_mhz == 0)
    1955           0 :                 max_dppclk_mhz = max_dispclk_mhz;
    1956             : 
    1957           0 :         if (max_fclk_mhz == 0)
    1958           0 :                 max_fclk_mhz = max_dcfclk_mhz * dcn3_2_soc.pct_ideal_sdp_bw_after_urgent / dcn3_2_soc.pct_ideal_fabric_bw_after_urgent;
    1959             : 
    1960           0 :         if (max_phyclk_mhz == 0)
    1961           0 :                 max_phyclk_mhz = dcn3_2_soc.clock_limits[0].phyclk_mhz;
    1962             : 
    1963           0 :         *num_entries = 0;
    1964           0 :         entry.dispclk_mhz = max_dispclk_mhz;
    1965           0 :         entry.dscclk_mhz = max_dispclk_mhz / 3;
    1966           0 :         entry.dppclk_mhz = max_dppclk_mhz;
    1967           0 :         entry.dtbclk_mhz = max_dtbclk_mhz;
    1968           0 :         entry.phyclk_mhz = max_phyclk_mhz;
    1969           0 :         entry.phyclk_d18_mhz = dcn3_2_soc.clock_limits[0].phyclk_d18_mhz;
    1970           0 :         entry.phyclk_d32_mhz = dcn3_2_soc.clock_limits[0].phyclk_d32_mhz;
    1971             : 
    1972             :         // Insert all the DCFCLK STAs
    1973           0 :         for (i = 0; i < num_dcfclk_stas; i++) {
    1974           0 :                 entry.dcfclk_mhz = dcfclk_sta_targets[i];
    1975           0 :                 entry.fabricclk_mhz = 0;
    1976           0 :                 entry.dram_speed_mts = 0;
    1977             : 
    1978           0 :                 DC_FP_START();
    1979           0 :                 insert_entry_into_table_sorted(table, num_entries, &entry);
    1980           0 :                 DC_FP_END();
    1981             :         }
    1982             : 
    1983             :         // Insert the max DCFCLK
    1984           0 :         entry.dcfclk_mhz = max_dcfclk_mhz;
    1985           0 :         entry.fabricclk_mhz = 0;
    1986           0 :         entry.dram_speed_mts = 0;
    1987             : 
    1988           0 :         DC_FP_START();
    1989           0 :         insert_entry_into_table_sorted(table, num_entries, &entry);
    1990           0 :         DC_FP_END();
    1991             : 
    1992             :         // Insert the UCLK DPMS
    1993           0 :         for (i = 0; i < num_uclk_dpms; i++) {
    1994           0 :                 entry.dcfclk_mhz = 0;
    1995           0 :                 entry.fabricclk_mhz = 0;
    1996           0 :                 entry.dram_speed_mts = bw_params->clk_table.entries[i].memclk_mhz * 16;
    1997             : 
    1998           0 :                 DC_FP_START();
    1999           0 :                 insert_entry_into_table_sorted(table, num_entries, &entry);
    2000           0 :                 DC_FP_END();
    2001             :         }
    2002             : 
    2003             :         // If FCLK is coarse grained, insert individual DPMs.
    2004           0 :         if (num_fclk_dpms > 2) {
    2005           0 :                 for (i = 0; i < num_fclk_dpms; i++) {
    2006           0 :                         entry.dcfclk_mhz = 0;
    2007           0 :                         entry.fabricclk_mhz = bw_params->clk_table.entries[i].fclk_mhz;
    2008           0 :                         entry.dram_speed_mts = 0;
    2009             : 
    2010           0 :                         DC_FP_START();
    2011           0 :                         insert_entry_into_table_sorted(table, num_entries, &entry);
    2012           0 :                         DC_FP_END();
    2013             :                 }
    2014             :         }
    2015             :         // If FCLK fine grained, only insert max
    2016             :         else {
    2017           0 :                 entry.dcfclk_mhz = 0;
    2018           0 :                 entry.fabricclk_mhz = max_fclk_mhz;
    2019           0 :                 entry.dram_speed_mts = 0;
    2020             : 
    2021           0 :                 DC_FP_START();
    2022           0 :                 insert_entry_into_table_sorted(table, num_entries, &entry);
    2023           0 :                 DC_FP_END();
    2024             :         }
    2025             : 
    2026             :         // At this point, the table contains all "points of interest" based on
    2027             :         // DPMs from PMFW, and STAs.  Table is sorted by BW, and all clock
    2028             :         // ratios (by derate, are exact).
    2029             : 
    2030             :         // Remove states that require higher clocks than are supported
    2031           0 :         for (i = *num_entries - 1; i >= 0 ; i--) {
    2032           0 :                 if (table[i].dcfclk_mhz > max_dcfclk_mhz ||
    2033           0 :                                 table[i].fabricclk_mhz > max_fclk_mhz ||
    2034           0 :                                 table[i].dram_speed_mts > max_uclk_mhz * 16)
    2035           0 :                         remove_entry_from_table_at_index(table, num_entries, i);
    2036             :         }
    2037             : 
    2038             :         // At this point, the table only contains supported points of interest
    2039             :         // it could be used as is, but some states may be redundant due to
    2040             :         // coarse grained nature of some clocks, so we want to round up to
    2041             :         // coarse grained DPMs and remove duplicates.
    2042             : 
    2043             :         // Round up UCLKs
    2044           0 :         for (i = *num_entries - 1; i >= 0 ; i--) {
    2045           0 :                 for (j = 0; j < num_uclk_dpms; j++) {
    2046           0 :                         if (bw_params->clk_table.entries[j].memclk_mhz * 16 >= table[i].dram_speed_mts) {
    2047           0 :                                 table[i].dram_speed_mts = bw_params->clk_table.entries[j].memclk_mhz * 16;
    2048           0 :                                 break;
    2049             :                         }
    2050             :                 }
    2051             :         }
    2052             : 
    2053             :         // If FCLK is coarse grained, round up to next DPMs
    2054           0 :         if (num_fclk_dpms > 2) {
    2055           0 :                 for (i = *num_entries - 1; i >= 0 ; i--) {
    2056           0 :                         for (j = 0; j < num_fclk_dpms; j++) {
    2057           0 :                                 if (bw_params->clk_table.entries[j].fclk_mhz >= table[i].fabricclk_mhz) {
    2058           0 :                                         table[i].fabricclk_mhz = bw_params->clk_table.entries[j].fclk_mhz;
    2059           0 :                                         break;
    2060             :                                 }
    2061             :                         }
    2062             :                 }
    2063             :         }
    2064             :         // Otherwise, round up to minimum.
    2065             :         else {
    2066           0 :                 for (i = *num_entries - 1; i >= 0 ; i--) {
    2067           0 :                         if (table[i].fabricclk_mhz < min_fclk_mhz) {
    2068           0 :                                 table[i].fabricclk_mhz = min_fclk_mhz;
    2069           0 :                                 break;
    2070             :                         }
    2071             :                 }
    2072             :         }
    2073             : 
    2074             :         // Round DCFCLKs up to minimum
    2075           0 :         for (i = *num_entries - 1; i >= 0 ; i--) {
    2076           0 :                 if (table[i].dcfclk_mhz < min_dcfclk_mhz) {
    2077           0 :                         table[i].dcfclk_mhz = min_dcfclk_mhz;
    2078           0 :                         break;
    2079             :                 }
    2080             :         }
    2081             : 
    2082             :         // Remove duplicate states, note duplicate states are always neighbouring since table is sorted.
    2083             :         i = 0;
    2084           0 :         while (i < *num_entries - 1) {
    2085           0 :                 if (table[i].dcfclk_mhz == table[i + 1].dcfclk_mhz &&
    2086           0 :                                 table[i].fabricclk_mhz == table[i + 1].fabricclk_mhz &&
    2087           0 :                                 table[i].dram_speed_mts == table[i + 1].dram_speed_mts)
    2088           0 :                         remove_entry_from_table_at_index(table, num_entries, i + 1);
    2089             :                 else
    2090           0 :                         i++;
    2091             :         }
    2092             : 
    2093             :         // Fix up the state indicies
    2094           0 :         for (i = *num_entries - 1; i >= 0 ; i--) {
    2095           0 :                 table[i].state = i;
    2096             :         }
    2097             : 
    2098             :         return 0;
    2099             : }
    2100             : 
    2101             : /**
    2102             :  * dcn32_update_bw_bounding_box
    2103             :  *
    2104             :  * This would override some dcn3_2 ip_or_soc initial parameters hardcoded from
    2105             :  * spreadsheet with actual values as per dGPU SKU:
    2106             :  * - with passed few options from dc->config
    2107             :  * - with dentist_vco_frequency from Clk Mgr (currently hardcoded, but might
    2108             :  *   need to get it from PM FW)
    2109             :  * - with passed latency values (passed in ns units) in dc-> bb override for
    2110             :  *   debugging purposes
    2111             :  * - with passed latencies from VBIOS (in 100_ns units) if available for
    2112             :  *   certain dGPU SKU
    2113             :  * - with number of DRAM channels from VBIOS (which differ for certain dGPU SKU
    2114             :  *   of the same ASIC)
    2115             :  * - clocks levels with passed clk_table entries from Clk Mgr as reported by PM
    2116             :  *   FW for different clocks (which might differ for certain dGPU SKU of the
    2117             :  *   same ASIC)
    2118             :  */
    2119           0 : void dcn32_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params)
    2120             : {
    2121           0 :         dc_assert_fp_enabled();
    2122             : 
    2123           0 :         if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) {
    2124             :                 /* Overrides from dc->config options */
    2125           0 :                 dcn3_2_ip.clamp_min_dcfclk = dc->config.clamp_min_dcfclk;
    2126             : 
    2127             :                 /* Override from passed dc->bb_overrides if available*/
    2128           0 :                 if ((int)(dcn3_2_soc.sr_exit_time_us * 1000) != dc->bb_overrides.sr_exit_time_ns
    2129           0 :                                 && dc->bb_overrides.sr_exit_time_ns) {
    2130           0 :                         dcn3_2_soc.sr_exit_time_us = dc->bb_overrides.sr_exit_time_ns / 1000.0;
    2131             :                 }
    2132             : 
    2133           0 :                 if ((int)(dcn3_2_soc.sr_enter_plus_exit_time_us * 1000)
    2134           0 :                                 != dc->bb_overrides.sr_enter_plus_exit_time_ns
    2135           0 :                                 && dc->bb_overrides.sr_enter_plus_exit_time_ns) {
    2136           0 :                         dcn3_2_soc.sr_enter_plus_exit_time_us =
    2137           0 :                                 dc->bb_overrides.sr_enter_plus_exit_time_ns / 1000.0;
    2138             :                 }
    2139             : 
    2140           0 :                 if ((int)(dcn3_2_soc.urgent_latency_us * 1000) != dc->bb_overrides.urgent_latency_ns
    2141           0 :                         && dc->bb_overrides.urgent_latency_ns) {
    2142           0 :                         dcn3_2_soc.urgent_latency_us = dc->bb_overrides.urgent_latency_ns / 1000.0;
    2143             :                 }
    2144             : 
    2145           0 :                 if ((int)(dcn3_2_soc.dram_clock_change_latency_us * 1000)
    2146           0 :                                 != dc->bb_overrides.dram_clock_change_latency_ns
    2147           0 :                                 && dc->bb_overrides.dram_clock_change_latency_ns) {
    2148           0 :                         dcn3_2_soc.dram_clock_change_latency_us =
    2149           0 :                                 dc->bb_overrides.dram_clock_change_latency_ns / 1000.0;
    2150             :                 }
    2151             : 
    2152           0 :                 if ((int)(dcn3_2_soc.fclk_change_latency_us * 1000)
    2153           0 :                                 != dc->bb_overrides.fclk_clock_change_latency_ns
    2154           0 :                                 && dc->bb_overrides.fclk_clock_change_latency_ns) {
    2155           0 :                         dcn3_2_soc.fclk_change_latency_us =
    2156           0 :                                 dc->bb_overrides.fclk_clock_change_latency_ns / 1000;
    2157             :                 }
    2158             : 
    2159           0 :                 if ((int)(dcn3_2_soc.dummy_pstate_latency_us * 1000)
    2160           0 :                                 != dc->bb_overrides.dummy_clock_change_latency_ns
    2161           0 :                                 && dc->bb_overrides.dummy_clock_change_latency_ns) {
    2162           0 :                         dcn3_2_soc.dummy_pstate_latency_us =
    2163           0 :                                 dc->bb_overrides.dummy_clock_change_latency_ns / 1000.0;
    2164             :                 }
    2165             : 
    2166             :                 /* Override from VBIOS if VBIOS bb_info available */
    2167           0 :                 if (dc->ctx->dc_bios->funcs->get_soc_bb_info) {
    2168           0 :                         struct bp_soc_bb_info bb_info = {0};
    2169             : 
    2170           0 :                         if (dc->ctx->dc_bios->funcs->get_soc_bb_info(dc->ctx->dc_bios, &bb_info) == BP_RESULT_OK) {
    2171           0 :                                 if (bb_info.dram_clock_change_latency_100ns > 0)
    2172           0 :                                         dcn3_2_soc.dram_clock_change_latency_us =
    2173           0 :                                                 bb_info.dram_clock_change_latency_100ns * 10;
    2174             : 
    2175           0 :                                 if (bb_info.dram_sr_enter_exit_latency_100ns > 0)
    2176           0 :                                         dcn3_2_soc.sr_enter_plus_exit_time_us =
    2177           0 :                                                 bb_info.dram_sr_enter_exit_latency_100ns * 10;
    2178             : 
    2179           0 :                                 if (bb_info.dram_sr_exit_latency_100ns > 0)
    2180           0 :                                         dcn3_2_soc.sr_exit_time_us =
    2181           0 :                                                 bb_info.dram_sr_exit_latency_100ns * 10;
    2182             :                         }
    2183             :                 }
    2184             : 
    2185             :                 /* Override from VBIOS for num_chan */
    2186           0 :                 if (dc->ctx->dc_bios->vram_info.num_chans)
    2187           0 :                         dcn3_2_soc.num_chans = dc->ctx->dc_bios->vram_info.num_chans;
    2188             : 
    2189           0 :                 if (dc->ctx->dc_bios->vram_info.dram_channel_width_bytes)
    2190           0 :                         dcn3_2_soc.dram_channel_width_bytes = dc->ctx->dc_bios->vram_info.dram_channel_width_bytes;
    2191             : 
    2192             :         }
    2193             : 
    2194             :         /* Override dispclk_dppclk_vco_speed_mhz from Clk Mgr */
    2195           0 :         dcn3_2_soc.dispclk_dppclk_vco_speed_mhz = dc->clk_mgr->dentist_vco_freq_khz / 1000.0;
    2196           0 :         dc->dml.soc.dispclk_dppclk_vco_speed_mhz = dc->clk_mgr->dentist_vco_freq_khz / 1000.0;
    2197             : 
    2198             :         /* Overrides Clock levelsfrom CLK Mgr table entries as reported by PM FW */
    2199           0 :         if ((!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) && (bw_params->clk_table.entries[0].memclk_mhz)) {
    2200           0 :                 if (dc->debug.use_legacy_soc_bb_mechanism) {
    2201           0 :                         unsigned int i = 0, j = 0, num_states = 0;
    2202             : 
    2203           0 :                         unsigned int dcfclk_mhz[DC__VOLTAGE_STATES] = {0};
    2204           0 :                         unsigned int dram_speed_mts[DC__VOLTAGE_STATES] = {0};
    2205           0 :                         unsigned int optimal_uclk_for_dcfclk_sta_targets[DC__VOLTAGE_STATES] = {0};
    2206           0 :                         unsigned int optimal_dcfclk_for_uclk[DC__VOLTAGE_STATES] = {0};
    2207           0 :                         unsigned int min_dcfclk = UINT_MAX;
    2208             :                         /* Set 199 as first value in STA target array to have a minimum DCFCLK value.
    2209             :                          * For DCN32 we set min to 199 so minimum FCLK DPM0 (300Mhz can be achieved) */
    2210           0 :                         unsigned int dcfclk_sta_targets[DC__VOLTAGE_STATES] = {199, 615, 906, 1324, 1564};
    2211           0 :                         unsigned int num_dcfclk_sta_targets = 4, num_uclk_states = 0;
    2212           0 :                         unsigned int max_dcfclk_mhz = 0, max_dispclk_mhz = 0, max_dppclk_mhz = 0, max_phyclk_mhz = 0;
    2213             : 
    2214           0 :                         for (i = 0; i < MAX_NUM_DPM_LVL; i++) {
    2215           0 :                                 if (bw_params->clk_table.entries[i].dcfclk_mhz > max_dcfclk_mhz)
    2216           0 :                                         max_dcfclk_mhz = bw_params->clk_table.entries[i].dcfclk_mhz;
    2217           0 :                                 if (bw_params->clk_table.entries[i].dcfclk_mhz != 0 &&
    2218             :                                                 bw_params->clk_table.entries[i].dcfclk_mhz < min_dcfclk)
    2219           0 :                                         min_dcfclk = bw_params->clk_table.entries[i].dcfclk_mhz;
    2220           0 :                                 if (bw_params->clk_table.entries[i].dispclk_mhz > max_dispclk_mhz)
    2221           0 :                                         max_dispclk_mhz = bw_params->clk_table.entries[i].dispclk_mhz;
    2222           0 :                                 if (bw_params->clk_table.entries[i].dppclk_mhz > max_dppclk_mhz)
    2223           0 :                                         max_dppclk_mhz = bw_params->clk_table.entries[i].dppclk_mhz;
    2224           0 :                                 if (bw_params->clk_table.entries[i].phyclk_mhz > max_phyclk_mhz)
    2225           0 :                                         max_phyclk_mhz = bw_params->clk_table.entries[i].phyclk_mhz;
    2226             :                         }
    2227           0 :                         if (min_dcfclk > dcfclk_sta_targets[0])
    2228           0 :                                 dcfclk_sta_targets[0] = min_dcfclk;
    2229           0 :                         if (!max_dcfclk_mhz)
    2230           0 :                                 max_dcfclk_mhz = dcn3_2_soc.clock_limits[0].dcfclk_mhz;
    2231           0 :                         if (!max_dispclk_mhz)
    2232           0 :                                 max_dispclk_mhz = dcn3_2_soc.clock_limits[0].dispclk_mhz;
    2233           0 :                         if (!max_dppclk_mhz)
    2234           0 :                                 max_dppclk_mhz = dcn3_2_soc.clock_limits[0].dppclk_mhz;
    2235           0 :                         if (!max_phyclk_mhz)
    2236           0 :                                 max_phyclk_mhz = dcn3_2_soc.clock_limits[0].phyclk_mhz;
    2237             : 
    2238           0 :                         if (max_dcfclk_mhz > dcfclk_sta_targets[num_dcfclk_sta_targets-1]) {
    2239             :                                 // If max DCFCLK is greater than the max DCFCLK STA target, insert into the DCFCLK STA target array
    2240           0 :                                 dcfclk_sta_targets[num_dcfclk_sta_targets] = max_dcfclk_mhz;
    2241           0 :                                 num_dcfclk_sta_targets++;
    2242           0 :                         } else if (max_dcfclk_mhz < dcfclk_sta_targets[num_dcfclk_sta_targets-1]) {
    2243             :                                 // If max DCFCLK is less than the max DCFCLK STA target, cap values and remove duplicates
    2244           0 :                                 for (i = 0; i < num_dcfclk_sta_targets; i++) {
    2245           0 :                                         if (dcfclk_sta_targets[i] > max_dcfclk_mhz) {
    2246           0 :                                                 dcfclk_sta_targets[i] = max_dcfclk_mhz;
    2247           0 :                                                 break;
    2248             :                                         }
    2249             :                                 }
    2250             :                                 // Update size of array since we "removed" duplicates
    2251           0 :                                 num_dcfclk_sta_targets = i + 1;
    2252             :                         }
    2253             : 
    2254           0 :                         num_uclk_states = bw_params->clk_table.num_entries;
    2255             : 
    2256             :                         // Calculate optimal dcfclk for each uclk
    2257           0 :                         for (i = 0; i < num_uclk_states; i++) {
    2258           0 :                                 dcn32_get_optimal_dcfclk_fclk_for_uclk(bw_params->clk_table.entries[i].memclk_mhz * 16,
    2259             :                                                 &optimal_dcfclk_for_uclk[i], NULL);
    2260           0 :                                 if (optimal_dcfclk_for_uclk[i] < bw_params->clk_table.entries[0].dcfclk_mhz) {
    2261           0 :                                         optimal_dcfclk_for_uclk[i] = bw_params->clk_table.entries[0].dcfclk_mhz;
    2262             :                                 }
    2263             :                         }
    2264             : 
    2265             :                         // Calculate optimal uclk for each dcfclk sta target
    2266           0 :                         for (i = 0; i < num_dcfclk_sta_targets; i++) {
    2267           0 :                                 for (j = 0; j < num_uclk_states; j++) {
    2268           0 :                                         if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j]) {
    2269           0 :                                                 optimal_uclk_for_dcfclk_sta_targets[i] =
    2270           0 :                                                                 bw_params->clk_table.entries[j].memclk_mhz * 16;
    2271           0 :                                                 break;
    2272             :                                         }
    2273             :                                 }
    2274             :                         }
    2275             : 
    2276             :                         i = 0;
    2277             :                         j = 0;
    2278             :                         // create the final dcfclk and uclk table
    2279           0 :                         while (i < num_dcfclk_sta_targets && j < num_uclk_states && num_states < DC__VOLTAGE_STATES) {
    2280           0 :                                 if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j] && i < num_dcfclk_sta_targets) {
    2281           0 :                                         dcfclk_mhz[num_states] = dcfclk_sta_targets[i];
    2282           0 :                                         dram_speed_mts[num_states++] = optimal_uclk_for_dcfclk_sta_targets[i++];
    2283             :                                 } else {
    2284           0 :                                         if (j < num_uclk_states && optimal_dcfclk_for_uclk[j] <= max_dcfclk_mhz) {
    2285           0 :                                                 dcfclk_mhz[num_states] = optimal_dcfclk_for_uclk[j];
    2286           0 :                                                 dram_speed_mts[num_states++] = bw_params->clk_table.entries[j++].memclk_mhz * 16;
    2287             :                                         } else {
    2288             :                                                 j = num_uclk_states;
    2289             :                                         }
    2290             :                                 }
    2291             :                         }
    2292             : 
    2293           0 :                         while (i < num_dcfclk_sta_targets && num_states < DC__VOLTAGE_STATES) {
    2294           0 :                                 dcfclk_mhz[num_states] = dcfclk_sta_targets[i];
    2295           0 :                                 dram_speed_mts[num_states++] = optimal_uclk_for_dcfclk_sta_targets[i++];
    2296             :                         }
    2297             : 
    2298           0 :                         while (j < num_uclk_states && num_states < DC__VOLTAGE_STATES &&
    2299           0 :                                         optimal_dcfclk_for_uclk[j] <= max_dcfclk_mhz) {
    2300           0 :                                 dcfclk_mhz[num_states] = optimal_dcfclk_for_uclk[j];
    2301           0 :                                 dram_speed_mts[num_states++] = bw_params->clk_table.entries[j++].memclk_mhz * 16;
    2302             :                         }
    2303             : 
    2304           0 :                         dcn3_2_soc.num_states = num_states;
    2305           0 :                         for (i = 0; i < dcn3_2_soc.num_states; i++) {
    2306           0 :                                 dcn3_2_soc.clock_limits[i].state = i;
    2307           0 :                                 dcn3_2_soc.clock_limits[i].dcfclk_mhz = dcfclk_mhz[i];
    2308           0 :                                 dcn3_2_soc.clock_limits[i].fabricclk_mhz = dcfclk_mhz[i];
    2309             : 
    2310             :                                 /* Fill all states with max values of all these clocks */
    2311           0 :                                 dcn3_2_soc.clock_limits[i].dispclk_mhz = max_dispclk_mhz;
    2312           0 :                                 dcn3_2_soc.clock_limits[i].dppclk_mhz  = max_dppclk_mhz;
    2313           0 :                                 dcn3_2_soc.clock_limits[i].phyclk_mhz  = max_phyclk_mhz;
    2314           0 :                                 dcn3_2_soc.clock_limits[i].dscclk_mhz  = max_dispclk_mhz / 3;
    2315             : 
    2316             :                                 /* Populate from bw_params for DTBCLK, SOCCLK */
    2317           0 :                                 if (i > 0) {
    2318           0 :                                         if (!bw_params->clk_table.entries[i].dtbclk_mhz) {
    2319           0 :                                                 dcn3_2_soc.clock_limits[i].dtbclk_mhz  = dcn3_2_soc.clock_limits[i-1].dtbclk_mhz;
    2320             :                                         } else {
    2321           0 :                                                 dcn3_2_soc.clock_limits[i].dtbclk_mhz  = bw_params->clk_table.entries[i].dtbclk_mhz;
    2322             :                                         }
    2323           0 :                                 } else if (bw_params->clk_table.entries[i].dtbclk_mhz) {
    2324           0 :                                         dcn3_2_soc.clock_limits[i].dtbclk_mhz  = bw_params->clk_table.entries[i].dtbclk_mhz;
    2325             :                                 }
    2326             : 
    2327           0 :                                 if (!bw_params->clk_table.entries[i].socclk_mhz && i > 0)
    2328           0 :                                         dcn3_2_soc.clock_limits[i].socclk_mhz = dcn3_2_soc.clock_limits[i-1].socclk_mhz;
    2329             :                                 else
    2330           0 :                                         dcn3_2_soc.clock_limits[i].socclk_mhz = bw_params->clk_table.entries[i].socclk_mhz;
    2331             : 
    2332           0 :                                 if (!dram_speed_mts[i] && i > 0)
    2333           0 :                                         dcn3_2_soc.clock_limits[i].dram_speed_mts = dcn3_2_soc.clock_limits[i-1].dram_speed_mts;
    2334             :                                 else
    2335           0 :                                         dcn3_2_soc.clock_limits[i].dram_speed_mts = dram_speed_mts[i];
    2336             : 
    2337             :                                 /* These clocks cannot come from bw_params, always fill from dcn3_2_soc[0] */
    2338             :                                 /* PHYCLK_D18, PHYCLK_D32 */
    2339           0 :                                 dcn3_2_soc.clock_limits[i].phyclk_d18_mhz = dcn3_2_soc.clock_limits[0].phyclk_d18_mhz;
    2340           0 :                                 dcn3_2_soc.clock_limits[i].phyclk_d32_mhz = dcn3_2_soc.clock_limits[0].phyclk_d32_mhz;
    2341             :                         }
    2342             :                 } else {
    2343           0 :                         build_synthetic_soc_states(bw_params, dcn3_2_soc.clock_limits, &dcn3_2_soc.num_states);
    2344             :                 }
    2345             : 
    2346             :                 /* Re-init DML with updated bb */
    2347           0 :                 dml_init_instance(&dc->dml, &dcn3_2_soc, &dcn3_2_ip, DML_PROJECT_DCN32);
    2348           0 :                 if (dc->current_state)
    2349           0 :                         dml_init_instance(&dc->current_state->bw_ctx.dml, &dcn3_2_soc, &dcn3_2_ip, DML_PROJECT_DCN32);
    2350             :         }
    2351           0 : }
    2352             : 

Generated by: LCOV version 1.14