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