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 | |
9f5171ce AL |
476 | /* Add 16 lines margin to the MALL REGION because SUB_VP_START_LINE must be aligned |
477 | * to 2 swaths (i.e. 16 lines) | |
478 | */ | |
8f5bb69d | 479 | phantom_vactive = get_subviewport_lines_needed_in_mall(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx) + |
9f5171ce | 480 | pstate_width_fw_delay_lines + dc->caps.subvp_swath_height_margin_lines; |
8f5bb69d RS |
481 | |
482 | // For backporch of phantom pipe, use vstartup of the main pipe | |
483 | phantom_bp = get_vstartup(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx); | |
484 | ||
485 | phantom_stream->dst.y = 0; | |
486 | phantom_stream->dst.height = phantom_vactive; | |
487 | phantom_stream->src.y = 0; | |
488 | phantom_stream->src.height = phantom_vactive; | |
489 | ||
490 | phantom_stream->timing.v_addressable = phantom_vactive; | |
491 | phantom_stream->timing.v_front_porch = 1; | |
492 | phantom_stream->timing.v_total = phantom_stream->timing.v_addressable + | |
493 | phantom_stream->timing.v_front_porch + | |
494 | phantom_stream->timing.v_sync_width + | |
495 | phantom_bp; | |
31ec699a | 496 | phantom_stream->timing.flags.DSC = 0; // Don't need DSC for phantom timing |
8f5bb69d RS |
497 | } |
498 | ||
4cef2269 RS |
499 | /** |
500 | * dcn32_get_num_free_pipes: Calculate number of free pipes | |
501 | * | |
502 | * This function assumes that a "used" pipe is a pipe that has | |
503 | * both a stream and a plane assigned to it. | |
504 | * | |
505 | * @dc: current dc state | |
506 | * @context: new dc state | |
507 | * | |
508 | * Return: | |
509 | * Number of free pipes available in the context | |
510 | */ | |
511 | static unsigned int dcn32_get_num_free_pipes(struct dc *dc, struct dc_state *context) | |
512 | { | |
513 | unsigned int i; | |
514 | unsigned int free_pipes = 0; | |
515 | unsigned int num_pipes = 0; | |
516 | ||
517 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
518 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; | |
519 | ||
520 | if (pipe->stream && !pipe->top_pipe) { | |
521 | while (pipe) { | |
522 | num_pipes++; | |
523 | pipe = pipe->bottom_pipe; | |
524 | } | |
525 | } | |
526 | } | |
527 | ||
528 | free_pipes = dc->res_pool->pipe_count - num_pipes; | |
529 | return free_pipes; | |
530 | } | |
531 | ||
532 | /** | |
533 | * dcn32_assign_subvp_pipe: Function to decide which pipe will use Sub-VP. | |
534 | * | |
535 | * We enter this function if we are Sub-VP capable (i.e. enough pipes available) | |
536 | * and regular P-State switching (i.e. VACTIVE/VBLANK) is not supported, or if | |
537 | * we are forcing SubVP P-State switching on the current config. | |
538 | * | |
539 | * The number of pipes used for the chosen surface must be less than or equal to the | |
540 | * number of free pipes available. | |
541 | * | |
542 | * In general we choose surfaces with the longest frame time first (better for SubVP + VBLANK). | |
543 | * For multi-display cases the ActiveDRAMClockChangeMargin doesn't provide enough info on its own | |
544 | * for determining which should be the SubVP pipe (need a way to determine if a pipe / plane doesn't | |
545 | * support MCLK switching naturally [i.e. ACTIVE or VBLANK]). | |
546 | * | |
547 | * @param dc: current dc state | |
548 | * @param context: new dc state | |
549 | * @param index: [out] dc pipe index for the pipe chosen to have phantom pipes assigned | |
550 | * | |
551 | * Return: | |
552 | * True if a valid pipe assignment was found for Sub-VP. Otherwise false. | |
553 | */ | |
554 | static bool dcn32_assign_subvp_pipe(struct dc *dc, | |
555 | struct dc_state *context, | |
556 | unsigned int *index) | |
557 | { | |
558 | unsigned int i, pipe_idx; | |
559 | unsigned int max_frame_time = 0; | |
560 | bool valid_assignment_found = false; | |
561 | unsigned int free_pipes = dcn32_get_num_free_pipes(dc, context); | |
562 | bool current_assignment_freesync = false; | |
a3c9b4c7 | 563 | struct vba_vars_st *vba = &context->bw_ctx.dml.vba; |
4cef2269 RS |
564 | |
565 | for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) { | |
566 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; | |
567 | unsigned int num_pipes = 0; | |
568 | unsigned int refresh_rate = 0; | |
569 | ||
570 | if (!pipe->stream) | |
571 | continue; | |
572 | ||
573 | // Round up | |
574 | refresh_rate = (pipe->stream->timing.pix_clk_100hz * 100 + | |
575 | pipe->stream->timing.v_total * pipe->stream->timing.h_total - 1) | |
576 | / (double)(pipe->stream->timing.v_total * pipe->stream->timing.h_total); | |
a3c9b4c7 AL |
577 | /* SubVP pipe candidate requirements: |
578 | * - Refresh rate < 120hz | |
579 | * - Not able to switch in vactive naturally (switching in active means the | |
580 | * DET provides enough buffer to hide the P-State switch latency -- trying | |
581 | * to combine this with SubVP can cause issues with the scheduling). | |
582 | */ | |
4cef2269 | 583 | if (pipe->plane_state && !pipe->top_pipe && |
a3c9b4c7 AL |
584 | pipe->stream->mall_stream_config.type == SUBVP_NONE && refresh_rate < 120 && |
585 | vba->ActiveDRAMClockChangeLatencyMarginPerState[vba->VoltageLevel][vba->maxMpcComb][vba->pipe_plane[pipe_idx]] <= 0) { | |
4cef2269 RS |
586 | while (pipe) { |
587 | num_pipes++; | |
588 | pipe = pipe->bottom_pipe; | |
589 | } | |
590 | ||
591 | pipe = &context->res_ctx.pipe_ctx[i]; | |
592 | if (num_pipes <= free_pipes) { | |
593 | struct dc_stream_state *stream = pipe->stream; | |
594 | unsigned int frame_us = (stream->timing.v_total * stream->timing.h_total / | |
595 | (double)(stream->timing.pix_clk_100hz * 100)) * 1000000; | |
596 | if (frame_us > max_frame_time && !stream->ignore_msa_timing_param) { | |
597 | *index = i; | |
598 | max_frame_time = frame_us; | |
599 | valid_assignment_found = true; | |
600 | current_assignment_freesync = false; | |
601 | /* For the 2-Freesync display case, still choose the one with the | |
602 | * longest frame time | |
603 | */ | |
604 | } else if (stream->ignore_msa_timing_param && (!valid_assignment_found || | |
605 | (current_assignment_freesync && frame_us > max_frame_time))) { | |
606 | *index = i; | |
607 | valid_assignment_found = true; | |
608 | current_assignment_freesync = true; | |
609 | } | |
610 | } | |
611 | } | |
612 | pipe_idx++; | |
613 | } | |
614 | return valid_assignment_found; | |
615 | } | |
616 | ||
617 | /** | |
618 | * dcn32_enough_pipes_for_subvp: Function to check if there are "enough" pipes for SubVP. | |
619 | * | |
620 | * This function returns true if there are enough free pipes | |
621 | * to create the required phantom pipes for any given stream | |
622 | * (that does not already have phantom pipe assigned). | |
623 | * | |
624 | * e.g. For a 2 stream config where the first stream uses one | |
625 | * pipe and the second stream uses 2 pipes (i.e. pipe split), | |
626 | * this function will return true because there is 1 remaining | |
627 | * pipe which can be used as the phantom pipe for the non pipe | |
628 | * split pipe. | |
629 | * | |
630 | * @dc: current dc state | |
631 | * @context: new dc state | |
632 | * | |
633 | * Return: | |
634 | * True if there are enough free pipes to assign phantom pipes to at least one | |
635 | * stream that does not already have phantom pipes assigned. Otherwise false. | |
636 | */ | |
637 | static bool dcn32_enough_pipes_for_subvp(struct dc *dc, struct dc_state *context) | |
638 | { | |
639 | unsigned int i, split_cnt, free_pipes; | |
640 | unsigned int min_pipe_split = dc->res_pool->pipe_count + 1; // init as max number of pipes + 1 | |
641 | bool subvp_possible = false; | |
642 | ||
643 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
644 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; | |
645 | ||
646 | // Find the minimum pipe split count for non SubVP pipes | |
647 | if (pipe->stream && !pipe->top_pipe && | |
648 | pipe->stream->mall_stream_config.type == SUBVP_NONE) { | |
649 | split_cnt = 0; | |
650 | while (pipe) { | |
651 | split_cnt++; | |
652 | pipe = pipe->bottom_pipe; | |
653 | } | |
654 | ||
655 | if (split_cnt < min_pipe_split) | |
656 | min_pipe_split = split_cnt; | |
657 | } | |
658 | } | |
659 | ||
660 | free_pipes = dcn32_get_num_free_pipes(dc, context); | |
661 | ||
662 | // SubVP only possible if at least one pipe is being used (i.e. free_pipes | |
663 | // should not equal to the pipe_count) | |
664 | if (free_pipes >= min_pipe_split && free_pipes < dc->res_pool->pipe_count) | |
665 | subvp_possible = true; | |
666 | ||
667 | return subvp_possible; | |
668 | } | |
669 | ||
670 | /** | |
671 | * subvp_subvp_schedulable: Determine if SubVP + SubVP config is schedulable | |
672 | * | |
673 | * High level algorithm: | |
674 | * 1. Find longest microschedule length (in us) between the two SubVP pipes | |
675 | * 2. Check if the worst case overlap (VBLANK in middle of ACTIVE) for both | |
676 | * pipes still allows for the maximum microschedule to fit in the active | |
677 | * region for both pipes. | |
678 | * | |
679 | * @dc: current dc state | |
680 | * @context: new dc state | |
681 | * | |
682 | * Return: | |
683 | * bool - True if the SubVP + SubVP config is schedulable, false otherwise | |
684 | */ | |
685 | static bool subvp_subvp_schedulable(struct dc *dc, struct dc_state *context) | |
686 | { | |
687 | struct pipe_ctx *subvp_pipes[2]; | |
688 | struct dc_stream_state *phantom = NULL; | |
689 | uint32_t microschedule_lines = 0; | |
690 | uint32_t index = 0; | |
691 | uint32_t i; | |
692 | uint32_t max_microschedule_us = 0; | |
693 | int32_t vactive1_us, vactive2_us, vblank1_us, vblank2_us; | |
694 | ||
695 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
696 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; | |
697 | uint32_t time_us = 0; | |
698 | ||
699 | /* Loop to calculate the maximum microschedule time between the two SubVP pipes, | |
700 | * and also to store the two main SubVP pipe pointers in subvp_pipes[2]. | |
701 | */ | |
702 | if (pipe->stream && pipe->plane_state && !pipe->top_pipe && | |
703 | pipe->stream->mall_stream_config.type == SUBVP_MAIN) { | |
704 | phantom = pipe->stream->mall_stream_config.paired_stream; | |
705 | microschedule_lines = (phantom->timing.v_total - phantom->timing.v_front_porch) + | |
706 | phantom->timing.v_addressable; | |
707 | ||
708 | // Round up when calculating microschedule time (+ 1 at the end) | |
709 | time_us = (microschedule_lines * phantom->timing.h_total) / | |
710 | (double)(phantom->timing.pix_clk_100hz * 100) * 1000000 + | |
711 | dc->caps.subvp_prefetch_end_to_mall_start_us + | |
712 | dc->caps.subvp_fw_processing_delay_us + 1; | |
713 | if (time_us > max_microschedule_us) | |
714 | max_microschedule_us = time_us; | |
715 | ||
716 | subvp_pipes[index] = pipe; | |
717 | index++; | |
718 | ||
719 | // Maximum 2 SubVP pipes | |
720 | if (index == 2) | |
721 | break; | |
722 | } | |
723 | } | |
724 | vactive1_us = ((subvp_pipes[0]->stream->timing.v_addressable * subvp_pipes[0]->stream->timing.h_total) / | |
725 | (double)(subvp_pipes[0]->stream->timing.pix_clk_100hz * 100)) * 1000000; | |
726 | vactive2_us = ((subvp_pipes[1]->stream->timing.v_addressable * subvp_pipes[1]->stream->timing.h_total) / | |
727 | (double)(subvp_pipes[1]->stream->timing.pix_clk_100hz * 100)) * 1000000; | |
728 | vblank1_us = (((subvp_pipes[0]->stream->timing.v_total - subvp_pipes[0]->stream->timing.v_addressable) * | |
729 | subvp_pipes[0]->stream->timing.h_total) / | |
730 | (double)(subvp_pipes[0]->stream->timing.pix_clk_100hz * 100)) * 1000000; | |
731 | vblank2_us = (((subvp_pipes[1]->stream->timing.v_total - subvp_pipes[1]->stream->timing.v_addressable) * | |
732 | subvp_pipes[1]->stream->timing.h_total) / | |
733 | (double)(subvp_pipes[1]->stream->timing.pix_clk_100hz * 100)) * 1000000; | |
734 | ||
735 | if ((vactive1_us - vblank2_us) / 2 > max_microschedule_us && | |
736 | (vactive2_us - vblank1_us) / 2 > max_microschedule_us) | |
737 | return true; | |
738 | ||
739 | return false; | |
740 | } | |
741 | ||
742 | /** | |
743 | * subvp_drr_schedulable: Determine if SubVP + DRR config is schedulable | |
744 | * | |
745 | * High level algorithm: | |
746 | * 1. Get timing for SubVP pipe, phantom pipe, and DRR pipe | |
747 | * 2. Determine the frame time for the DRR display when adding required margin for MCLK switching | |
748 | * (the margin is equal to the MALL region + DRR margin (500us)) | |
749 | * 3.If (SubVP Active - Prefetch > Stretched DRR frame + max(MALL region, Stretched DRR frame)) | |
750 | * then report the configuration as supported | |
751 | * | |
752 | * @dc: current dc state | |
753 | * @context: new dc state | |
754 | * @drr_pipe: DRR pipe_ctx for the SubVP + DRR config | |
755 | * | |
756 | * Return: | |
757 | * bool - True if the SubVP + DRR config is schedulable, false otherwise | |
758 | */ | |
759 | static bool subvp_drr_schedulable(struct dc *dc, struct dc_state *context, struct pipe_ctx *drr_pipe) | |
760 | { | |
761 | bool schedulable = false; | |
762 | uint32_t i; | |
763 | struct pipe_ctx *pipe = NULL; | |
764 | struct dc_crtc_timing *main_timing = NULL; | |
765 | struct dc_crtc_timing *phantom_timing = NULL; | |
766 | struct dc_crtc_timing *drr_timing = NULL; | |
767 | int16_t prefetch_us = 0; | |
768 | int16_t mall_region_us = 0; | |
769 | int16_t drr_frame_us = 0; // nominal frame time | |
770 | int16_t subvp_active_us = 0; | |
771 | int16_t stretched_drr_us = 0; | |
772 | int16_t drr_stretched_vblank_us = 0; | |
773 | int16_t max_vblank_mallregion = 0; | |
774 | ||
775 | // Find SubVP pipe | |
776 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
777 | pipe = &context->res_ctx.pipe_ctx[i]; | |
778 | ||
779 | // We check for master pipe, but it shouldn't matter since we only need | |
780 | // the pipe for timing info (stream should be same for any pipe splits) | |
781 | if (!pipe->stream || !pipe->plane_state || pipe->top_pipe || pipe->prev_odm_pipe) | |
782 | continue; | |
783 | ||
784 | // Find the SubVP pipe | |
785 | if (pipe->stream->mall_stream_config.type == SUBVP_MAIN) | |
786 | break; | |
787 | } | |
788 | ||
789 | main_timing = &pipe->stream->timing; | |
790 | phantom_timing = &pipe->stream->mall_stream_config.paired_stream->timing; | |
791 | drr_timing = &drr_pipe->stream->timing; | |
792 | prefetch_us = (phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total / | |
793 | (double)(phantom_timing->pix_clk_100hz * 100) * 1000000 + | |
794 | dc->caps.subvp_prefetch_end_to_mall_start_us; | |
795 | subvp_active_us = main_timing->v_addressable * main_timing->h_total / | |
796 | (double)(main_timing->pix_clk_100hz * 100) * 1000000; | |
797 | drr_frame_us = drr_timing->v_total * drr_timing->h_total / | |
798 | (double)(drr_timing->pix_clk_100hz * 100) * 1000000; | |
799 | // P-State allow width and FW delays already included phantom_timing->v_addressable | |
800 | mall_region_us = phantom_timing->v_addressable * phantom_timing->h_total / | |
801 | (double)(phantom_timing->pix_clk_100hz * 100) * 1000000; | |
802 | stretched_drr_us = drr_frame_us + mall_region_us + SUBVP_DRR_MARGIN_US; | |
803 | drr_stretched_vblank_us = (drr_timing->v_total - drr_timing->v_addressable) * drr_timing->h_total / | |
804 | (double)(drr_timing->pix_clk_100hz * 100) * 1000000 + (stretched_drr_us - drr_frame_us); | |
805 | max_vblank_mallregion = drr_stretched_vblank_us > mall_region_us ? drr_stretched_vblank_us : mall_region_us; | |
806 | ||
807 | /* We consider SubVP + DRR schedulable if the stretched frame duration of the DRR display (i.e. the | |
808 | * highest refresh rate + margin that can support UCLK P-State switch) passes the static analysis | |
809 | * for VBLANK: (VACTIVE region of the SubVP pipe can fit the MALL prefetch, VBLANK frame time, | |
810 | * and the max of (VBLANK blanking time, MALL region)). | |
811 | */ | |
812 | if (stretched_drr_us < (1 / (double)drr_timing->min_refresh_in_uhz) * 1000000 * 1000000 && | |
813 | subvp_active_us - prefetch_us - stretched_drr_us - max_vblank_mallregion > 0) | |
814 | schedulable = true; | |
815 | ||
816 | return schedulable; | |
817 | } | |
818 | ||
819 | ||
820 | /** | |
821 | * subvp_vblank_schedulable: Determine if SubVP + VBLANK config is schedulable | |
822 | * | |
823 | * High level algorithm: | |
824 | * 1. Get timing for SubVP pipe, phantom pipe, and VBLANK pipe | |
825 | * 2. If (SubVP Active - Prefetch > Vblank Frame Time + max(MALL region, Vblank blanking time)) | |
826 | * then report the configuration as supported | |
827 | * 3. If the VBLANK display is DRR, then take the DRR static schedulability path | |
828 | * | |
829 | * @dc: current dc state | |
830 | * @context: new dc state | |
831 | * | |
832 | * Return: | |
833 | * bool - True if the SubVP + VBLANK/DRR config is schedulable, false otherwise | |
834 | */ | |
835 | static bool subvp_vblank_schedulable(struct dc *dc, struct dc_state *context) | |
836 | { | |
837 | struct pipe_ctx *pipe = NULL; | |
838 | struct pipe_ctx *subvp_pipe = NULL; | |
839 | bool found = false; | |
840 | bool schedulable = false; | |
841 | uint32_t i = 0; | |
842 | uint8_t vblank_index = 0; | |
843 | uint16_t prefetch_us = 0; | |
844 | uint16_t mall_region_us = 0; | |
845 | uint16_t vblank_frame_us = 0; | |
846 | uint16_t subvp_active_us = 0; | |
847 | uint16_t vblank_blank_us = 0; | |
848 | uint16_t max_vblank_mallregion = 0; | |
849 | struct dc_crtc_timing *main_timing = NULL; | |
850 | struct dc_crtc_timing *phantom_timing = NULL; | |
851 | struct dc_crtc_timing *vblank_timing = NULL; | |
852 | ||
853 | /* For SubVP + VBLANK/DRR cases, we assume there can only be | |
854 | * a single VBLANK/DRR display. If DML outputs SubVP + VBLANK | |
855 | * is supported, it is either a single VBLANK case or two VBLANK | |
856 | * displays which are synchronized (in which case they have identical | |
857 | * timings). | |
858 | */ | |
859 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
860 | pipe = &context->res_ctx.pipe_ctx[i]; | |
861 | ||
862 | // We check for master pipe, but it shouldn't matter since we only need | |
863 | // the pipe for timing info (stream should be same for any pipe splits) | |
864 | if (!pipe->stream || !pipe->plane_state || pipe->top_pipe || pipe->prev_odm_pipe) | |
865 | continue; | |
866 | ||
867 | if (!found && pipe->stream->mall_stream_config.type == SUBVP_NONE) { | |
868 | // Found pipe which is not SubVP or Phantom (i.e. the VBLANK pipe). | |
869 | vblank_index = i; | |
870 | found = true; | |
871 | } | |
872 | ||
873 | if (!subvp_pipe && pipe->stream->mall_stream_config.type == SUBVP_MAIN) | |
874 | subvp_pipe = pipe; | |
875 | } | |
876 | // Use ignore_msa_timing_param flag to identify as DRR | |
877 | if (found && context->res_ctx.pipe_ctx[vblank_index].stream->ignore_msa_timing_param) { | |
878 | // SUBVP + DRR case | |
879 | schedulable = subvp_drr_schedulable(dc, context, &context->res_ctx.pipe_ctx[vblank_index]); | |
880 | } else if (found) { | |
881 | main_timing = &subvp_pipe->stream->timing; | |
882 | phantom_timing = &subvp_pipe->stream->mall_stream_config.paired_stream->timing; | |
883 | vblank_timing = &context->res_ctx.pipe_ctx[vblank_index].stream->timing; | |
884 | // Prefetch time is equal to VACTIVE + BP + VSYNC of the phantom pipe | |
885 | // Also include the prefetch end to mallstart delay time | |
886 | prefetch_us = (phantom_timing->v_total - phantom_timing->v_front_porch) * phantom_timing->h_total / | |
887 | (double)(phantom_timing->pix_clk_100hz * 100) * 1000000 + | |
888 | dc->caps.subvp_prefetch_end_to_mall_start_us; | |
889 | // P-State allow width and FW delays already included phantom_timing->v_addressable | |
890 | mall_region_us = phantom_timing->v_addressable * phantom_timing->h_total / | |
891 | (double)(phantom_timing->pix_clk_100hz * 100) * 1000000; | |
892 | vblank_frame_us = vblank_timing->v_total * vblank_timing->h_total / | |
893 | (double)(vblank_timing->pix_clk_100hz * 100) * 1000000; | |
894 | vblank_blank_us = (vblank_timing->v_total - vblank_timing->v_addressable) * vblank_timing->h_total / | |
895 | (double)(vblank_timing->pix_clk_100hz * 100) * 1000000; | |
896 | subvp_active_us = main_timing->v_addressable * main_timing->h_total / | |
897 | (double)(main_timing->pix_clk_100hz * 100) * 1000000; | |
898 | max_vblank_mallregion = vblank_blank_us > mall_region_us ? vblank_blank_us : mall_region_us; | |
899 | ||
900 | // Schedulable if VACTIVE region of the SubVP pipe can fit the MALL prefetch, VBLANK frame time, | |
901 | // and the max of (VBLANK blanking time, MALL region) | |
902 | // TODO: Possibly add some margin (i.e. the below conditions should be [...] > X instead of [...] > 0) | |
903 | if (subvp_active_us - prefetch_us - vblank_frame_us - max_vblank_mallregion > 0) | |
904 | schedulable = true; | |
905 | } | |
906 | return schedulable; | |
907 | } | |
908 | ||
909 | /** | |
910 | * subvp_validate_static_schedulability: Check which SubVP case is calculated and handle | |
911 | * static analysis based on the case. | |
912 | * | |
913 | * Three cases: | |
914 | * 1. SubVP + SubVP | |
915 | * 2. SubVP + VBLANK (DRR checked internally) | |
916 | * 3. SubVP + VACTIVE (currently unsupported) | |
917 | * | |
918 | * @dc: current dc state | |
919 | * @context: new dc state | |
920 | * @vlevel: Voltage level calculated by DML | |
921 | * | |
922 | * Return: | |
923 | * bool - True if statically schedulable, false otherwise | |
924 | */ | |
925 | static bool subvp_validate_static_schedulability(struct dc *dc, | |
926 | struct dc_state *context, | |
927 | int vlevel) | |
928 | { | |
929 | bool schedulable = true; // true by default for single display case | |
930 | struct vba_vars_st *vba = &context->bw_ctx.dml.vba; | |
931 | uint32_t i, pipe_idx; | |
932 | uint8_t subvp_count = 0; | |
933 | uint8_t vactive_count = 0; | |
934 | ||
935 | for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) { | |
936 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; | |
937 | ||
938 | if (!pipe->stream) | |
939 | continue; | |
940 | ||
941 | if (pipe->plane_state && !pipe->top_pipe && | |
942 | pipe->stream->mall_stream_config.type == SUBVP_MAIN) | |
943 | subvp_count++; | |
944 | ||
945 | // Count how many planes that aren't SubVP/phantom are capable of VACTIVE | |
946 | // switching (SubVP + VACTIVE unsupported). In situations where we force | |
947 | // SubVP for a VACTIVE plane, we don't want to increment the vactive_count. | |
948 | if (vba->ActiveDRAMClockChangeLatencyMargin[vba->pipe_plane[pipe_idx]] > 0 && | |
949 | pipe->stream->mall_stream_config.type == SUBVP_NONE) { | |
950 | vactive_count++; | |
951 | } | |
952 | pipe_idx++; | |
953 | } | |
954 | ||
955 | if (subvp_count == 2) { | |
956 | // Static schedulability check for SubVP + SubVP case | |
957 | schedulable = subvp_subvp_schedulable(dc, context); | |
958 | } else if (vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_vblank_w_mall_sub_vp) { | |
959 | // Static schedulability check for SubVP + VBLANK case. Also handle the case where | |
960 | // DML outputs SubVP + VBLANK + VACTIVE (DML will report as SubVP + VBLANK) | |
961 | if (vactive_count > 0) | |
962 | schedulable = false; | |
963 | else | |
964 | schedulable = subvp_vblank_schedulable(dc, context); | |
965 | } else if (vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_vactive_w_mall_sub_vp && | |
966 | vactive_count > 0) { | |
967 | // For single display SubVP cases, DML will output dm_dram_clock_change_vactive_w_mall_sub_vp by default. | |
968 | // We tell the difference between SubVP vs. SubVP + VACTIVE by checking the vactive_count. | |
969 | // SubVP + VACTIVE currently unsupported | |
970 | schedulable = false; | |
971 | } | |
972 | return schedulable; | |
973 | } | |
974 | ||
5b4ee987 | 975 | static void dcn32_full_validate_bw_helper(struct dc *dc, |
4cef2269 RS |
976 | struct dc_state *context, |
977 | display_e2e_pipe_params_st *pipes, | |
978 | int *vlevel, | |
979 | int *split, | |
980 | bool *merge, | |
981 | int *pipe_cnt) | |
982 | { | |
983 | struct vba_vars_st *vba = &context->bw_ctx.dml.vba; | |
984 | unsigned int dc_pipe_idx = 0; | |
985 | bool found_supported_config = false; | |
986 | struct pipe_ctx *pipe = NULL; | |
987 | uint32_t non_subvp_pipes = 0; | |
988 | bool drr_pipe_found = false; | |
989 | uint32_t drr_pipe_index = 0; | |
990 | uint32_t i = 0; | |
991 | ||
992 | dc_assert_fp_enabled(); | |
993 | ||
994 | /* | |
995 | * DML favors voltage over p-state, but we're more interested in | |
996 | * supporting p-state over voltage. We can't support p-state in | |
997 | * prefetch mode > 0 so try capping the prefetch mode to start. | |
86678d46 | 998 | * Override present for testing. |
4cef2269 | 999 | */ |
86678d46 DG |
1000 | if (dc->debug.dml_disallow_alternate_prefetch_modes) |
1001 | context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final = | |
4cef2269 | 1002 | dm_prefetch_support_uclk_fclk_and_stutter; |
86678d46 DG |
1003 | else |
1004 | context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final = | |
1005 | dm_prefetch_support_uclk_fclk_and_stutter_if_possible; | |
1006 | ||
4cef2269 RS |
1007 | *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt); |
1008 | /* This may adjust vlevel and maxMpcComb */ | |
a3c9b4c7 | 1009 | if (*vlevel < context->bw_ctx.dml.soc.num_states) { |
4cef2269 | 1010 | *vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, *vlevel, split, merge); |
a3c9b4c7 AL |
1011 | vba->VoltageLevel = *vlevel; |
1012 | } | |
4cef2269 RS |
1013 | |
1014 | /* Conditions for setting up phantom pipes for SubVP: | |
1015 | * 1. Not force disable SubVP | |
1016 | * 2. Full update (i.e. !fast_validate) | |
1017 | * 3. Enough pipes are available to support SubVP (TODO: Which pipes will use VACTIVE / VBLANK / SUBVP?) | |
1018 | * 4. Display configuration passes validation | |
1019 | * 5. (Config doesn't support MCLK in VACTIVE/VBLANK || dc->debug.force_subvp_mclk_switch) | |
1020 | */ | |
1021 | if (!dc->debug.force_disable_subvp && dcn32_all_pipes_have_stream_and_plane(dc, context) && | |
1022 | !dcn32_mpo_in_use(context) && (*vlevel == context->bw_ctx.dml.soc.num_states || | |
1023 | vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] == dm_dram_clock_change_unsupported || | |
1024 | dc->debug.force_subvp_mclk_switch)) { | |
1025 | ||
1026 | dcn32_merge_pipes_for_subvp(dc, context); | |
e0b859cf | 1027 | // to re-initialize viewport after the pipe merge |
922710a8 | 1028 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
e0b859cf EW |
1029 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; |
1030 | ||
1031 | if (!pipe_ctx->plane_state || !pipe_ctx->stream) | |
1032 | continue; | |
1033 | ||
1034 | resource_build_scaling_params(pipe_ctx); | |
1035 | } | |
4cef2269 RS |
1036 | |
1037 | while (!found_supported_config && dcn32_enough_pipes_for_subvp(dc, context) && | |
1038 | dcn32_assign_subvp_pipe(dc, context, &dc_pipe_idx)) { | |
1039 | /* For the case where *vlevel = num_states, bandwidth validation has failed for this config. | |
1040 | * Adding phantom pipes won't change the validation result, so change the DML input param | |
1041 | * for P-State support before adding phantom pipes and recalculating the DML result. | |
1042 | * However, this case is only applicable for SubVP + DRR cases because the prefetch mode | |
1043 | * will not allow for switch in VBLANK. The DRR display must have it's VBLANK stretched | |
1044 | * enough to support MCLK switching. | |
1045 | */ | |
86678d46 DG |
1046 | if (*vlevel == context->bw_ctx.dml.soc.num_states && |
1047 | context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final == | |
1048 | dm_prefetch_support_uclk_fclk_and_stutter) { | |
4cef2269 RS |
1049 | context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final = |
1050 | dm_prefetch_support_stutter; | |
1051 | /* There are params (such as FabricClock) that need to be recalculated | |
1052 | * after validation fails (otherwise it will be 0). Calculation for | |
1053 | * phantom vactive requires call into DML, so we must ensure all the | |
1054 | * vba params are valid otherwise we'll get incorrect phantom vactive. | |
1055 | */ | |
1056 | *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt); | |
1057 | } | |
1058 | ||
1059 | dc->res_pool->funcs->add_phantom_pipes(dc, context, pipes, *pipe_cnt, dc_pipe_idx); | |
1060 | ||
1061 | *pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, false); | |
1062 | // Populate dppclk to trigger a recalculate in dml_get_voltage_level | |
1063 | // so the phantom pipe DLG params can be assigned correctly. | |
1064 | pipes[0].clks_cfg.dppclk_mhz = get_dppclk_calculated(&context->bw_ctx.dml, pipes, *pipe_cnt, 0); | |
1065 | *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt); | |
1066 | ||
1067 | if (*vlevel < context->bw_ctx.dml.soc.num_states && | |
1068 | vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] != dm_dram_clock_change_unsupported | |
1069 | && subvp_validate_static_schedulability(dc, context, *vlevel)) { | |
1070 | found_supported_config = true; | |
1071 | } else if (*vlevel < context->bw_ctx.dml.soc.num_states && | |
1072 | vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] == dm_dram_clock_change_unsupported) { | |
1073 | /* Case where 1 SubVP is added, and DML reports MCLK unsupported. This handles | |
1074 | * the case for SubVP + DRR, where the DRR display does not support MCLK switch | |
1075 | * at it's native refresh rate / timing. | |
1076 | */ | |
1077 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
1078 | pipe = &context->res_ctx.pipe_ctx[i]; | |
1079 | if (pipe->stream && pipe->plane_state && !pipe->top_pipe && | |
1080 | pipe->stream->mall_stream_config.type == SUBVP_NONE) { | |
1081 | non_subvp_pipes++; | |
1082 | // Use ignore_msa_timing_param flag to identify as DRR | |
1083 | if (pipe->stream->ignore_msa_timing_param) { | |
1084 | drr_pipe_found = true; | |
1085 | drr_pipe_index = i; | |
1086 | } | |
1087 | } | |
1088 | } | |
1089 | // If there is only 1 remaining non SubVP pipe that is DRR, check static | |
1090 | // schedulability for SubVP + DRR. | |
1091 | if (non_subvp_pipes == 1 && drr_pipe_found) { | |
1092 | found_supported_config = subvp_drr_schedulable(dc, context, | |
1093 | &context->res_ctx.pipe_ctx[drr_pipe_index]); | |
1094 | } | |
1095 | } | |
1096 | } | |
1097 | ||
1098 | // If SubVP pipe config is unsupported (or cannot be used for UCLK switching) | |
1099 | // remove phantom pipes and repopulate dml pipes | |
1100 | if (!found_supported_config) { | |
1101 | dc->res_pool->funcs->remove_phantom_pipes(dc, context); | |
1102 | vba->DRAMClockChangeSupport[*vlevel][vba->maxMpcComb] = dm_dram_clock_change_unsupported; | |
1103 | *pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, false); | |
f577c7c7 ST |
1104 | |
1105 | *vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, *pipe_cnt); | |
1106 | /* This may adjust vlevel and maxMpcComb */ | |
a3c9b4c7 | 1107 | if (*vlevel < context->bw_ctx.dml.soc.num_states) { |
f577c7c7 | 1108 | *vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, *vlevel, split, merge); |
a3c9b4c7 AL |
1109 | vba->VoltageLevel = *vlevel; |
1110 | } | |
4cef2269 RS |
1111 | } else { |
1112 | // only call dcn20_validate_apply_pipe_split_flags if we found a supported config | |
1113 | memset(split, 0, MAX_PIPES * sizeof(int)); | |
1114 | memset(merge, 0, MAX_PIPES * sizeof(bool)); | |
1115 | *vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, *vlevel, split, merge); | |
a3c9b4c7 | 1116 | vba->VoltageLevel = *vlevel; |
4cef2269 RS |
1117 | |
1118 | // Most populate phantom DLG params before programming hardware / timing for phantom pipe | |
1119 | DC_FP_START(); | |
1120 | dcn32_helper_populate_phantom_dlg_params(dc, context, pipes, *pipe_cnt); | |
1121 | DC_FP_END(); | |
1122 | ||
1123 | // Note: We can't apply the phantom pipes to hardware at this time. We have to wait | |
1124 | // until driver has acquired the DMCUB lock to do it safely. | |
1125 | } | |
1126 | } | |
1127 | } | |
1128 | ||
5b4ee987 RS |
1129 | static bool is_dtbclk_required(struct dc *dc, struct dc_state *context) |
1130 | { | |
1131 | int i; | |
1132 | ||
1133 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
1134 | if (!context->res_ctx.pipe_ctx[i].stream) | |
1135 | continue; | |
1136 | if (is_dp_128b_132b_signal(&context->res_ctx.pipe_ctx[i])) | |
1137 | return true; | |
1138 | } | |
1139 | return false; | |
1140 | } | |
1141 | ||
1142 | static void dcn32_calculate_dlg_params(struct dc *dc, struct dc_state *context, | |
1143 | display_e2e_pipe_params_st *pipes, | |
1144 | int pipe_cnt, int vlevel) | |
1145 | { | |
1146 | int i, pipe_idx; | |
1147 | bool usr_retraining_support = false; | |
1148 | bool unbounded_req_enabled = false; | |
1149 | ||
1150 | dc_assert_fp_enabled(); | |
1151 | ||
1152 | /* Writeback MCIF_WB arbitration parameters */ | |
1153 | dc->res_pool->funcs->set_mcif_arb_params(dc, context, pipes, pipe_cnt); | |
1154 | ||
1155 | context->bw_ctx.bw.dcn.clk.dispclk_khz = context->bw_ctx.dml.vba.DISPCLK * 1000; | |
1156 | context->bw_ctx.bw.dcn.clk.dcfclk_khz = context->bw_ctx.dml.vba.DCFCLK * 1000; | |
1157 | context->bw_ctx.bw.dcn.clk.socclk_khz = context->bw_ctx.dml.vba.SOCCLK * 1000; | |
1158 | context->bw_ctx.bw.dcn.clk.dramclk_khz = context->bw_ctx.dml.vba.DRAMSpeed * 1000 / 16; | |
1159 | context->bw_ctx.bw.dcn.clk.dcfclk_deep_sleep_khz = context->bw_ctx.dml.vba.DCFCLKDeepSleep * 1000; | |
1160 | context->bw_ctx.bw.dcn.clk.fclk_khz = context->bw_ctx.dml.vba.FabricClock * 1000; | |
1161 | context->bw_ctx.bw.dcn.clk.p_state_change_support = | |
1162 | context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] | |
1163 | != dm_dram_clock_change_unsupported; | |
1164 | context->bw_ctx.bw.dcn.clk.num_ways = dcn32_helper_calculate_num_ways_for_subvp(dc, context); | |
1165 | ||
1166 | context->bw_ctx.bw.dcn.clk.dppclk_khz = 0; | |
1167 | context->bw_ctx.bw.dcn.clk.dtbclk_en = is_dtbclk_required(dc, context); | |
1168 | context->bw_ctx.bw.dcn.clk.ref_dtbclk_khz = context->bw_ctx.dml.vba.DTBCLKPerState[vlevel] * 1000; | |
1169 | if (context->bw_ctx.dml.vba.FCLKChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] == dm_fclock_change_unsupported) | |
1170 | context->bw_ctx.bw.dcn.clk.fclk_p_state_change_support = false; | |
1171 | else | |
1172 | context->bw_ctx.bw.dcn.clk.fclk_p_state_change_support = true; | |
1173 | ||
1174 | usr_retraining_support = context->bw_ctx.dml.vba.USRRetrainingSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb]; | |
1175 | ASSERT(usr_retraining_support); | |
1176 | ||
1177 | if (context->bw_ctx.bw.dcn.clk.dispclk_khz < dc->debug.min_disp_clk_khz) | |
1178 | context->bw_ctx.bw.dcn.clk.dispclk_khz = dc->debug.min_disp_clk_khz; | |
1179 | ||
1180 | unbounded_req_enabled = get_unbounded_request_enabled(&context->bw_ctx.dml, pipes, pipe_cnt); | |
1181 | ||
1182 | if (unbounded_req_enabled && pipe_cnt > 1) { | |
1183 | // Unbounded requesting should not ever be used when more than 1 pipe is enabled. | |
1184 | ASSERT(false); | |
1185 | unbounded_req_enabled = false; | |
1186 | } | |
1187 | ||
1188 | for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) { | |
1189 | if (!context->res_ctx.pipe_ctx[i].stream) | |
1190 | continue; | |
1191 | pipes[pipe_idx].pipe.dest.vstartup_start = get_vstartup(&context->bw_ctx.dml, pipes, pipe_cnt, | |
1192 | pipe_idx); | |
1193 | pipes[pipe_idx].pipe.dest.vupdate_offset = get_vupdate_offset(&context->bw_ctx.dml, pipes, pipe_cnt, | |
1194 | pipe_idx); | |
1195 | pipes[pipe_idx].pipe.dest.vupdate_width = get_vupdate_width(&context->bw_ctx.dml, pipes, pipe_cnt, | |
1196 | pipe_idx); | |
1197 | pipes[pipe_idx].pipe.dest.vready_offset = get_vready_offset(&context->bw_ctx.dml, pipes, pipe_cnt, | |
1198 | pipe_idx); | |
1199 | ||
1200 | if (context->res_ctx.pipe_ctx[i].stream->mall_stream_config.type == SUBVP_PHANTOM) { | |
1201 | // Phantom pipe requires that DET_SIZE = 0 and no unbounded requests | |
1202 | context->res_ctx.pipe_ctx[i].det_buffer_size_kb = 0; | |
1203 | context->res_ctx.pipe_ctx[i].unbounded_req = false; | |
1204 | } else { | |
1205 | context->res_ctx.pipe_ctx[i].det_buffer_size_kb = get_det_buffer_size_kbytes(&context->bw_ctx.dml, pipes, pipe_cnt, | |
1206 | pipe_idx); | |
1207 | context->res_ctx.pipe_ctx[i].unbounded_req = unbounded_req_enabled; | |
1208 | } | |
1209 | ||
1210 | if (context->bw_ctx.bw.dcn.clk.dppclk_khz < pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000) | |
1211 | context->bw_ctx.bw.dcn.clk.dppclk_khz = pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000; | |
1212 | context->res_ctx.pipe_ctx[i].plane_res.bw.dppclk_khz = pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000; | |
1213 | context->res_ctx.pipe_ctx[i].pipe_dlg_param = pipes[pipe_idx].pipe.dest; | |
1214 | pipe_idx++; | |
1215 | } | |
1216 | /*save a original dppclock copy*/ | |
1217 | context->bw_ctx.bw.dcn.clk.bw_dppclk_khz = context->bw_ctx.bw.dcn.clk.dppclk_khz; | |
1218 | context->bw_ctx.bw.dcn.clk.bw_dispclk_khz = context->bw_ctx.bw.dcn.clk.dispclk_khz; | |
1219 | context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz = context->bw_ctx.dml.soc.clock_limits[vlevel].dppclk_mhz | |
1220 | * 1000; | |
1221 | context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz = context->bw_ctx.dml.soc.clock_limits[vlevel].dispclk_mhz | |
1222 | * 1000; | |
1223 | ||
1224 | context->bw_ctx.bw.dcn.compbuf_size_kb = context->bw_ctx.dml.ip.config_return_buffer_size_in_kbytes; | |
1225 | ||
1226 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
1227 | if (context->res_ctx.pipe_ctx[i].stream) | |
1228 | context->bw_ctx.bw.dcn.compbuf_size_kb -= context->res_ctx.pipe_ctx[i].det_buffer_size_kb; | |
1229 | } | |
1230 | ||
1231 | for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) { | |
1232 | ||
1233 | if (!context->res_ctx.pipe_ctx[i].stream) | |
1234 | continue; | |
1235 | ||
1236 | context->bw_ctx.dml.funcs.rq_dlg_get_dlg_reg_v2(&context->bw_ctx.dml, | |
1237 | &context->res_ctx.pipe_ctx[i].dlg_regs, &context->res_ctx.pipe_ctx[i].ttu_regs, pipes, | |
1238 | pipe_cnt, pipe_idx); | |
1239 | ||
1240 | context->bw_ctx.dml.funcs.rq_dlg_get_rq_reg_v2(&context->res_ctx.pipe_ctx[i].rq_regs, | |
1241 | &context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx); | |
1242 | pipe_idx++; | |
1243 | } | |
1244 | } | |
1245 | ||
1246 | static struct pipe_ctx *dcn32_find_split_pipe( | |
1247 | struct dc *dc, | |
1248 | struct dc_state *context, | |
1249 | int old_index) | |
1250 | { | |
1251 | struct pipe_ctx *pipe = NULL; | |
1252 | int i; | |
1253 | ||
1254 | if (old_index >= 0 && context->res_ctx.pipe_ctx[old_index].stream == NULL) { | |
1255 | pipe = &context->res_ctx.pipe_ctx[old_index]; | |
1256 | pipe->pipe_idx = old_index; | |
1257 | } | |
1258 | ||
1259 | if (!pipe) | |
1260 | for (i = dc->res_pool->pipe_count - 1; i >= 0; i--) { | |
1261 | if (dc->current_state->res_ctx.pipe_ctx[i].top_pipe == NULL | |
1262 | && dc->current_state->res_ctx.pipe_ctx[i].prev_odm_pipe == NULL) { | |
1263 | if (context->res_ctx.pipe_ctx[i].stream == NULL) { | |
1264 | pipe = &context->res_ctx.pipe_ctx[i]; | |
1265 | pipe->pipe_idx = i; | |
1266 | break; | |
1267 | } | |
1268 | } | |
1269 | } | |
1270 | ||
1271 | /* | |
1272 | * May need to fix pipes getting tossed from 1 opp to another on flip | |
1273 | * Add for debugging transient underflow during topology updates: | |
1274 | * ASSERT(pipe); | |
1275 | */ | |
1276 | if (!pipe) | |
1277 | for (i = dc->res_pool->pipe_count - 1; i >= 0; i--) { | |
1278 | if (context->res_ctx.pipe_ctx[i].stream == NULL) { | |
1279 | pipe = &context->res_ctx.pipe_ctx[i]; | |
1280 | pipe->pipe_idx = i; | |
1281 | break; | |
1282 | } | |
1283 | } | |
1284 | ||
1285 | return pipe; | |
1286 | } | |
1287 | ||
1288 | static bool dcn32_split_stream_for_mpc_or_odm( | |
1289 | const struct dc *dc, | |
1290 | struct resource_context *res_ctx, | |
1291 | struct pipe_ctx *pri_pipe, | |
1292 | struct pipe_ctx *sec_pipe, | |
1293 | bool odm) | |
1294 | { | |
1295 | int pipe_idx = sec_pipe->pipe_idx; | |
1296 | const struct resource_pool *pool = dc->res_pool; | |
1297 | ||
1298 | DC_LOGGER_INIT(dc->ctx->logger); | |
1299 | ||
1300 | if (odm && pri_pipe->plane_state) { | |
1301 | /* ODM + window MPO, where MPO window is on left half only */ | |
1302 | if (pri_pipe->plane_state->clip_rect.x + pri_pipe->plane_state->clip_rect.width <= | |
1303 | pri_pipe->stream->src.x + pri_pipe->stream->src.width/2) { | |
1304 | ||
1305 | DC_LOG_SCALER("%s - ODM + window MPO(left). pri_pipe:%d\n", | |
1306 | __func__, | |
1307 | pri_pipe->pipe_idx); | |
1308 | return true; | |
1309 | } | |
1310 | ||
1311 | /* ODM + window MPO, where MPO window is on right half only */ | |
1312 | if (pri_pipe->plane_state->clip_rect.x >= pri_pipe->stream->src.x + pri_pipe->stream->src.width/2) { | |
1313 | ||
1314 | DC_LOG_SCALER("%s - ODM + window MPO(right). pri_pipe:%d\n", | |
1315 | __func__, | |
1316 | pri_pipe->pipe_idx); | |
1317 | return true; | |
1318 | } | |
1319 | } | |
1320 | ||
1321 | *sec_pipe = *pri_pipe; | |
1322 | ||
1323 | sec_pipe->pipe_idx = pipe_idx; | |
1324 | sec_pipe->plane_res.mi = pool->mis[pipe_idx]; | |
1325 | sec_pipe->plane_res.hubp = pool->hubps[pipe_idx]; | |
1326 | sec_pipe->plane_res.ipp = pool->ipps[pipe_idx]; | |
1327 | sec_pipe->plane_res.xfm = pool->transforms[pipe_idx]; | |
1328 | sec_pipe->plane_res.dpp = pool->dpps[pipe_idx]; | |
1329 | sec_pipe->plane_res.mpcc_inst = pool->dpps[pipe_idx]->inst; | |
1330 | sec_pipe->stream_res.dsc = NULL; | |
1331 | if (odm) { | |
1332 | if (pri_pipe->next_odm_pipe) { | |
1333 | ASSERT(pri_pipe->next_odm_pipe != sec_pipe); | |
1334 | sec_pipe->next_odm_pipe = pri_pipe->next_odm_pipe; | |
1335 | sec_pipe->next_odm_pipe->prev_odm_pipe = sec_pipe; | |
1336 | } | |
1337 | if (pri_pipe->top_pipe && pri_pipe->top_pipe->next_odm_pipe) { | |
1338 | pri_pipe->top_pipe->next_odm_pipe->bottom_pipe = sec_pipe; | |
1339 | sec_pipe->top_pipe = pri_pipe->top_pipe->next_odm_pipe; | |
1340 | } | |
1341 | if (pri_pipe->bottom_pipe && pri_pipe->bottom_pipe->next_odm_pipe) { | |
1342 | pri_pipe->bottom_pipe->next_odm_pipe->top_pipe = sec_pipe; | |
1343 | sec_pipe->bottom_pipe = pri_pipe->bottom_pipe->next_odm_pipe; | |
1344 | } | |
1345 | pri_pipe->next_odm_pipe = sec_pipe; | |
1346 | sec_pipe->prev_odm_pipe = pri_pipe; | |
1347 | ASSERT(sec_pipe->top_pipe == NULL); | |
1348 | ||
1349 | if (!sec_pipe->top_pipe) | |
1350 | sec_pipe->stream_res.opp = pool->opps[pipe_idx]; | |
1351 | else | |
1352 | sec_pipe->stream_res.opp = sec_pipe->top_pipe->stream_res.opp; | |
1353 | if (sec_pipe->stream->timing.flags.DSC == 1) { | |
1354 | dcn20_acquire_dsc(dc, res_ctx, &sec_pipe->stream_res.dsc, pipe_idx); | |
1355 | ASSERT(sec_pipe->stream_res.dsc); | |
1356 | if (sec_pipe->stream_res.dsc == NULL) | |
1357 | return false; | |
1358 | } | |
1359 | } else { | |
1360 | if (pri_pipe->bottom_pipe) { | |
1361 | ASSERT(pri_pipe->bottom_pipe != sec_pipe); | |
1362 | sec_pipe->bottom_pipe = pri_pipe->bottom_pipe; | |
1363 | sec_pipe->bottom_pipe->top_pipe = sec_pipe; | |
1364 | } | |
1365 | pri_pipe->bottom_pipe = sec_pipe; | |
1366 | sec_pipe->top_pipe = pri_pipe; | |
1367 | ||
1368 | ASSERT(pri_pipe->plane_state); | |
1369 | } | |
1370 | ||
1371 | return true; | |
1372 | } | |
1373 | ||
1374 | bool dcn32_internal_validate_bw(struct dc *dc, | |
1375 | struct dc_state *context, | |
1376 | display_e2e_pipe_params_st *pipes, | |
1377 | int *pipe_cnt_out, | |
1378 | int *vlevel_out, | |
1379 | bool fast_validate) | |
1380 | { | |
1381 | bool out = false; | |
1382 | bool repopulate_pipes = false; | |
1383 | int split[MAX_PIPES] = { 0 }; | |
1384 | bool merge[MAX_PIPES] = { false }; | |
1385 | bool newly_split[MAX_PIPES] = { false }; | |
86678d46 DG |
1386 | int pipe_cnt, i, pipe_idx; |
1387 | int vlevel = context->bw_ctx.dml.soc.num_states; | |
5b4ee987 RS |
1388 | struct vba_vars_st *vba = &context->bw_ctx.dml.vba; |
1389 | ||
1390 | dc_assert_fp_enabled(); | |
1391 | ||
1392 | ASSERT(pipes); | |
1393 | if (!pipes) | |
1394 | return false; | |
1395 | ||
1396 | // For each full update, remove all existing phantom pipes first | |
1397 | dc->res_pool->funcs->remove_phantom_pipes(dc, context); | |
1398 | ||
1399 | dc->res_pool->funcs->update_soc_for_wm_a(dc, context); | |
1400 | ||
1401 | pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, fast_validate); | |
1402 | ||
1403 | if (!pipe_cnt) { | |
1404 | out = true; | |
1405 | goto validate_out; | |
1406 | } | |
1407 | ||
1408 | dml_log_pipe_params(&context->bw_ctx.dml, pipes, pipe_cnt); | |
1409 | ||
1410 | if (!fast_validate) { | |
1411 | DC_FP_START(); | |
1412 | dcn32_full_validate_bw_helper(dc, context, pipes, &vlevel, split, merge, &pipe_cnt); | |
1413 | DC_FP_END(); | |
1414 | } | |
1415 | ||
86678d46 | 1416 | if (fast_validate || |
dd075563 | 1417 | (dc->debug.dml_disallow_alternate_prefetch_modes && |
86678d46 | 1418 | (vlevel == context->bw_ctx.dml.soc.num_states || |
dd075563 | 1419 | vba->DRAMClockChangeSupport[vlevel][vba->maxMpcComb] == dm_dram_clock_change_unsupported))) { |
5b4ee987 | 1420 | /* |
86678d46 DG |
1421 | * If dml_disallow_alternate_prefetch_modes is false, then we have already |
1422 | * tried alternate prefetch modes during full validation. | |
1423 | * | |
1424 | * If mode is unsupported or there is no p-state support, then | |
1425 | * fall back to favouring voltage. | |
5b4ee987 | 1426 | * |
86678d46 DG |
1427 | * If Prefetch mode 0 failed for this config, or passed with Max UCLK, then try |
1428 | * to support with Prefetch mode 1 (dm_prefetch_support_fclk_and_stutter == 2) | |
5b4ee987 RS |
1429 | */ |
1430 | context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final = | |
86678d46 | 1431 | dm_prefetch_support_fclk_and_stutter; |
5b4ee987 RS |
1432 | |
1433 | vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, pipe_cnt); | |
1434 | ||
1435 | /* Last attempt with Prefetch mode 2 (dm_prefetch_support_stutter == 3) */ | |
1436 | if (vlevel == context->bw_ctx.dml.soc.num_states) { | |
1437 | context->bw_ctx.dml.soc.allow_for_pstate_or_stutter_in_vblank_final = | |
1438 | dm_prefetch_support_stutter; | |
1439 | vlevel = dml_get_voltage_level(&context->bw_ctx.dml, pipes, pipe_cnt); | |
1440 | } | |
1441 | ||
1442 | if (vlevel < context->bw_ctx.dml.soc.num_states) { | |
1443 | memset(split, 0, sizeof(split)); | |
1444 | memset(merge, 0, sizeof(merge)); | |
1445 | vlevel = dcn20_validate_apply_pipe_split_flags(dc, context, vlevel, split, merge); | |
a3c9b4c7 AL |
1446 | // dcn20_validate_apply_pipe_split_flags can modify voltage level outside of DML |
1447 | vba->VoltageLevel = vlevel; | |
5b4ee987 RS |
1448 | } |
1449 | } | |
1450 | ||
1451 | dml_log_mode_support_params(&context->bw_ctx.dml); | |
1452 | ||
1453 | if (vlevel == context->bw_ctx.dml.soc.num_states) | |
1454 | goto validate_fail; | |
1455 | ||
1456 | for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) { | |
1457 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; | |
1458 | struct pipe_ctx *mpo_pipe = pipe->bottom_pipe; | |
1459 | ||
1460 | if (!pipe->stream) | |
1461 | continue; | |
1462 | ||
1463 | if (vba->ODMCombineEnabled[vba->pipe_plane[pipe_idx]] != dm_odm_combine_mode_disabled | |
1464 | && !dc->config.enable_windowed_mpo_odm | |
1465 | && pipe->plane_state && mpo_pipe | |
1466 | && memcmp(&mpo_pipe->plane_res.scl_data.recout, | |
1467 | &pipe->plane_res.scl_data.recout, | |
1468 | sizeof(struct rect)) != 0) { | |
1469 | ASSERT(mpo_pipe->plane_state != pipe->plane_state); | |
1470 | goto validate_fail; | |
1471 | } | |
1472 | pipe_idx++; | |
1473 | } | |
1474 | ||
1475 | /* merge pipes if necessary */ | |
1476 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
1477 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; | |
1478 | ||
1479 | /*skip pipes that don't need merging*/ | |
1480 | if (!merge[i]) | |
1481 | continue; | |
1482 | ||
1483 | /* if ODM merge we ignore mpc tree, mpo pipes will have their own flags */ | |
1484 | if (pipe->prev_odm_pipe) { | |
1485 | /*split off odm pipe*/ | |
1486 | pipe->prev_odm_pipe->next_odm_pipe = pipe->next_odm_pipe; | |
1487 | if (pipe->next_odm_pipe) | |
1488 | pipe->next_odm_pipe->prev_odm_pipe = pipe->prev_odm_pipe; | |
1489 | ||
1490 | pipe->bottom_pipe = NULL; | |
1491 | pipe->next_odm_pipe = NULL; | |
1492 | pipe->plane_state = NULL; | |
1493 | pipe->stream = NULL; | |
1494 | pipe->top_pipe = NULL; | |
1495 | pipe->prev_odm_pipe = NULL; | |
1496 | if (pipe->stream_res.dsc) | |
1497 | dcn20_release_dsc(&context->res_ctx, dc->res_pool, &pipe->stream_res.dsc); | |
1498 | memset(&pipe->plane_res, 0, sizeof(pipe->plane_res)); | |
1499 | memset(&pipe->stream_res, 0, sizeof(pipe->stream_res)); | |
1500 | repopulate_pipes = true; | |
1501 | } else if (pipe->top_pipe && pipe->top_pipe->plane_state == pipe->plane_state) { | |
1502 | struct pipe_ctx *top_pipe = pipe->top_pipe; | |
1503 | struct pipe_ctx *bottom_pipe = pipe->bottom_pipe; | |
1504 | ||
1505 | top_pipe->bottom_pipe = bottom_pipe; | |
1506 | if (bottom_pipe) | |
1507 | bottom_pipe->top_pipe = top_pipe; | |
1508 | ||
1509 | pipe->top_pipe = NULL; | |
1510 | pipe->bottom_pipe = NULL; | |
1511 | pipe->plane_state = NULL; | |
1512 | pipe->stream = NULL; | |
1513 | memset(&pipe->plane_res, 0, sizeof(pipe->plane_res)); | |
1514 | memset(&pipe->stream_res, 0, sizeof(pipe->stream_res)); | |
1515 | repopulate_pipes = true; | |
1516 | } else | |
1517 | ASSERT(0); /* Should never try to merge master pipe */ | |
1518 | ||
1519 | } | |
1520 | ||
1521 | for (i = 0, pipe_idx = -1; i < dc->res_pool->pipe_count; i++) { | |
1522 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; | |
1523 | struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i]; | |
1524 | struct pipe_ctx *hsplit_pipe = NULL; | |
1525 | bool odm; | |
1526 | int old_index = -1; | |
1527 | ||
1528 | if (!pipe->stream || newly_split[i]) | |
1529 | continue; | |
1530 | ||
1531 | pipe_idx++; | |
1532 | odm = vba->ODMCombineEnabled[vba->pipe_plane[pipe_idx]] != dm_odm_combine_mode_disabled; | |
1533 | ||
1534 | if (!pipe->plane_state && !odm) | |
1535 | continue; | |
1536 | ||
1537 | if (split[i]) { | |
1538 | if (odm) { | |
1539 | if (split[i] == 4 && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe) | |
1540 | old_index = old_pipe->next_odm_pipe->next_odm_pipe->pipe_idx; | |
1541 | else if (old_pipe->next_odm_pipe) | |
1542 | old_index = old_pipe->next_odm_pipe->pipe_idx; | |
1543 | } else { | |
1544 | if (split[i] == 4 && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe && | |
1545 | old_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state) | |
1546 | old_index = old_pipe->bottom_pipe->bottom_pipe->pipe_idx; | |
1547 | else if (old_pipe->bottom_pipe && | |
1548 | old_pipe->bottom_pipe->plane_state == old_pipe->plane_state) | |
1549 | old_index = old_pipe->bottom_pipe->pipe_idx; | |
1550 | } | |
1551 | hsplit_pipe = dcn32_find_split_pipe(dc, context, old_index); | |
1552 | ASSERT(hsplit_pipe); | |
1553 | if (!hsplit_pipe) | |
1554 | goto validate_fail; | |
1555 | ||
1556 | if (!dcn32_split_stream_for_mpc_or_odm( | |
1557 | dc, &context->res_ctx, | |
1558 | pipe, hsplit_pipe, odm)) | |
1559 | goto validate_fail; | |
1560 | ||
1561 | newly_split[hsplit_pipe->pipe_idx] = true; | |
1562 | repopulate_pipes = true; | |
1563 | } | |
1564 | if (split[i] == 4) { | |
1565 | struct pipe_ctx *pipe_4to1; | |
1566 | ||
1567 | if (odm && old_pipe->next_odm_pipe) | |
1568 | old_index = old_pipe->next_odm_pipe->pipe_idx; | |
1569 | else if (!odm && old_pipe->bottom_pipe && | |
1570 | old_pipe->bottom_pipe->plane_state == old_pipe->plane_state) | |
1571 | old_index = old_pipe->bottom_pipe->pipe_idx; | |
1572 | else | |
1573 | old_index = -1; | |
1574 | pipe_4to1 = dcn32_find_split_pipe(dc, context, old_index); | |
1575 | ASSERT(pipe_4to1); | |
1576 | if (!pipe_4to1) | |
1577 | goto validate_fail; | |
1578 | if (!dcn32_split_stream_for_mpc_or_odm( | |
1579 | dc, &context->res_ctx, | |
1580 | pipe, pipe_4to1, odm)) | |
1581 | goto validate_fail; | |
1582 | newly_split[pipe_4to1->pipe_idx] = true; | |
1583 | ||
1584 | if (odm && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe | |
1585 | && old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe) | |
1586 | old_index = old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe->pipe_idx; | |
1587 | else if (!odm && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe && | |
1588 | old_pipe->bottom_pipe->bottom_pipe->bottom_pipe && | |
1589 | old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state) | |
1590 | old_index = old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->pipe_idx; | |
1591 | else | |
1592 | old_index = -1; | |
1593 | pipe_4to1 = dcn32_find_split_pipe(dc, context, old_index); | |
1594 | ASSERT(pipe_4to1); | |
1595 | if (!pipe_4to1) | |
1596 | goto validate_fail; | |
1597 | if (!dcn32_split_stream_for_mpc_or_odm( | |
1598 | dc, &context->res_ctx, | |
1599 | hsplit_pipe, pipe_4to1, odm)) | |
1600 | goto validate_fail; | |
1601 | newly_split[pipe_4to1->pipe_idx] = true; | |
1602 | } | |
1603 | if (odm) | |
1604 | dcn20_build_mapped_resource(dc, context, pipe->stream); | |
1605 | } | |
1606 | ||
1607 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
1608 | struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; | |
1609 | ||
1610 | if (pipe->plane_state) { | |
1611 | if (!resource_build_scaling_params(pipe)) | |
1612 | goto validate_fail; | |
1613 | } | |
1614 | } | |
1615 | ||
1616 | /* Actual dsc count per stream dsc validation*/ | |
1617 | if (!dcn20_validate_dsc(dc, context)) { | |
1618 | vba->ValidationStatus[vba->soc.num_states] = DML_FAIL_DSC_VALIDATION_FAILURE; | |
1619 | goto validate_fail; | |
1620 | } | |
1621 | ||
1622 | if (repopulate_pipes) | |
1623 | pipe_cnt = dc->res_pool->funcs->populate_dml_pipes(dc, context, pipes, fast_validate); | |
1624 | *vlevel_out = vlevel; | |
1625 | *pipe_cnt_out = pipe_cnt; | |
1626 | ||
1627 | out = true; | |
1628 | goto validate_out; | |
1629 | ||
1630 | validate_fail: | |
1631 | out = false; | |
1632 | ||
1633 | validate_out: | |
1634 | return out; | |
1635 | } | |
1636 | ||
1637 | ||
0339530d RS |
1638 | void dcn32_calculate_wm_and_dlg_fpu(struct dc *dc, struct dc_state *context, |
1639 | display_e2e_pipe_params_st *pipes, | |
1640 | int pipe_cnt, | |
1641 | int vlevel) | |
1642 | { | |
1643 | int i, pipe_idx, vlevel_temp = 0; | |
1644 | double dcfclk = dcn3_2_soc.clock_limits[0].dcfclk_mhz; | |
1645 | double dcfclk_from_validation = context->bw_ctx.dml.vba.DCFCLKState[vlevel][context->bw_ctx.dml.vba.maxMpcComb]; | |
1646 | bool pstate_en = context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] != | |
1647 | dm_dram_clock_change_unsupported; | |
6290ba4c RS |
1648 | unsigned int dummy_latency_index = 0; |
1649 | int maxMpcComb = context->bw_ctx.dml.vba.maxMpcComb; | |
1650 | unsigned int min_dram_speed_mts = context->bw_ctx.dml.vba.DRAMSpeed; | |
1651 | unsigned int min_dram_speed_mts_margin; | |
0339530d RS |
1652 | |
1653 | dc_assert_fp_enabled(); | |
1654 | ||
1655 | // Override DRAMClockChangeSupport for SubVP + DRR case where the DRR cannot switch without stretching it's VBLANK | |
1656 | if (!pstate_en && dcn32_subvp_in_use(dc, context)) { | |
1657 | context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][context->bw_ctx.dml.vba.maxMpcComb] = dm_dram_clock_change_vblank_w_mall_sub_vp; | |
1658 | pstate_en = true; | |
1659 | } | |
1660 | ||
6290ba4c RS |
1661 | context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching = false; |
1662 | ||
1663 | if (!pstate_en) { | |
1664 | /* only when the mclk switch can not be natural, is the fw based vblank stretch attempted */ | |
1665 | context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching = | |
1666 | dcn30_can_support_mclk_switch_using_fw_based_vblank_stretch(dc, context); | |
1667 | ||
1668 | if (context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching) { | |
1669 | dummy_latency_index = dcn30_find_dummy_latency_index_for_fw_based_mclk_switch(dc, | |
1670 | context, pipes, pipe_cnt, vlevel); | |
1671 | ||
1672 | /* After calling dcn30_find_dummy_latency_index_for_fw_based_mclk_switch | |
1673 | * we reinstate the original dram_clock_change_latency_us on the context | |
1674 | * and all variables that may have changed up to this point, except the | |
1675 | * newly found dummy_latency_index | |
1676 | */ | |
1677 | context->bw_ctx.dml.soc.dram_clock_change_latency_us = | |
1678 | dc->clk_mgr->bw_params->wm_table.nv_entries[WM_A].dml_input.pstate_latency_us; | |
1679 | dcn32_internal_validate_bw(dc, context, pipes, &pipe_cnt, &vlevel, false); | |
1680 | maxMpcComb = context->bw_ctx.dml.vba.maxMpcComb; | |
1681 | dcfclk = context->bw_ctx.dml.vba.DCFCLKState[vlevel][context->bw_ctx.dml.vba.maxMpcComb]; | |
1682 | pstate_en = context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][maxMpcComb] != | |
1683 | dm_dram_clock_change_unsupported; | |
1684 | } | |
1685 | } | |
1686 | ||
0339530d RS |
1687 | /* Set B: |
1688 | * For Set B calculations use clocks from clock_limits[2] when available i.e. when SMU is present, | |
1689 | * otherwise use arbitrary low value from spreadsheet for DCFCLK as lower is safer for watermark | |
1690 | * calculations to cover bootup clocks. | |
1691 | * DCFCLK: soc.clock_limits[2] when available | |
1692 | * UCLK: soc.clock_limits[2] when available | |
1693 | */ | |
1694 | if (dcn3_2_soc.num_states > 2) { | |
1695 | vlevel_temp = 2; | |
1696 | dcfclk = dcn3_2_soc.clock_limits[2].dcfclk_mhz; | |
1697 | } else | |
1698 | dcfclk = 615; //DCFCLK Vmin_lv | |
1699 | ||
1700 | pipes[0].clks_cfg.voltage = vlevel_temp; | |
1701 | pipes[0].clks_cfg.dcfclk_mhz = dcfclk; | |
1702 | pipes[0].clks_cfg.socclk_mhz = context->bw_ctx.dml.soc.clock_limits[vlevel_temp].socclk_mhz; | |
1703 | ||
1704 | if (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_B].valid) { | |
1705 | context->bw_ctx.dml.soc.dram_clock_change_latency_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_B].dml_input.pstate_latency_us; | |
1706 | context->bw_ctx.dml.soc.fclk_change_latency_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_B].dml_input.fclk_change_latency_us; | |
1707 | context->bw_ctx.dml.soc.sr_enter_plus_exit_time_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_B].dml_input.sr_enter_plus_exit_time_us; | |
1708 | context->bw_ctx.dml.soc.sr_exit_time_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_B].dml_input.sr_exit_time_us; | |
1709 | } | |
1710 | context->bw_ctx.bw.dcn.watermarks.b.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1711 | context->bw_ctx.bw.dcn.watermarks.b.cstate_pstate.cstate_enter_plus_exit_ns = get_wm_stutter_enter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1712 | context->bw_ctx.bw.dcn.watermarks.b.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1713 | context->bw_ctx.bw.dcn.watermarks.b.cstate_pstate.pstate_change_ns = get_wm_dram_clock_change(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1714 | context->bw_ctx.bw.dcn.watermarks.b.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1715 | context->bw_ctx.bw.dcn.watermarks.b.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1716 | context->bw_ctx.bw.dcn.watermarks.b.frac_urg_bw_flip = get_fraction_of_urgent_bandwidth_imm_flip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1717 | context->bw_ctx.bw.dcn.watermarks.b.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1718 | context->bw_ctx.bw.dcn.watermarks.b.cstate_pstate.fclk_pstate_change_ns = get_fclk_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1719 | context->bw_ctx.bw.dcn.watermarks.b.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1720 | ||
1721 | /* Set D: | |
1722 | * All clocks min. | |
1723 | * DCFCLK: Min, as reported by PM FW when available | |
1724 | * UCLK : Min, as reported by PM FW when available | |
1725 | * sr_enter_exit/sr_exit should be lower than used for DRAM (TBD after bringup or later, use as decided in Clk Mgr) | |
1726 | */ | |
1727 | ||
1728 | if (dcn3_2_soc.num_states > 2) { | |
1729 | vlevel_temp = 0; | |
1730 | dcfclk = dc->clk_mgr->bw_params->clk_table.entries[0].dcfclk_mhz; | |
1731 | } else | |
1732 | dcfclk = 615; //DCFCLK Vmin_lv | |
1733 | ||
1734 | pipes[0].clks_cfg.voltage = vlevel_temp; | |
1735 | pipes[0].clks_cfg.dcfclk_mhz = dcfclk; | |
1736 | pipes[0].clks_cfg.socclk_mhz = context->bw_ctx.dml.soc.clock_limits[vlevel_temp].socclk_mhz; | |
1737 | ||
1738 | if (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_D].valid) { | |
1739 | context->bw_ctx.dml.soc.dram_clock_change_latency_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_D].dml_input.pstate_latency_us; | |
1740 | context->bw_ctx.dml.soc.fclk_change_latency_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_D].dml_input.fclk_change_latency_us; | |
1741 | context->bw_ctx.dml.soc.sr_enter_plus_exit_time_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_D].dml_input.sr_enter_plus_exit_time_us; | |
1742 | context->bw_ctx.dml.soc.sr_exit_time_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_D].dml_input.sr_exit_time_us; | |
1743 | } | |
1744 | context->bw_ctx.bw.dcn.watermarks.d.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1745 | context->bw_ctx.bw.dcn.watermarks.d.cstate_pstate.cstate_enter_plus_exit_ns = get_wm_stutter_enter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1746 | context->bw_ctx.bw.dcn.watermarks.d.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1747 | context->bw_ctx.bw.dcn.watermarks.d.cstate_pstate.pstate_change_ns = get_wm_dram_clock_change(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1748 | context->bw_ctx.bw.dcn.watermarks.d.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1749 | context->bw_ctx.bw.dcn.watermarks.d.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1750 | context->bw_ctx.bw.dcn.watermarks.d.frac_urg_bw_flip = get_fraction_of_urgent_bandwidth_imm_flip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1751 | context->bw_ctx.bw.dcn.watermarks.d.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1752 | context->bw_ctx.bw.dcn.watermarks.d.cstate_pstate.fclk_pstate_change_ns = get_fclk_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1753 | context->bw_ctx.bw.dcn.watermarks.d.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1754 | ||
1755 | /* Set C, for Dummy P-State: | |
1756 | * All clocks min. | |
1757 | * DCFCLK: Min, as reported by PM FW, when available | |
1758 | * UCLK : Min, as reported by PM FW, when available | |
1759 | * pstate latency as per UCLK state dummy pstate latency | |
1760 | */ | |
1761 | ||
1762 | // For Set A and Set C use values from validation | |
1763 | pipes[0].clks_cfg.voltage = vlevel; | |
1764 | pipes[0].clks_cfg.dcfclk_mhz = dcfclk_from_validation; | |
1765 | pipes[0].clks_cfg.socclk_mhz = context->bw_ctx.dml.soc.clock_limits[vlevel].socclk_mhz; | |
1766 | ||
1767 | if (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_C].valid) { | |
6290ba4c RS |
1768 | min_dram_speed_mts = context->bw_ctx.dml.vba.DRAMSpeed; |
1769 | min_dram_speed_mts_margin = 160; | |
0339530d | 1770 | |
6290ba4c RS |
1771 | context->bw_ctx.dml.soc.dram_clock_change_latency_us = |
1772 | dc->clk_mgr->bw_params->dummy_pstate_table[0].dummy_pstate_latency_us; | |
0339530d | 1773 | |
6290ba4c RS |
1774 | if (context->bw_ctx.dml.vba.DRAMClockChangeSupport[vlevel][maxMpcComb] == |
1775 | dm_dram_clock_change_unsupported) { | |
1776 | int min_dram_speed_mts_offset = dc->clk_mgr->bw_params->clk_table.num_entries - 1; | |
1777 | ||
1778 | min_dram_speed_mts = | |
1779 | dc->clk_mgr->bw_params->clk_table.entries[min_dram_speed_mts_offset].memclk_mhz * 16; | |
1780 | } | |
1781 | ||
1782 | if (!context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching) { | |
1783 | /* find largest table entry that is lower than dram speed, | |
1784 | * but lower than DPM0 still uses DPM0 | |
1785 | */ | |
1786 | for (dummy_latency_index = 3; dummy_latency_index > 0; dummy_latency_index--) | |
1787 | if (min_dram_speed_mts + min_dram_speed_mts_margin > | |
1788 | dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dram_speed_mts) | |
1789 | break; | |
1790 | } | |
1791 | ||
1792 | context->bw_ctx.dml.soc.dram_clock_change_latency_us = | |
1793 | dc->clk_mgr->bw_params->dummy_pstate_table[dummy_latency_index].dummy_pstate_latency_us; | |
0339530d | 1794 | |
0339530d RS |
1795 | context->bw_ctx.dml.soc.fclk_change_latency_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_C].dml_input.fclk_change_latency_us; |
1796 | context->bw_ctx.dml.soc.sr_enter_plus_exit_time_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_C].dml_input.sr_enter_plus_exit_time_us; | |
1797 | context->bw_ctx.dml.soc.sr_exit_time_us = dc->clk_mgr->bw_params->wm_table.nv_entries[WM_C].dml_input.sr_exit_time_us; | |
1798 | } | |
1799 | ||
1800 | context->bw_ctx.bw.dcn.watermarks.c.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1801 | context->bw_ctx.bw.dcn.watermarks.c.cstate_pstate.cstate_enter_plus_exit_ns = get_wm_stutter_enter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1802 | context->bw_ctx.bw.dcn.watermarks.c.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1803 | context->bw_ctx.bw.dcn.watermarks.c.cstate_pstate.pstate_change_ns = get_wm_dram_clock_change(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1804 | context->bw_ctx.bw.dcn.watermarks.c.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1805 | context->bw_ctx.bw.dcn.watermarks.c.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1806 | context->bw_ctx.bw.dcn.watermarks.c.frac_urg_bw_flip = get_fraction_of_urgent_bandwidth_imm_flip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1807 | context->bw_ctx.bw.dcn.watermarks.c.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
7857825b AL |
1808 | /* On DCN32/321, PMFW will set PSTATE_CHANGE_TYPE = 1 (FCLK) for UCLK dummy p-state. |
1809 | * In this case we must program FCLK WM Set C to use the UCLK dummy p-state WM | |
1810 | * value. | |
1811 | */ | |
1812 | context->bw_ctx.bw.dcn.watermarks.c.cstate_pstate.fclk_pstate_change_ns = get_wm_dram_clock_change(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
0339530d RS |
1813 | context->bw_ctx.bw.dcn.watermarks.c.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; |
1814 | ||
1815 | if ((!pstate_en) && (dc->clk_mgr->bw_params->wm_table.nv_entries[WM_C].valid)) { | |
1816 | /* The only difference between A and C is p-state latency, if p-state is not supported | |
1817 | * with full p-state latency we want to calculate DLG based on dummy p-state latency, | |
1818 | * Set A p-state watermark set to 0 on DCN30, when p-state unsupported, for now keep as DCN30. | |
1819 | */ | |
1820 | context->bw_ctx.bw.dcn.watermarks.a = context->bw_ctx.bw.dcn.watermarks.c; | |
1821 | context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns = 0; | |
1822 | } else { | |
1823 | /* Set A: | |
1824 | * All clocks min. | |
1825 | * DCFCLK: Min, as reported by PM FW, when available | |
1826 | * UCLK: Min, as reported by PM FW, when available | |
1827 | */ | |
1828 | dc->res_pool->funcs->update_soc_for_wm_a(dc, context); | |
1829 | context->bw_ctx.bw.dcn.watermarks.a.urgent_ns = get_wm_urgent(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1830 | context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.cstate_enter_plus_exit_ns = get_wm_stutter_enter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1831 | context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.cstate_exit_ns = get_wm_stutter_exit(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1832 | context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns = get_wm_dram_clock_change(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1833 | context->bw_ctx.bw.dcn.watermarks.a.pte_meta_urgent_ns = get_wm_memory_trip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1834 | context->bw_ctx.bw.dcn.watermarks.a.frac_urg_bw_nom = get_fraction_of_urgent_bandwidth(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1835 | context->bw_ctx.bw.dcn.watermarks.a.frac_urg_bw_flip = get_fraction_of_urgent_bandwidth_imm_flip(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1836 | context->bw_ctx.bw.dcn.watermarks.a.urgent_latency_ns = get_urgent_latency(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1837 | context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.fclk_pstate_change_ns = get_fclk_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1838 | context->bw_ctx.bw.dcn.watermarks.a.usr_retraining_ns = get_usr_retraining_watermark(&context->bw_ctx.dml, pipes, pipe_cnt) * 1000; | |
1839 | } | |
1840 | ||
1841 | for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) { | |
1842 | if (!context->res_ctx.pipe_ctx[i].stream) | |
1843 | continue; | |
1844 | ||
1845 | pipes[pipe_idx].clks_cfg.dispclk_mhz = get_dispclk_calculated(&context->bw_ctx.dml, pipes, pipe_cnt); | |
1846 | pipes[pipe_idx].clks_cfg.dppclk_mhz = get_dppclk_calculated(&context->bw_ctx.dml, pipes, pipe_cnt, pipe_idx); | |
1847 | ||
1848 | if (dc->config.forced_clocks) { | |
1849 | pipes[pipe_idx].clks_cfg.dispclk_mhz = context->bw_ctx.dml.soc.clock_limits[0].dispclk_mhz; | |
1850 | pipes[pipe_idx].clks_cfg.dppclk_mhz = context->bw_ctx.dml.soc.clock_limits[0].dppclk_mhz; | |
1851 | } | |
1852 | if (dc->debug.min_disp_clk_khz > pipes[pipe_idx].clks_cfg.dispclk_mhz * 1000) | |
1853 | pipes[pipe_idx].clks_cfg.dispclk_mhz = dc->debug.min_disp_clk_khz / 1000.0; | |
1854 | if (dc->debug.min_dpp_clk_khz > pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000) | |
1855 | pipes[pipe_idx].clks_cfg.dppclk_mhz = dc->debug.min_dpp_clk_khz / 1000.0; | |
1856 | ||
1857 | pipe_idx++; | |
1858 | } | |
1859 | ||
1860 | context->perf_params.stutter_period_us = context->bw_ctx.dml.vba.StutterPeriod; | |
1861 | ||
1862 | dcn32_calculate_dlg_params(dc, context, pipes, pipe_cnt, vlevel); | |
1863 | ||
1864 | if (!pstate_en) | |
1865 | /* Restore full p-state latency */ | |
1866 | context->bw_ctx.dml.soc.dram_clock_change_latency_us = | |
1867 | dc->clk_mgr->bw_params->wm_table.nv_entries[WM_A].dml_input.pstate_latency_us; | |
1868 | ||
6290ba4c RS |
1869 | if (context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching) |
1870 | dcn30_setup_mclk_switch_using_fw_based_vblank_stretch(dc, context); | |
0339530d RS |
1871 | } |
1872 | ||
4e14e0fc RS |
1873 | static void dcn32_get_optimal_dcfclk_fclk_for_uclk(unsigned int uclk_mts, |
1874 | unsigned int *optimal_dcfclk, | |
1875 | unsigned int *optimal_fclk) | |
1876 | { | |
1877 | double bw_from_dram, bw_from_dram1, bw_from_dram2; | |
1878 | ||
1879 | bw_from_dram1 = uclk_mts * dcn3_2_soc.num_chans * | |
1880 | dcn3_2_soc.dram_channel_width_bytes * (dcn3_2_soc.max_avg_dram_bw_use_normal_percent / 100); | |
1881 | bw_from_dram2 = uclk_mts * dcn3_2_soc.num_chans * | |
1882 | dcn3_2_soc.dram_channel_width_bytes * (dcn3_2_soc.max_avg_sdp_bw_use_normal_percent / 100); | |
1883 | ||
1884 | bw_from_dram = (bw_from_dram1 < bw_from_dram2) ? bw_from_dram1 : bw_from_dram2; | |
1885 | ||
1886 | if (optimal_fclk) | |
1887 | *optimal_fclk = bw_from_dram / | |
1888 | (dcn3_2_soc.fabric_datapath_to_dcn_data_return_bytes * (dcn3_2_soc.max_avg_sdp_bw_use_normal_percent / 100)); | |
1889 | ||
1890 | if (optimal_dcfclk) | |
1891 | *optimal_dcfclk = bw_from_dram / | |
1892 | (dcn3_2_soc.return_bus_width_bytes * (dcn3_2_soc.max_avg_sdp_bw_use_normal_percent / 100)); | |
1893 | } | |
1894 | ||
1895 | static void remove_entry_from_table_at_index(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries, | |
1896 | unsigned int index) | |
1897 | { | |
1898 | int i; | |
1899 | ||
1900 | if (*num_entries == 0) | |
1901 | return; | |
1902 | ||
1903 | for (i = index; i < *num_entries - 1; i++) { | |
1904 | table[i] = table[i + 1]; | |
1905 | } | |
1906 | memset(&table[--(*num_entries)], 0, sizeof(struct _vcs_dpi_voltage_scaling_st)); | |
1907 | } | |
1908 | ||
1909 | static int build_synthetic_soc_states(struct clk_bw_params *bw_params, | |
1910 | struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries) | |
1911 | { | |
1912 | int i, j; | |
1913 | struct _vcs_dpi_voltage_scaling_st entry = {0}; | |
1914 | ||
1915 | unsigned int max_dcfclk_mhz = 0, max_dispclk_mhz = 0, max_dppclk_mhz = 0, | |
1916 | max_phyclk_mhz = 0, max_dtbclk_mhz = 0, max_fclk_mhz = 0, max_uclk_mhz = 0; | |
1917 | ||
1918 | unsigned int min_dcfclk_mhz = 199, min_fclk_mhz = 299; | |
1919 | ||
1920 | static const unsigned int num_dcfclk_stas = 5; | |
1921 | unsigned int dcfclk_sta_targets[DC__VOLTAGE_STATES] = {199, 615, 906, 1324, 1564}; | |
1922 | ||
1923 | unsigned int num_uclk_dpms = 0; | |
1924 | unsigned int num_fclk_dpms = 0; | |
1925 | unsigned int num_dcfclk_dpms = 0; | |
1926 | ||
1927 | for (i = 0; i < MAX_NUM_DPM_LVL; i++) { | |
1928 | if (bw_params->clk_table.entries[i].dcfclk_mhz > max_dcfclk_mhz) | |
1929 | max_dcfclk_mhz = bw_params->clk_table.entries[i].dcfclk_mhz; | |
1930 | if (bw_params->clk_table.entries[i].fclk_mhz > max_fclk_mhz) | |
1931 | max_fclk_mhz = bw_params->clk_table.entries[i].fclk_mhz; | |
1932 | if (bw_params->clk_table.entries[i].memclk_mhz > max_uclk_mhz) | |
1933 | max_uclk_mhz = bw_params->clk_table.entries[i].memclk_mhz; | |
1934 | if (bw_params->clk_table.entries[i].dispclk_mhz > max_dispclk_mhz) | |
1935 | max_dispclk_mhz = bw_params->clk_table.entries[i].dispclk_mhz; | |
1936 | if (bw_params->clk_table.entries[i].dppclk_mhz > max_dppclk_mhz) | |
1937 | max_dppclk_mhz = bw_params->clk_table.entries[i].dppclk_mhz; | |
1938 | if (bw_params->clk_table.entries[i].phyclk_mhz > max_phyclk_mhz) | |
1939 | max_phyclk_mhz = bw_params->clk_table.entries[i].phyclk_mhz; | |
1940 | if (bw_params->clk_table.entries[i].dtbclk_mhz > max_dtbclk_mhz) | |
1941 | max_dtbclk_mhz = bw_params->clk_table.entries[i].dtbclk_mhz; | |
1942 | ||
1943 | if (bw_params->clk_table.entries[i].memclk_mhz > 0) | |
1944 | num_uclk_dpms++; | |
1945 | if (bw_params->clk_table.entries[i].fclk_mhz > 0) | |
1946 | num_fclk_dpms++; | |
1947 | if (bw_params->clk_table.entries[i].dcfclk_mhz > 0) | |
1948 | num_dcfclk_dpms++; | |
1949 | } | |
1950 | ||
1951 | if (!max_dcfclk_mhz || !max_dispclk_mhz || !max_dtbclk_mhz) | |
1952 | return -1; | |
1953 | ||
1954 | if (max_dppclk_mhz == 0) | |
1955 | max_dppclk_mhz = max_dispclk_mhz; | |
1956 | ||
1957 | if (max_fclk_mhz == 0) | |
1958 | max_fclk_mhz = max_dcfclk_mhz * dcn3_2_soc.pct_ideal_sdp_bw_after_urgent / dcn3_2_soc.pct_ideal_fabric_bw_after_urgent; | |
1959 | ||
1960 | if (max_phyclk_mhz == 0) | |
1961 | max_phyclk_mhz = dcn3_2_soc.clock_limits[0].phyclk_mhz; | |
1962 | ||
1963 | *num_entries = 0; | |
1964 | entry.dispclk_mhz = max_dispclk_mhz; | |
1965 | entry.dscclk_mhz = max_dispclk_mhz / 3; | |
1966 | entry.dppclk_mhz = max_dppclk_mhz; | |
1967 | entry.dtbclk_mhz = max_dtbclk_mhz; | |
1968 | entry.phyclk_mhz = max_phyclk_mhz; | |
1969 | entry.phyclk_d18_mhz = dcn3_2_soc.clock_limits[0].phyclk_d18_mhz; | |
1970 | entry.phyclk_d32_mhz = dcn3_2_soc.clock_limits[0].phyclk_d32_mhz; | |
1971 | ||
1972 | // Insert all the DCFCLK STAs | |
1973 | for (i = 0; i < num_dcfclk_stas; i++) { | |
1974 | entry.dcfclk_mhz = dcfclk_sta_targets[i]; | |
1975 | entry.fabricclk_mhz = 0; | |
1976 | entry.dram_speed_mts = 0; | |
1977 | ||
1978 | DC_FP_START(); | |
1979 | insert_entry_into_table_sorted(table, num_entries, &entry); | |
1980 | DC_FP_END(); | |
1981 | } | |
1982 | ||
1983 | // Insert the max DCFCLK | |
1984 | entry.dcfclk_mhz = max_dcfclk_mhz; | |
1985 | entry.fabricclk_mhz = 0; | |
1986 | entry.dram_speed_mts = 0; | |
1987 | ||
1988 | DC_FP_START(); | |
1989 | insert_entry_into_table_sorted(table, num_entries, &entry); | |
1990 | DC_FP_END(); | |
1991 | ||
1992 | // Insert the UCLK DPMS | |
1993 | for (i = 0; i < num_uclk_dpms; i++) { | |
1994 | entry.dcfclk_mhz = 0; | |
1995 | entry.fabricclk_mhz = 0; | |
1996 | entry.dram_speed_mts = bw_params->clk_table.entries[i].memclk_mhz * 16; | |
1997 | ||
1998 | DC_FP_START(); | |
1999 | insert_entry_into_table_sorted(table, num_entries, &entry); | |
2000 | DC_FP_END(); | |
2001 | } | |
2002 | ||
2003 | // If FCLK is coarse grained, insert individual DPMs. | |
2004 | if (num_fclk_dpms > 2) { | |
2005 | for (i = 0; i < num_fclk_dpms; i++) { | |
2006 | entry.dcfclk_mhz = 0; | |
2007 | entry.fabricclk_mhz = bw_params->clk_table.entries[i].fclk_mhz; | |
2008 | entry.dram_speed_mts = 0; | |
2009 | ||
2010 | DC_FP_START(); | |
2011 | insert_entry_into_table_sorted(table, num_entries, &entry); | |
2012 | DC_FP_END(); | |
2013 | } | |
2014 | } | |
2015 | // If FCLK fine grained, only insert max | |
2016 | else { | |
2017 | entry.dcfclk_mhz = 0; | |
2018 | entry.fabricclk_mhz = max_fclk_mhz; | |
2019 | entry.dram_speed_mts = 0; | |
2020 | ||
2021 | DC_FP_START(); | |
2022 | insert_entry_into_table_sorted(table, num_entries, &entry); | |
2023 | DC_FP_END(); | |
2024 | } | |
2025 | ||
2026 | // At this point, the table contains all "points of interest" based on | |
2027 | // DPMs from PMFW, and STAs. Table is sorted by BW, and all clock | |
2028 | // ratios (by derate, are exact). | |
2029 | ||
2030 | // Remove states that require higher clocks than are supported | |
2031 | for (i = *num_entries - 1; i >= 0 ; i--) { | |
2032 | if (table[i].dcfclk_mhz > max_dcfclk_mhz || | |
2033 | table[i].fabricclk_mhz > max_fclk_mhz || | |
2034 | table[i].dram_speed_mts > max_uclk_mhz * 16) | |
2035 | remove_entry_from_table_at_index(table, num_entries, i); | |
2036 | } | |
2037 | ||
2038 | // At this point, the table only contains supported points of interest | |
2039 | // it could be used as is, but some states may be redundant due to | |
2040 | // coarse grained nature of some clocks, so we want to round up to | |
2041 | // coarse grained DPMs and remove duplicates. | |
2042 | ||
2043 | // Round up UCLKs | |
2044 | for (i = *num_entries - 1; i >= 0 ; i--) { | |
2045 | for (j = 0; j < num_uclk_dpms; j++) { | |
2046 | if (bw_params->clk_table.entries[j].memclk_mhz * 16 >= table[i].dram_speed_mts) { | |
2047 | table[i].dram_speed_mts = bw_params->clk_table.entries[j].memclk_mhz * 16; | |
2048 | break; | |
2049 | } | |
2050 | } | |
2051 | } | |
2052 | ||
2053 | // If FCLK is coarse grained, round up to next DPMs | |
2054 | if (num_fclk_dpms > 2) { | |
2055 | for (i = *num_entries - 1; i >= 0 ; i--) { | |
2056 | for (j = 0; j < num_fclk_dpms; j++) { | |
2057 | if (bw_params->clk_table.entries[j].fclk_mhz >= table[i].fabricclk_mhz) { | |
2058 | table[i].fabricclk_mhz = bw_params->clk_table.entries[j].fclk_mhz; | |
2059 | break; | |
2060 | } | |
2061 | } | |
2062 | } | |
2063 | } | |
2064 | // Otherwise, round up to minimum. | |
2065 | else { | |
2066 | for (i = *num_entries - 1; i >= 0 ; i--) { | |
2067 | if (table[i].fabricclk_mhz < min_fclk_mhz) { | |
2068 | table[i].fabricclk_mhz = min_fclk_mhz; | |
2069 | break; | |
2070 | } | |
2071 | } | |
2072 | } | |
2073 | ||
2074 | // Round DCFCLKs up to minimum | |
2075 | for (i = *num_entries - 1; i >= 0 ; i--) { | |
2076 | if (table[i].dcfclk_mhz < min_dcfclk_mhz) { | |
2077 | table[i].dcfclk_mhz = min_dcfclk_mhz; | |
2078 | break; | |
2079 | } | |
2080 | } | |
2081 | ||
2082 | // Remove duplicate states, note duplicate states are always neighbouring since table is sorted. | |
2083 | i = 0; | |
2084 | while (i < *num_entries - 1) { | |
2085 | if (table[i].dcfclk_mhz == table[i + 1].dcfclk_mhz && | |
2086 | table[i].fabricclk_mhz == table[i + 1].fabricclk_mhz && | |
2087 | table[i].dram_speed_mts == table[i + 1].dram_speed_mts) | |
2088 | remove_entry_from_table_at_index(table, num_entries, i + 1); | |
2089 | else | |
2090 | i++; | |
2091 | } | |
2092 | ||
2093 | // Fix up the state indicies | |
2094 | for (i = *num_entries - 1; i >= 0 ; i--) { | |
2095 | table[i].state = i; | |
2096 | } | |
2097 | ||
2098 | return 0; | |
2099 | } | |
2100 | ||
2101 | /** | |
2102 | * dcn32_update_bw_bounding_box | |
2103 | * | |
2104 | * This would override some dcn3_2 ip_or_soc initial parameters hardcoded from | |
2105 | * spreadsheet with actual values as per dGPU SKU: | |
2106 | * - with passed few options from dc->config | |
2107 | * - with dentist_vco_frequency from Clk Mgr (currently hardcoded, but might | |
2108 | * need to get it from PM FW) | |
2109 | * - with passed latency values (passed in ns units) in dc-> bb override for | |
2110 | * debugging purposes | |
2111 | * - with passed latencies from VBIOS (in 100_ns units) if available for | |
2112 | * certain dGPU SKU | |
2113 | * - with number of DRAM channels from VBIOS (which differ for certain dGPU SKU | |
2114 | * of the same ASIC) | |
2115 | * - clocks levels with passed clk_table entries from Clk Mgr as reported by PM | |
2116 | * FW for different clocks (which might differ for certain dGPU SKU of the | |
2117 | * same ASIC) | |
2118 | */ | |
2119 | void dcn32_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params) | |
2120 | { | |
2121 | dc_assert_fp_enabled(); | |
2122 | ||
2123 | if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { | |
2124 | /* Overrides from dc->config options */ | |
2125 | dcn3_2_ip.clamp_min_dcfclk = dc->config.clamp_min_dcfclk; | |
2126 | ||
2127 | /* Override from passed dc->bb_overrides if available*/ | |
2128 | if ((int)(dcn3_2_soc.sr_exit_time_us * 1000) != dc->bb_overrides.sr_exit_time_ns | |
2129 | && dc->bb_overrides.sr_exit_time_ns) { | |
2130 | dcn3_2_soc.sr_exit_time_us = dc->bb_overrides.sr_exit_time_ns / 1000.0; | |
2131 | } | |
2132 | ||
2133 | if ((int)(dcn3_2_soc.sr_enter_plus_exit_time_us * 1000) | |
2134 | != dc->bb_overrides.sr_enter_plus_exit_time_ns | |
2135 | && dc->bb_overrides.sr_enter_plus_exit_time_ns) { | |
2136 | dcn3_2_soc.sr_enter_plus_exit_time_us = | |
2137 | dc->bb_overrides.sr_enter_plus_exit_time_ns / 1000.0; | |
2138 | } | |
2139 | ||
2140 | if ((int)(dcn3_2_soc.urgent_latency_us * 1000) != dc->bb_overrides.urgent_latency_ns | |
2141 | && dc->bb_overrides.urgent_latency_ns) { | |
2142 | dcn3_2_soc.urgent_latency_us = dc->bb_overrides.urgent_latency_ns / 1000.0; | |
2143 | } | |
2144 | ||
2145 | if ((int)(dcn3_2_soc.dram_clock_change_latency_us * 1000) | |
2146 | != dc->bb_overrides.dram_clock_change_latency_ns | |
2147 | && dc->bb_overrides.dram_clock_change_latency_ns) { | |
2148 | dcn3_2_soc.dram_clock_change_latency_us = | |
2149 | dc->bb_overrides.dram_clock_change_latency_ns / 1000.0; | |
2150 | } | |
2151 | ||
0cd34ce8 AL |
2152 | if ((int)(dcn3_2_soc.fclk_change_latency_us * 1000) |
2153 | != dc->bb_overrides.fclk_clock_change_latency_ns | |
2154 | && dc->bb_overrides.fclk_clock_change_latency_ns) { | |
2155 | dcn3_2_soc.fclk_change_latency_us = | |
2156 | dc->bb_overrides.fclk_clock_change_latency_ns / 1000; | |
2157 | } | |
2158 | ||
4e14e0fc RS |
2159 | if ((int)(dcn3_2_soc.dummy_pstate_latency_us * 1000) |
2160 | != dc->bb_overrides.dummy_clock_change_latency_ns | |
2161 | && dc->bb_overrides.dummy_clock_change_latency_ns) { | |
2162 | dcn3_2_soc.dummy_pstate_latency_us = | |
2163 | dc->bb_overrides.dummy_clock_change_latency_ns / 1000.0; | |
2164 | } | |
2165 | ||
2166 | /* Override from VBIOS if VBIOS bb_info available */ | |
2167 | if (dc->ctx->dc_bios->funcs->get_soc_bb_info) { | |
2168 | struct bp_soc_bb_info bb_info = {0}; | |
2169 | ||
2170 | if (dc->ctx->dc_bios->funcs->get_soc_bb_info(dc->ctx->dc_bios, &bb_info) == BP_RESULT_OK) { | |
2171 | if (bb_info.dram_clock_change_latency_100ns > 0) | |
bbc9f072 YL |
2172 | dcn3_2_soc.dram_clock_change_latency_us = |
2173 | bb_info.dram_clock_change_latency_100ns * 10; | |
4e14e0fc | 2174 | |
bbc9f072 YL |
2175 | if (bb_info.dram_sr_enter_exit_latency_100ns > 0) |
2176 | dcn3_2_soc.sr_enter_plus_exit_time_us = | |
2177 | bb_info.dram_sr_enter_exit_latency_100ns * 10; | |
4e14e0fc | 2178 | |
bbc9f072 YL |
2179 | if (bb_info.dram_sr_exit_latency_100ns > 0) |
2180 | dcn3_2_soc.sr_exit_time_us = | |
2181 | bb_info.dram_sr_exit_latency_100ns * 10; | |
4e14e0fc RS |
2182 | } |
2183 | } | |
2184 | ||
2185 | /* Override from VBIOS for num_chan */ | |
2186 | if (dc->ctx->dc_bios->vram_info.num_chans) | |
2187 | dcn3_2_soc.num_chans = dc->ctx->dc_bios->vram_info.num_chans; | |
2188 | ||
2189 | if (dc->ctx->dc_bios->vram_info.dram_channel_width_bytes) | |
2190 | dcn3_2_soc.dram_channel_width_bytes = dc->ctx->dc_bios->vram_info.dram_channel_width_bytes; | |
2191 | ||
2192 | } | |
2193 | ||
2194 | /* Override dispclk_dppclk_vco_speed_mhz from Clk Mgr */ | |
2195 | dcn3_2_soc.dispclk_dppclk_vco_speed_mhz = dc->clk_mgr->dentist_vco_freq_khz / 1000.0; | |
2196 | dc->dml.soc.dispclk_dppclk_vco_speed_mhz = dc->clk_mgr->dentist_vco_freq_khz / 1000.0; | |
2197 | ||
2198 | /* Overrides Clock levelsfrom CLK Mgr table entries as reported by PM FW */ | |
2199 | if ((!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) && (bw_params->clk_table.entries[0].memclk_mhz)) { | |
2200 | if (dc->debug.use_legacy_soc_bb_mechanism) { | |
2201 | unsigned int i = 0, j = 0, num_states = 0; | |
2202 | ||
2203 | unsigned int dcfclk_mhz[DC__VOLTAGE_STATES] = {0}; | |
2204 | unsigned int dram_speed_mts[DC__VOLTAGE_STATES] = {0}; | |
2205 | unsigned int optimal_uclk_for_dcfclk_sta_targets[DC__VOLTAGE_STATES] = {0}; | |
2206 | unsigned int optimal_dcfclk_for_uclk[DC__VOLTAGE_STATES] = {0}; | |
2207 | unsigned int min_dcfclk = UINT_MAX; | |
2208 | /* Set 199 as first value in STA target array to have a minimum DCFCLK value. | |
2209 | * For DCN32 we set min to 199 so minimum FCLK DPM0 (300Mhz can be achieved) */ | |
2210 | unsigned int dcfclk_sta_targets[DC__VOLTAGE_STATES] = {199, 615, 906, 1324, 1564}; | |
2211 | unsigned int num_dcfclk_sta_targets = 4, num_uclk_states = 0; | |
2212 | unsigned int max_dcfclk_mhz = 0, max_dispclk_mhz = 0, max_dppclk_mhz = 0, max_phyclk_mhz = 0; | |
2213 | ||
2214 | for (i = 0; i < MAX_NUM_DPM_LVL; i++) { | |
2215 | if (bw_params->clk_table.entries[i].dcfclk_mhz > max_dcfclk_mhz) | |
2216 | max_dcfclk_mhz = bw_params->clk_table.entries[i].dcfclk_mhz; | |
2217 | if (bw_params->clk_table.entries[i].dcfclk_mhz != 0 && | |
2218 | bw_params->clk_table.entries[i].dcfclk_mhz < min_dcfclk) | |
2219 | min_dcfclk = bw_params->clk_table.entries[i].dcfclk_mhz; | |
2220 | if (bw_params->clk_table.entries[i].dispclk_mhz > max_dispclk_mhz) | |
2221 | max_dispclk_mhz = bw_params->clk_table.entries[i].dispclk_mhz; | |
2222 | if (bw_params->clk_table.entries[i].dppclk_mhz > max_dppclk_mhz) | |
2223 | max_dppclk_mhz = bw_params->clk_table.entries[i].dppclk_mhz; | |
2224 | if (bw_params->clk_table.entries[i].phyclk_mhz > max_phyclk_mhz) | |
2225 | max_phyclk_mhz = bw_params->clk_table.entries[i].phyclk_mhz; | |
2226 | } | |
2227 | if (min_dcfclk > dcfclk_sta_targets[0]) | |
2228 | dcfclk_sta_targets[0] = min_dcfclk; | |
2229 | if (!max_dcfclk_mhz) | |
2230 | max_dcfclk_mhz = dcn3_2_soc.clock_limits[0].dcfclk_mhz; | |
2231 | if (!max_dispclk_mhz) | |
2232 | max_dispclk_mhz = dcn3_2_soc.clock_limits[0].dispclk_mhz; | |
2233 | if (!max_dppclk_mhz) | |
2234 | max_dppclk_mhz = dcn3_2_soc.clock_limits[0].dppclk_mhz; | |
2235 | if (!max_phyclk_mhz) | |
2236 | max_phyclk_mhz = dcn3_2_soc.clock_limits[0].phyclk_mhz; | |
2237 | ||
2238 | if (max_dcfclk_mhz > dcfclk_sta_targets[num_dcfclk_sta_targets-1]) { | |
2239 | // If max DCFCLK is greater than the max DCFCLK STA target, insert into the DCFCLK STA target array | |
2240 | dcfclk_sta_targets[num_dcfclk_sta_targets] = max_dcfclk_mhz; | |
2241 | num_dcfclk_sta_targets++; | |
2242 | } else if (max_dcfclk_mhz < dcfclk_sta_targets[num_dcfclk_sta_targets-1]) { | |
2243 | // If max DCFCLK is less than the max DCFCLK STA target, cap values and remove duplicates | |
2244 | for (i = 0; i < num_dcfclk_sta_targets; i++) { | |
2245 | if (dcfclk_sta_targets[i] > max_dcfclk_mhz) { | |
2246 | dcfclk_sta_targets[i] = max_dcfclk_mhz; | |
2247 | break; | |
2248 | } | |
2249 | } | |
2250 | // Update size of array since we "removed" duplicates | |
2251 | num_dcfclk_sta_targets = i + 1; | |
2252 | } | |
2253 | ||
2254 | num_uclk_states = bw_params->clk_table.num_entries; | |
2255 | ||
2256 | // Calculate optimal dcfclk for each uclk | |
2257 | for (i = 0; i < num_uclk_states; i++) { | |
2258 | dcn32_get_optimal_dcfclk_fclk_for_uclk(bw_params->clk_table.entries[i].memclk_mhz * 16, | |
2259 | &optimal_dcfclk_for_uclk[i], NULL); | |
2260 | if (optimal_dcfclk_for_uclk[i] < bw_params->clk_table.entries[0].dcfclk_mhz) { | |
2261 | optimal_dcfclk_for_uclk[i] = bw_params->clk_table.entries[0].dcfclk_mhz; | |
2262 | } | |
2263 | } | |
2264 | ||
2265 | // Calculate optimal uclk for each dcfclk sta target | |
2266 | for (i = 0; i < num_dcfclk_sta_targets; i++) { | |
2267 | for (j = 0; j < num_uclk_states; j++) { | |
2268 | if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j]) { | |
2269 | optimal_uclk_for_dcfclk_sta_targets[i] = | |
2270 | bw_params->clk_table.entries[j].memclk_mhz * 16; | |
2271 | break; | |
2272 | } | |
2273 | } | |
2274 | } | |
2275 | ||
2276 | i = 0; | |
2277 | j = 0; | |
2278 | // create the final dcfclk and uclk table | |
2279 | while (i < num_dcfclk_sta_targets && j < num_uclk_states && num_states < DC__VOLTAGE_STATES) { | |
2280 | if (dcfclk_sta_targets[i] < optimal_dcfclk_for_uclk[j] && i < num_dcfclk_sta_targets) { | |
2281 | dcfclk_mhz[num_states] = dcfclk_sta_targets[i]; | |
2282 | dram_speed_mts[num_states++] = optimal_uclk_for_dcfclk_sta_targets[i++]; | |
2283 | } else { | |
2284 | if (j < num_uclk_states && optimal_dcfclk_for_uclk[j] <= max_dcfclk_mhz) { | |
2285 | dcfclk_mhz[num_states] = optimal_dcfclk_for_uclk[j]; | |
2286 | dram_speed_mts[num_states++] = bw_params->clk_table.entries[j++].memclk_mhz * 16; | |
2287 | } else { | |
2288 | j = num_uclk_states; | |
2289 | } | |
2290 | } | |
2291 | } | |
2292 | ||
2293 | while (i < num_dcfclk_sta_targets && num_states < DC__VOLTAGE_STATES) { | |
2294 | dcfclk_mhz[num_states] = dcfclk_sta_targets[i]; | |
2295 | dram_speed_mts[num_states++] = optimal_uclk_for_dcfclk_sta_targets[i++]; | |
2296 | } | |
2297 | ||
2298 | while (j < num_uclk_states && num_states < DC__VOLTAGE_STATES && | |
2299 | optimal_dcfclk_for_uclk[j] <= max_dcfclk_mhz) { | |
2300 | dcfclk_mhz[num_states] = optimal_dcfclk_for_uclk[j]; | |
2301 | dram_speed_mts[num_states++] = bw_params->clk_table.entries[j++].memclk_mhz * 16; | |
2302 | } | |
2303 | ||
2304 | dcn3_2_soc.num_states = num_states; | |
2305 | for (i = 0; i < dcn3_2_soc.num_states; i++) { | |
2306 | dcn3_2_soc.clock_limits[i].state = i; | |
2307 | dcn3_2_soc.clock_limits[i].dcfclk_mhz = dcfclk_mhz[i]; | |
2308 | dcn3_2_soc.clock_limits[i].fabricclk_mhz = dcfclk_mhz[i]; | |
2309 | ||
2310 | /* Fill all states with max values of all these clocks */ | |
2311 | dcn3_2_soc.clock_limits[i].dispclk_mhz = max_dispclk_mhz; | |
2312 | dcn3_2_soc.clock_limits[i].dppclk_mhz = max_dppclk_mhz; | |
2313 | dcn3_2_soc.clock_limits[i].phyclk_mhz = max_phyclk_mhz; | |
2314 | dcn3_2_soc.clock_limits[i].dscclk_mhz = max_dispclk_mhz / 3; | |
2315 | ||
2316 | /* Populate from bw_params for DTBCLK, SOCCLK */ | |
2317 | if (i > 0) { | |
2318 | if (!bw_params->clk_table.entries[i].dtbclk_mhz) { | |
2319 | dcn3_2_soc.clock_limits[i].dtbclk_mhz = dcn3_2_soc.clock_limits[i-1].dtbclk_mhz; | |
2320 | } else { | |
2321 | dcn3_2_soc.clock_limits[i].dtbclk_mhz = bw_params->clk_table.entries[i].dtbclk_mhz; | |
2322 | } | |
2323 | } else if (bw_params->clk_table.entries[i].dtbclk_mhz) { | |
2324 | dcn3_2_soc.clock_limits[i].dtbclk_mhz = bw_params->clk_table.entries[i].dtbclk_mhz; | |
2325 | } | |
2326 | ||
2327 | if (!bw_params->clk_table.entries[i].socclk_mhz && i > 0) | |
2328 | dcn3_2_soc.clock_limits[i].socclk_mhz = dcn3_2_soc.clock_limits[i-1].socclk_mhz; | |
2329 | else | |
2330 | dcn3_2_soc.clock_limits[i].socclk_mhz = bw_params->clk_table.entries[i].socclk_mhz; | |
2331 | ||
2332 | if (!dram_speed_mts[i] && i > 0) | |
2333 | dcn3_2_soc.clock_limits[i].dram_speed_mts = dcn3_2_soc.clock_limits[i-1].dram_speed_mts; | |
2334 | else | |
2335 | dcn3_2_soc.clock_limits[i].dram_speed_mts = dram_speed_mts[i]; | |
2336 | ||
2337 | /* These clocks cannot come from bw_params, always fill from dcn3_2_soc[0] */ | |
2338 | /* PHYCLK_D18, PHYCLK_D32 */ | |
2339 | dcn3_2_soc.clock_limits[i].phyclk_d18_mhz = dcn3_2_soc.clock_limits[0].phyclk_d18_mhz; | |
2340 | dcn3_2_soc.clock_limits[i].phyclk_d32_mhz = dcn3_2_soc.clock_limits[0].phyclk_d32_mhz; | |
2341 | } | |
2342 | } else { | |
2343 | build_synthetic_soc_states(bw_params, dcn3_2_soc.clock_limits, &dcn3_2_soc.num_states); | |
2344 | } | |
2345 | ||
2346 | /* Re-init DML with updated bb */ | |
2347 | dml_init_instance(&dc->dml, &dcn3_2_soc, &dcn3_2_ip, DML_PROJECT_DCN32); | |
2348 | if (dc->current_state) | |
2349 | dml_init_instance(&dc->current_state->bw_ctx.dml, &dcn3_2_soc, &dcn3_2_ip, DML_PROJECT_DCN32); | |
2350 | } | |
2351 | } | |
2352 |