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