brintos

brintos / linux-shallow public Read only

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