]>
Commit | Line | Data |
---|---|---|
9a70eba7 DL |
1 | /* |
2 | * Copyright 2012-16 Advanced Micro Devices, Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Authors: AMD | |
23 | * | |
24 | */ | |
25 | ||
26 | #include "dce_clocks.h" | |
27 | #include "dm_services.h" | |
28 | #include "reg_helper.h" | |
29 | #include "fixed32_32.h" | |
30 | #include "bios_parser_interface.h" | |
31 | #include "dc.h" | |
15a27de2 HW |
32 | #include "core_dc.h" |
33 | #include "dce_abm.h" | |
9f72f51d | 34 | #include "dmcu.h" |
ff5ef992 AD |
35 | #if defined(CONFIG_DRM_AMD_DC_DCN1_0) |
36 | #include "dcn_calcs.h" | |
37 | #include "core_dc.h" | |
38 | #endif | |
9a70eba7 | 39 | |
15a27de2 HW |
40 | |
41 | ||
e11b86ad DL |
42 | #define TO_DCE_CLOCKS(clocks)\ |
43 | container_of(clocks, struct dce_disp_clk, base) | |
9a70eba7 DL |
44 | |
45 | #define REG(reg) \ | |
46 | (clk_dce->regs->reg) | |
47 | ||
48 | #undef FN | |
49 | #define FN(reg_name, field_name) \ | |
50 | clk_dce->clk_shift->field_name, clk_dce->clk_mask->field_name | |
51 | ||
52 | #define CTX \ | |
53 | clk_dce->base.ctx | |
54 | ||
e11b86ad DL |
55 | /* Max clock values for each state indexed by "enum clocks_state": */ |
56 | static struct state_dependent_clocks dce80_max_clks_by_state[] = { | |
57 | /* ClocksStateInvalid - should not be used */ | |
58 | { .display_clk_khz = 0, .pixel_clk_khz = 0 }, | |
59 | /* ClocksStateUltraLow - not expected to be used for DCE 8.0 */ | |
60 | { .display_clk_khz = 0, .pixel_clk_khz = 0 }, | |
61 | /* ClocksStateLow */ | |
62 | { .display_clk_khz = 352000, .pixel_clk_khz = 330000}, | |
63 | /* ClocksStateNominal */ | |
64 | { .display_clk_khz = 600000, .pixel_clk_khz = 400000 }, | |
65 | /* ClocksStatePerformance */ | |
66 | { .display_clk_khz = 600000, .pixel_clk_khz = 400000 } }; | |
67 | ||
68 | static struct state_dependent_clocks dce110_max_clks_by_state[] = { | |
69 | /*ClocksStateInvalid - should not be used*/ | |
70 | { .display_clk_khz = 0, .pixel_clk_khz = 0 }, | |
71 | /*ClocksStateUltraLow - currently by HW design team not supposed to be used*/ | |
72 | { .display_clk_khz = 352000, .pixel_clk_khz = 330000 }, | |
73 | /*ClocksStateLow*/ | |
74 | { .display_clk_khz = 352000, .pixel_clk_khz = 330000 }, | |
75 | /*ClocksStateNominal*/ | |
76 | { .display_clk_khz = 467000, .pixel_clk_khz = 400000 }, | |
77 | /*ClocksStatePerformance*/ | |
78 | { .display_clk_khz = 643000, .pixel_clk_khz = 400000 } }; | |
79 | ||
80 | static struct state_dependent_clocks dce112_max_clks_by_state[] = { | |
81 | /*ClocksStateInvalid - should not be used*/ | |
82 | { .display_clk_khz = 0, .pixel_clk_khz = 0 }, | |
83 | /*ClocksStateUltraLow - currently by HW design team not supposed to be used*/ | |
84 | { .display_clk_khz = 389189, .pixel_clk_khz = 346672 }, | |
85 | /*ClocksStateLow*/ | |
86 | { .display_clk_khz = 459000, .pixel_clk_khz = 400000 }, | |
87 | /*ClocksStateNominal*/ | |
88 | { .display_clk_khz = 667000, .pixel_clk_khz = 600000 }, | |
89 | /*ClocksStatePerformance*/ | |
90 | { .display_clk_khz = 1132000, .pixel_clk_khz = 600000 } }; | |
91 | ||
2c8ad2d5 AD |
92 | static struct state_dependent_clocks dce120_max_clks_by_state[] = { |
93 | /*ClocksStateInvalid - should not be used*/ | |
94 | { .display_clk_khz = 0, .pixel_clk_khz = 0 }, | |
95 | /*ClocksStateUltraLow - currently by HW design team not supposed to be used*/ | |
96 | { .display_clk_khz = 0, .pixel_clk_khz = 0 }, | |
97 | /*ClocksStateLow*/ | |
98 | { .display_clk_khz = 460000, .pixel_clk_khz = 400000 }, | |
99 | /*ClocksStateNominal*/ | |
100 | { .display_clk_khz = 670000, .pixel_clk_khz = 600000 }, | |
101 | /*ClocksStatePerformance*/ | |
102 | { .display_clk_khz = 1133000, .pixel_clk_khz = 600000 } }; | |
2c8ad2d5 | 103 | |
9a70eba7 | 104 | /* Starting point for each divider range.*/ |
e11b86ad | 105 | enum dce_divider_range_start { |
9a70eba7 DL |
106 | DIVIDER_RANGE_01_START = 200, /* 2.00*/ |
107 | DIVIDER_RANGE_02_START = 1600, /* 16.00*/ | |
108 | DIVIDER_RANGE_03_START = 3200, /* 32.00*/ | |
109 | DIVIDER_RANGE_SCALE_FACTOR = 100 /* Results are scaled up by 100.*/ | |
110 | }; | |
111 | ||
112 | /* Ranges for divider identifiers (Divider ID or DID) | |
113 | mmDENTIST_DISPCLK_CNTL.DENTIST_DISPCLK_WDIVIDER*/ | |
e11b86ad | 114 | enum dce_divider_id_register_setting { |
9a70eba7 DL |
115 | DIVIDER_RANGE_01_BASE_DIVIDER_ID = 0X08, |
116 | DIVIDER_RANGE_02_BASE_DIVIDER_ID = 0X40, | |
117 | DIVIDER_RANGE_03_BASE_DIVIDER_ID = 0X60, | |
118 | DIVIDER_RANGE_MAX_DIVIDER_ID = 0X80 | |
119 | }; | |
120 | ||
121 | /* Step size between each divider within a range. | |
122 | Incrementing the DENTIST_DISPCLK_WDIVIDER by one | |
123 | will increment the divider by this much.*/ | |
e11b86ad | 124 | enum dce_divider_range_step_size { |
9a70eba7 DL |
125 | DIVIDER_RANGE_01_STEP_SIZE = 25, /* 0.25*/ |
126 | DIVIDER_RANGE_02_STEP_SIZE = 50, /* 0.50*/ | |
127 | DIVIDER_RANGE_03_STEP_SIZE = 100 /* 1.00 */ | |
128 | }; | |
129 | ||
e11b86ad DL |
130 | static bool dce_divider_range_construct( |
131 | struct dce_divider_range *div_range, | |
132 | int range_start, | |
133 | int range_step, | |
134 | int did_min, | |
135 | int did_max) | |
136 | { | |
137 | div_range->div_range_start = range_start; | |
138 | div_range->div_range_step = range_step; | |
139 | div_range->did_min = did_min; | |
140 | div_range->did_max = did_max; | |
141 | ||
142 | if (div_range->div_range_step == 0) { | |
143 | div_range->div_range_step = 1; | |
144 | /*div_range_step cannot be zero*/ | |
145 | BREAK_TO_DEBUGGER(); | |
146 | } | |
147 | /* Calculate this based on the other inputs.*/ | |
148 | /* See DividerRange.h for explanation of */ | |
149 | /* the relationship between divider id (DID) and a divider.*/ | |
150 | /* Number of Divider IDs = (Maximum Divider ID - Minimum Divider ID)*/ | |
151 | /* Maximum divider identified in this range = | |
152 | * (Number of Divider IDs)*Step size between dividers | |
153 | * + The start of this range.*/ | |
154 | div_range->div_range_end = (did_max - did_min) * range_step | |
155 | + range_start; | |
156 | return true; | |
157 | } | |
158 | ||
159 | static int dce_divider_range_calc_divider( | |
160 | struct dce_divider_range *div_range, | |
161 | int did) | |
162 | { | |
163 | /* Is this DID within our range?*/ | |
164 | if ((did < div_range->did_min) || (did >= div_range->did_max)) | |
165 | return INVALID_DIVIDER; | |
166 | ||
167 | return ((did - div_range->did_min) * div_range->div_range_step) | |
168 | + div_range->div_range_start; | |
169 | ||
170 | } | |
171 | ||
e11b86ad DL |
172 | static int dce_divider_range_get_divider( |
173 | struct dce_divider_range *div_range, | |
174 | int ranges_num, | |
175 | int did) | |
176 | { | |
177 | int div = INVALID_DIVIDER; | |
178 | int i; | |
9a70eba7 | 179 | |
e11b86ad DL |
180 | for (i = 0; i < ranges_num; i++) { |
181 | /* Calculate divider with given divider ID*/ | |
182 | div = dce_divider_range_calc_divider(&div_range[i], did); | |
183 | /* Found a valid return divider*/ | |
184 | if (div != INVALID_DIVIDER) | |
185 | break; | |
186 | } | |
187 | return div; | |
188 | } | |
189 | ||
e11b86ad | 190 | static int dce_clocks_get_dp_ref_freq(struct display_clock *clk) |
9a70eba7 DL |
191 | { |
192 | struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk); | |
193 | int dprefclk_wdivider; | |
194 | int dprefclk_src_sel; | |
195 | int dp_ref_clk_khz = 600000; | |
196 | int target_div = INVALID_DIVIDER; | |
197 | ||
198 | /* ASSERT DP Reference Clock source is from DFS*/ | |
199 | REG_GET(DPREFCLK_CNTL, DPREFCLK_SRC_SEL, &dprefclk_src_sel); | |
200 | ASSERT(dprefclk_src_sel == 0); | |
201 | ||
202 | /* Read the mmDENTIST_DISPCLK_CNTL to get the currently | |
203 | * programmed DID DENTIST_DPREFCLK_WDIVIDER*/ | |
204 | REG_GET(DENTIST_DISPCLK_CNTL, DENTIST_DPREFCLK_WDIVIDER, &dprefclk_wdivider); | |
205 | ||
206 | /* Convert DENTIST_DPREFCLK_WDIVIDERto actual divider*/ | |
e11b86ad | 207 | target_div = dce_divider_range_get_divider( |
9a70eba7 DL |
208 | clk_dce->divider_ranges, |
209 | DIVIDER_RANGE_MAX, | |
210 | dprefclk_wdivider); | |
211 | ||
212 | if (target_div != INVALID_DIVIDER) { | |
213 | /* Calculate the current DFS clock, in kHz.*/ | |
214 | dp_ref_clk_khz = (DIVIDER_RANGE_SCALE_FACTOR | |
215 | * clk_dce->dentist_vco_freq_khz) / target_div; | |
216 | } | |
217 | ||
218 | /* SW will adjust DP REF Clock average value for all purposes | |
219 | * (DP DTO / DP Audio DTO and DP GTC) | |
220 | if clock is spread for all cases: | |
221 | -if SS enabled on DP Ref clock and HW de-spreading enabled with SW | |
222 | calculations for DS_INCR/DS_MODULO (this is planned to be default case) | |
223 | -if SS enabled on DP Ref clock and HW de-spreading enabled with HW | |
224 | calculations (not planned to be used, but average clock should still | |
225 | be valid) | |
226 | -if SS enabled on DP Ref clock and HW de-spreading disabled | |
227 | (should not be case with CIK) then SW should program all rates | |
228 | generated according to average value (case as with previous ASICs) | |
229 | */ | |
7d091f7a | 230 | if (clk_dce->ss_on_dprefclk && clk_dce->dprefclk_ss_divider != 0) { |
9a70eba7 DL |
231 | struct fixed32_32 ss_percentage = dal_fixed32_32_div_int( |
232 | dal_fixed32_32_from_fraction( | |
7d091f7a HW |
233 | clk_dce->dprefclk_ss_percentage, |
234 | clk_dce->dprefclk_ss_divider), 200); | |
9a70eba7 DL |
235 | struct fixed32_32 adj_dp_ref_clk_khz; |
236 | ||
237 | ss_percentage = dal_fixed32_32_sub(dal_fixed32_32_one, | |
238 | ss_percentage); | |
239 | adj_dp_ref_clk_khz = | |
240 | dal_fixed32_32_mul_int( | |
241 | ss_percentage, | |
242 | dp_ref_clk_khz); | |
243 | dp_ref_clk_khz = dal_fixed32_32_floor(adj_dp_ref_clk_khz); | |
244 | } | |
245 | ||
246 | return dp_ref_clk_khz; | |
247 | } | |
248 | ||
3f6d7435 HW |
249 | /* TODO: This is DCN DPREFCLK: it could be program by DENTIST by VBIOS |
250 | * or CLK0_CLK11 by SMU. For DCE120, it is wlays 600Mhz. Will re-visit | |
251 | * clock implementation | |
252 | */ | |
253 | static int dce_clocks_get_dp_ref_freq_wrkaround(struct display_clock *clk) | |
254 | { | |
255 | struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk); | |
256 | int dp_ref_clk_khz = 600000; | |
257 | ||
258 | if (clk_dce->ss_on_dprefclk && clk_dce->dprefclk_ss_divider != 0) { | |
259 | struct fixed32_32 ss_percentage = dal_fixed32_32_div_int( | |
260 | dal_fixed32_32_from_fraction( | |
261 | clk_dce->dprefclk_ss_percentage, | |
262 | clk_dce->dprefclk_ss_divider), 200); | |
263 | struct fixed32_32 adj_dp_ref_clk_khz; | |
264 | ||
265 | ss_percentage = dal_fixed32_32_sub(dal_fixed32_32_one, | |
266 | ss_percentage); | |
267 | adj_dp_ref_clk_khz = | |
268 | dal_fixed32_32_mul_int( | |
269 | ss_percentage, | |
270 | dp_ref_clk_khz); | |
271 | dp_ref_clk_khz = dal_fixed32_32_floor(adj_dp_ref_clk_khz); | |
272 | } | |
273 | ||
274 | return dp_ref_clk_khz; | |
275 | } | |
9a70eba7 DL |
276 | static enum dm_pp_clocks_state dce_get_required_clocks_state( |
277 | struct display_clock *clk, | |
278 | struct state_dependent_clocks *req_clocks) | |
279 | { | |
280 | struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk); | |
281 | int i; | |
282 | enum dm_pp_clocks_state low_req_clk; | |
283 | ||
284 | /* Iterate from highest supported to lowest valid state, and update | |
285 | * lowest RequiredState with the lowest state that satisfies | |
286 | * all required clocks | |
287 | */ | |
288 | for (i = clk->max_clks_state; i >= DM_PP_CLOCKS_STATE_ULTRA_LOW; i--) | |
289 | if (req_clocks->display_clk_khz > | |
290 | clk_dce->max_clks_by_state[i].display_clk_khz | |
291 | || req_clocks->pixel_clk_khz > | |
292 | clk_dce->max_clks_by_state[i].pixel_clk_khz) | |
293 | break; | |
294 | ||
295 | low_req_clk = i + 1; | |
296 | if (low_req_clk > clk->max_clks_state) { | |
297 | dm_logger_write(clk->ctx->logger, LOG_WARNING, | |
298 | "%s: clocks unsupported", __func__); | |
299 | low_req_clk = DM_PP_CLOCKS_STATE_INVALID; | |
300 | } | |
301 | ||
302 | return low_req_clk; | |
303 | } | |
304 | ||
305 | static bool dce_clock_set_min_clocks_state( | |
306 | struct display_clock *clk, | |
307 | enum dm_pp_clocks_state clocks_state) | |
308 | { | |
309 | struct dm_pp_power_level_change_request level_change_req = { | |
310 | clocks_state }; | |
311 | ||
312 | if (clocks_state > clk->max_clks_state) { | |
313 | /*Requested state exceeds max supported state.*/ | |
314 | dm_logger_write(clk->ctx->logger, LOG_WARNING, | |
315 | "Requested state exceeds max supported state"); | |
316 | return false; | |
317 | } else if (clocks_state == clk->cur_min_clks_state) { | |
318 | /*if we're trying to set the same state, we can just return | |
319 | * since nothing needs to be done*/ | |
320 | return true; | |
321 | } | |
322 | ||
323 | /* get max clock state from PPLIB */ | |
324 | if (dm_pp_apply_power_level_change_request(clk->ctx, &level_change_req)) | |
325 | clk->cur_min_clks_state = clocks_state; | |
326 | ||
327 | return true; | |
328 | } | |
329 | ||
330 | static void dce_set_clock( | |
331 | struct display_clock *clk, | |
e11b86ad | 332 | int requested_clk_khz) |
9a70eba7 DL |
333 | { |
334 | struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk); | |
335 | struct bp_pixel_clock_parameters pxl_clk_params = { 0 }; | |
336 | struct dc_bios *bp = clk->ctx->dc_bios; | |
337 | ||
338 | /* Make sure requested clock isn't lower than minimum threshold*/ | |
339 | if (requested_clk_khz > 0) | |
7d7024ca | 340 | requested_clk_khz = max(requested_clk_khz, |
9a70eba7 DL |
341 | clk_dce->dentist_vco_freq_khz / 64); |
342 | ||
343 | /* Prepare to program display clock*/ | |
344 | pxl_clk_params.target_pixel_clock = requested_clk_khz; | |
345 | pxl_clk_params.pll_id = CLOCK_SOURCE_ID_DFS; | |
346 | ||
347 | bp->funcs->program_display_engine_pll(bp, &pxl_clk_params); | |
348 | ||
349 | if (clk_dce->dfs_bypass_enabled) { | |
350 | ||
351 | /* Cache the fixed display clock*/ | |
352 | clk_dce->dfs_bypass_disp_clk = | |
353 | pxl_clk_params.dfs_bypass_display_clock; | |
354 | } | |
355 | ||
356 | /* from power down, we need mark the clock state as ClocksStateNominal | |
357 | * from HWReset, so when resume we will call pplib voltage regulator.*/ | |
358 | if (requested_clk_khz == 0) | |
359 | clk->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL; | |
360 | } | |
361 | ||
9a70eba7 DL |
362 | static void dce_psr_set_clock( |
363 | struct display_clock *clk, | |
e11b86ad | 364 | int requested_clk_khz) |
9a70eba7 DL |
365 | { |
366 | struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk); | |
9f72f51d AZ |
367 | struct dc_context *ctx = clk_dce->base.ctx; |
368 | struct core_dc *core_dc = DC_TO_CORE(ctx->dc); | |
369 | struct dmcu *dmcu = core_dc->res_pool->dmcu; | |
9a70eba7 DL |
370 | |
371 | dce_set_clock(clk, requested_clk_khz); | |
9f72f51d AZ |
372 | |
373 | dmcu->funcs->set_psr_wait_loop(dmcu, requested_clk_khz / 1000 / 7); | |
9a70eba7 DL |
374 | } |
375 | ||
e11b86ad | 376 | static void dce112_set_clock( |
9a70eba7 | 377 | struct display_clock *clk, |
e11b86ad | 378 | int requested_clk_khz) |
9a70eba7 DL |
379 | { |
380 | struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk); | |
381 | struct bp_set_dce_clock_parameters dce_clk_params; | |
382 | struct dc_bios *bp = clk->ctx->dc_bios; | |
15a27de2 HW |
383 | struct core_dc *core_dc = DC_TO_CORE(clk->ctx->dc); |
384 | struct abm *abm = core_dc->res_pool->abm; | |
9f72f51d | 385 | struct dmcu *dmcu = core_dc->res_pool->dmcu; |
9a70eba7 DL |
386 | |
387 | /* Prepare to program display clock*/ | |
388 | memset(&dce_clk_params, 0, sizeof(dce_clk_params)); | |
389 | ||
390 | /* Make sure requested clock isn't lower than minimum threshold*/ | |
391 | if (requested_clk_khz > 0) | |
7d7024ca | 392 | requested_clk_khz = max(requested_clk_khz, |
e11b86ad | 393 | clk_dce->dentist_vco_freq_khz / 62); |
9a70eba7 DL |
394 | |
395 | dce_clk_params.target_clock_frequency = requested_clk_khz; | |
396 | dce_clk_params.pll_id = CLOCK_SOURCE_ID_DFS; | |
397 | dce_clk_params.clock_type = DCECLOCK_TYPE_DISPLAY_CLOCK; | |
398 | ||
399 | bp->funcs->set_dce_clock(bp, &dce_clk_params); | |
400 | ||
401 | /* from power down, we need mark the clock state as ClocksStateNominal | |
402 | * from HWReset, so when resume we will call pplib voltage regulator.*/ | |
403 | if (requested_clk_khz == 0) | |
404 | clk->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL; | |
405 | ||
406 | /*Program DP ref Clock*/ | |
407 | /*VBIOS will determine DPREFCLK frequency, so we don't set it*/ | |
408 | dce_clk_params.target_clock_frequency = 0; | |
409 | dce_clk_params.clock_type = DCECLOCK_TYPE_DPREFCLK; | |
e11b86ad DL |
410 | dce_clk_params.flags.USE_GENLOCK_AS_SOURCE_FOR_DPREFCLK = |
411 | (dce_clk_params.pll_id == | |
412 | CLOCK_SOURCE_COMBO_DISPLAY_PLL0); | |
9a70eba7 DL |
413 | |
414 | bp->funcs->set_dce_clock(bp, &dce_clk_params); | |
ece4f358 | 415 | |
15a27de2 | 416 | if (abm->funcs->is_dmcu_initialized(abm)) |
9f72f51d AZ |
417 | dmcu->funcs->set_psr_wait_loop(dmcu, |
418 | requested_clk_khz / 1000 / 7); | |
ff5ef992 | 419 | |
9a70eba7 DL |
420 | } |
421 | ||
422 | static void dce_clock_read_integrated_info(struct dce_disp_clk *clk_dce) | |
423 | { | |
424 | struct dc_debug *debug = &clk_dce->base.ctx->dc->debug; | |
425 | struct dc_bios *bp = clk_dce->base.ctx->dc_bios; | |
c2e218dd HW |
426 | struct integrated_info info = { { { 0 } } }; |
427 | struct firmware_info fw_info = { { 0 } }; | |
9a70eba7 DL |
428 | int i; |
429 | ||
430 | if (bp->integrated_info) | |
431 | info = *bp->integrated_info; | |
432 | ||
433 | clk_dce->dentist_vco_freq_khz = info.dentist_vco_freq; | |
434 | if (clk_dce->dentist_vco_freq_khz == 0) { | |
435 | bp->funcs->get_firmware_info(bp, &fw_info); | |
436 | clk_dce->dentist_vco_freq_khz = | |
437 | fw_info.smu_gpu_pll_output_freq; | |
438 | if (clk_dce->dentist_vco_freq_khz == 0) | |
439 | clk_dce->dentist_vco_freq_khz = 3600000; | |
440 | } | |
441 | ||
442 | /*update the maximum display clock for each power state*/ | |
443 | for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { | |
444 | enum dm_pp_clocks_state clk_state = DM_PP_CLOCKS_STATE_INVALID; | |
445 | ||
446 | switch (i) { | |
447 | case 0: | |
448 | clk_state = DM_PP_CLOCKS_STATE_ULTRA_LOW; | |
449 | break; | |
450 | ||
451 | case 1: | |
452 | clk_state = DM_PP_CLOCKS_STATE_LOW; | |
453 | break; | |
454 | ||
455 | case 2: | |
456 | clk_state = DM_PP_CLOCKS_STATE_NOMINAL; | |
457 | break; | |
458 | ||
459 | case 3: | |
460 | clk_state = DM_PP_CLOCKS_STATE_PERFORMANCE; | |
461 | break; | |
462 | ||
463 | default: | |
464 | clk_state = DM_PP_CLOCKS_STATE_INVALID; | |
465 | break; | |
466 | } | |
467 | ||
468 | /*Do not allow bad VBIOS/SBIOS to override with invalid values, | |
469 | * check for > 100MHz*/ | |
470 | if (info.disp_clk_voltage[i].max_supported_clk >= 100000) | |
471 | clk_dce->max_clks_by_state[clk_state].display_clk_khz = | |
472 | info.disp_clk_voltage[i].max_supported_clk; | |
473 | } | |
474 | ||
85944914 | 475 | if (!debug->disable_dfs_bypass && bp->integrated_info) |
9a70eba7 DL |
476 | if (bp->integrated_info->gpu_cap_info & DFS_BYPASS_ENABLE) |
477 | clk_dce->dfs_bypass_enabled = true; | |
478 | ||
479 | clk_dce->use_max_disp_clk = debug->max_disp_clk; | |
480 | } | |
481 | ||
482 | static void dce_clock_read_ss_info(struct dce_disp_clk *clk_dce) | |
483 | { | |
484 | struct dc_bios *bp = clk_dce->base.ctx->dc_bios; | |
485 | int ss_info_num = bp->funcs->get_ss_entry_number( | |
486 | bp, AS_SIGNAL_TYPE_GPU_PLL); | |
487 | ||
488 | if (ss_info_num) { | |
c2e218dd | 489 | struct spread_spectrum_info info = { { 0 } }; |
9a70eba7 DL |
490 | enum bp_result result = bp->funcs->get_spread_spectrum_info( |
491 | bp, AS_SIGNAL_TYPE_GPU_PLL, 0, &info); | |
492 | ||
493 | /* Based on VBIOS, VBIOS will keep entry for GPU PLL SS | |
494 | * even if SS not enabled and in that case | |
495 | * SSInfo.spreadSpectrumPercentage !=0 would be sign | |
496 | * that SS is enabled | |
497 | */ | |
498 | if (result == BP_RESULT_OK && | |
499 | info.spread_spectrum_percentage != 0) { | |
7d091f7a HW |
500 | clk_dce->ss_on_dprefclk = true; |
501 | clk_dce->dprefclk_ss_divider = info.spread_percentage_divider; | |
9a70eba7 DL |
502 | |
503 | if (info.type.CENTER_MODE == 0) { | |
7d091f7a | 504 | /* TODO: Currently for DP Reference clock we |
9a70eba7 DL |
505 | * need only SS percentage for |
506 | * downspread */ | |
7d091f7a | 507 | clk_dce->dprefclk_ss_percentage = |
9a70eba7 DL |
508 | info.spread_spectrum_percentage; |
509 | } | |
7d091f7a HW |
510 | |
511 | return; | |
9a70eba7 DL |
512 | } |
513 | ||
7d091f7a HW |
514 | result = bp->funcs->get_spread_spectrum_info( |
515 | bp, AS_SIGNAL_TYPE_DISPLAY_PORT, 0, &info); | |
516 | ||
517 | /* Based on VBIOS, VBIOS will keep entry for DPREFCLK SS | |
518 | * even if SS not enabled and in that case | |
519 | * SSInfo.spreadSpectrumPercentage !=0 would be sign | |
520 | * that SS is enabled | |
521 | */ | |
522 | if (result == BP_RESULT_OK && | |
523 | info.spread_spectrum_percentage != 0) { | |
524 | clk_dce->ss_on_dprefclk = true; | |
525 | clk_dce->dprefclk_ss_divider = info.spread_percentage_divider; | |
526 | ||
527 | if (info.type.CENTER_MODE == 0) { | |
528 | /* Currently for DP Reference clock we | |
529 | * need only SS percentage for | |
530 | * downspread */ | |
531 | clk_dce->dprefclk_ss_percentage = | |
532 | info.spread_spectrum_percentage; | |
533 | } | |
534 | } | |
9a70eba7 DL |
535 | } |
536 | } | |
537 | ||
2c8ad2d5 AD |
538 | static bool dce_apply_clock_voltage_request( |
539 | struct display_clock *clk, | |
540 | enum dm_pp_clock_type clocks_type, | |
541 | int clocks_in_khz, | |
542 | bool pre_mode_set, | |
543 | bool update_dp_phyclk) | |
544 | { | |
fd8cc371 | 545 | bool send_request = false; |
2c8ad2d5 AD |
546 | struct dm_pp_clock_for_voltage_req clock_voltage_req = {0}; |
547 | ||
548 | switch (clocks_type) { | |
549 | case DM_PP_CLOCK_TYPE_DISPLAY_CLK: | |
550 | case DM_PP_CLOCK_TYPE_PIXELCLK: | |
551 | case DM_PP_CLOCK_TYPE_DISPLAYPHYCLK: | |
552 | break; | |
553 | default: | |
554 | BREAK_TO_DEBUGGER(); | |
555 | return false; | |
556 | } | |
557 | ||
558 | clock_voltage_req.clk_type = clocks_type; | |
559 | clock_voltage_req.clocks_in_khz = clocks_in_khz; | |
560 | ||
561 | /* to pplib */ | |
562 | if (pre_mode_set) { | |
563 | switch (clocks_type) { | |
564 | case DM_PP_CLOCK_TYPE_DISPLAY_CLK: | |
565 | if (clocks_in_khz > clk->cur_clocks_value.dispclk_in_khz) { | |
2c8ad2d5 | 566 | clk->cur_clocks_value.dispclk_notify_pplib_done = true; |
fd8cc371 | 567 | send_request = true; |
2c8ad2d5 AD |
568 | } else |
569 | clk->cur_clocks_value.dispclk_notify_pplib_done = false; | |
570 | /* no matter incrase or decrase clock, update current clock value */ | |
571 | clk->cur_clocks_value.dispclk_in_khz = clocks_in_khz; | |
572 | break; | |
573 | case DM_PP_CLOCK_TYPE_PIXELCLK: | |
574 | if (clocks_in_khz > clk->cur_clocks_value.max_pixelclk_in_khz) { | |
2c8ad2d5 | 575 | clk->cur_clocks_value.pixelclk_notify_pplib_done = true; |
fd8cc371 | 576 | send_request = true; |
2c8ad2d5 AD |
577 | } else |
578 | clk->cur_clocks_value.pixelclk_notify_pplib_done = false; | |
579 | /* no matter incrase or decrase clock, update current clock value */ | |
580 | clk->cur_clocks_value.max_pixelclk_in_khz = clocks_in_khz; | |
581 | break; | |
582 | case DM_PP_CLOCK_TYPE_DISPLAYPHYCLK: | |
583 | if (clocks_in_khz > clk->cur_clocks_value.max_non_dp_phyclk_in_khz) { | |
2c8ad2d5 | 584 | clk->cur_clocks_value.phyclk_notigy_pplib_done = true; |
fd8cc371 | 585 | send_request = true; |
2c8ad2d5 AD |
586 | } else |
587 | clk->cur_clocks_value.phyclk_notigy_pplib_done = false; | |
588 | /* no matter incrase or decrase clock, update current clock value */ | |
589 | clk->cur_clocks_value.max_non_dp_phyclk_in_khz = clocks_in_khz; | |
590 | break; | |
591 | default: | |
592 | ASSERT(0); | |
593 | break; | |
594 | } | |
fd8cc371 | 595 | |
2c8ad2d5 AD |
596 | } else { |
597 | switch (clocks_type) { | |
598 | case DM_PP_CLOCK_TYPE_DISPLAY_CLK: | |
599 | if (!clk->cur_clocks_value.dispclk_notify_pplib_done) | |
fd8cc371 | 600 | send_request = true; |
2c8ad2d5 AD |
601 | break; |
602 | case DM_PP_CLOCK_TYPE_PIXELCLK: | |
603 | if (!clk->cur_clocks_value.pixelclk_notify_pplib_done) | |
fd8cc371 | 604 | send_request = true; |
2c8ad2d5 AD |
605 | break; |
606 | case DM_PP_CLOCK_TYPE_DISPLAYPHYCLK: | |
607 | if (!clk->cur_clocks_value.phyclk_notigy_pplib_done) | |
fd8cc371 | 608 | send_request = true; |
2c8ad2d5 AD |
609 | break; |
610 | default: | |
611 | ASSERT(0); | |
612 | break; | |
613 | } | |
614 | } | |
fd8cc371 | 615 | if (send_request) { |
ff5ef992 | 616 | #if defined(CONFIG_DRM_AMD_DC_DCN1_0) |
c8210d5a HW |
617 | if (clk->ctx->dce_version == DCN_VERSION_1_0) { |
618 | struct core_dc *core_dc = DC_TO_CORE(clk->ctx->dc); | |
619 | /*use dcfclk request voltage*/ | |
620 | clock_voltage_req.clk_type = DM_PP_CLOCK_TYPE_DCFCLK; | |
621 | clock_voltage_req.clocks_in_khz = | |
ff5ef992 | 622 | dcn_find_dcfclk_suits_all(core_dc, &clk->cur_clocks_value); |
c8210d5a | 623 | } |
ff5ef992 | 624 | #endif |
fd8cc371 CL |
625 | dm_pp_apply_clock_for_voltage_request( |
626 | clk->ctx, &clock_voltage_req); | |
627 | } | |
2c8ad2d5 AD |
628 | if (update_dp_phyclk && (clocks_in_khz > |
629 | clk->cur_clocks_value.max_dp_phyclk_in_khz)) | |
630 | clk->cur_clocks_value.max_dp_phyclk_in_khz = clocks_in_khz; | |
631 | ||
632 | return true; | |
633 | } | |
634 | ||
fd8cc371 | 635 | |
2c8ad2d5 | 636 | static const struct display_clock_funcs dce120_funcs = { |
3f6d7435 | 637 | .get_dp_ref_clk_frequency = dce_clocks_get_dp_ref_freq_wrkaround, |
2c8ad2d5 AD |
638 | .apply_clock_voltage_request = dce_apply_clock_voltage_request, |
639 | .set_clock = dce112_set_clock | |
640 | }; | |
2c8ad2d5 | 641 | |
9a70eba7 DL |
642 | static const struct display_clock_funcs dce112_funcs = { |
643 | .get_dp_ref_clk_frequency = dce_clocks_get_dp_ref_freq, | |
644 | .get_required_clocks_state = dce_get_required_clocks_state, | |
645 | .set_min_clocks_state = dce_clock_set_min_clocks_state, | |
e11b86ad | 646 | .set_clock = dce112_set_clock |
9a70eba7 DL |
647 | }; |
648 | ||
649 | static const struct display_clock_funcs dce110_funcs = { | |
650 | .get_dp_ref_clk_frequency = dce_clocks_get_dp_ref_freq, | |
651 | .get_required_clocks_state = dce_get_required_clocks_state, | |
652 | .set_min_clocks_state = dce_clock_set_min_clocks_state, | |
653 | .set_clock = dce_psr_set_clock | |
654 | }; | |
655 | ||
656 | static const struct display_clock_funcs dce_funcs = { | |
657 | .get_dp_ref_clk_frequency = dce_clocks_get_dp_ref_freq, | |
658 | .get_required_clocks_state = dce_get_required_clocks_state, | |
659 | .set_min_clocks_state = dce_clock_set_min_clocks_state, | |
660 | .set_clock = dce_set_clock | |
661 | }; | |
662 | ||
663 | static void dce_disp_clk_construct( | |
664 | struct dce_disp_clk *clk_dce, | |
665 | struct dc_context *ctx, | |
666 | const struct dce_disp_clk_registers *regs, | |
667 | const struct dce_disp_clk_shift *clk_shift, | |
668 | const struct dce_disp_clk_mask *clk_mask) | |
669 | { | |
670 | struct display_clock *base = &clk_dce->base; | |
671 | ||
672 | base->ctx = ctx; | |
673 | base->funcs = &dce_funcs; | |
674 | ||
675 | clk_dce->regs = regs; | |
676 | clk_dce->clk_shift = clk_shift; | |
677 | clk_dce->clk_mask = clk_mask; | |
678 | ||
679 | clk_dce->dfs_bypass_disp_clk = 0; | |
7d091f7a HW |
680 | |
681 | clk_dce->dprefclk_ss_percentage = 0; | |
682 | clk_dce->dprefclk_ss_divider = 1000; | |
683 | clk_dce->ss_on_dprefclk = false; | |
684 | ||
9a70eba7 DL |
685 | base->max_clks_state = DM_PP_CLOCKS_STATE_NOMINAL; |
686 | base->cur_min_clks_state = DM_PP_CLOCKS_STATE_INVALID; | |
687 | ||
688 | dce_clock_read_integrated_info(clk_dce); | |
689 | dce_clock_read_ss_info(clk_dce); | |
690 | ||
e11b86ad | 691 | dce_divider_range_construct( |
9a70eba7 DL |
692 | &clk_dce->divider_ranges[DIVIDER_RANGE_01], |
693 | DIVIDER_RANGE_01_START, | |
694 | DIVIDER_RANGE_01_STEP_SIZE, | |
695 | DIVIDER_RANGE_01_BASE_DIVIDER_ID, | |
696 | DIVIDER_RANGE_02_BASE_DIVIDER_ID); | |
e11b86ad | 697 | dce_divider_range_construct( |
9a70eba7 DL |
698 | &clk_dce->divider_ranges[DIVIDER_RANGE_02], |
699 | DIVIDER_RANGE_02_START, | |
700 | DIVIDER_RANGE_02_STEP_SIZE, | |
701 | DIVIDER_RANGE_02_BASE_DIVIDER_ID, | |
702 | DIVIDER_RANGE_03_BASE_DIVIDER_ID); | |
e11b86ad | 703 | dce_divider_range_construct( |
9a70eba7 DL |
704 | &clk_dce->divider_ranges[DIVIDER_RANGE_03], |
705 | DIVIDER_RANGE_03_START, | |
706 | DIVIDER_RANGE_03_STEP_SIZE, | |
707 | DIVIDER_RANGE_03_BASE_DIVIDER_ID, | |
708 | DIVIDER_RANGE_MAX_DIVIDER_ID); | |
709 | } | |
710 | ||
711 | struct display_clock *dce_disp_clk_create( | |
712 | struct dc_context *ctx, | |
713 | const struct dce_disp_clk_registers *regs, | |
714 | const struct dce_disp_clk_shift *clk_shift, | |
715 | const struct dce_disp_clk_mask *clk_mask) | |
716 | { | |
717 | struct dce_disp_clk *clk_dce = dm_alloc(sizeof(*clk_dce)); | |
718 | ||
719 | if (clk_dce == NULL) { | |
720 | BREAK_TO_DEBUGGER(); | |
721 | return NULL; | |
722 | } | |
723 | ||
e11b86ad DL |
724 | memcpy(clk_dce->max_clks_by_state, |
725 | dce80_max_clks_by_state, | |
726 | sizeof(dce80_max_clks_by_state)); | |
727 | ||
9a70eba7 DL |
728 | dce_disp_clk_construct( |
729 | clk_dce, ctx, regs, clk_shift, clk_mask); | |
730 | ||
731 | return &clk_dce->base; | |
732 | } | |
733 | ||
734 | struct display_clock *dce110_disp_clk_create( | |
735 | struct dc_context *ctx, | |
736 | const struct dce_disp_clk_registers *regs, | |
737 | const struct dce_disp_clk_shift *clk_shift, | |
738 | const struct dce_disp_clk_mask *clk_mask) | |
739 | { | |
740 | struct dce_disp_clk *clk_dce = dm_alloc(sizeof(*clk_dce)); | |
741 | ||
742 | if (clk_dce == NULL) { | |
743 | BREAK_TO_DEBUGGER(); | |
744 | return NULL; | |
745 | } | |
746 | ||
e11b86ad DL |
747 | memcpy(clk_dce->max_clks_by_state, |
748 | dce110_max_clks_by_state, | |
749 | sizeof(dce110_max_clks_by_state)); | |
750 | ||
9a70eba7 DL |
751 | dce_disp_clk_construct( |
752 | clk_dce, ctx, regs, clk_shift, clk_mask); | |
753 | ||
754 | clk_dce->base.funcs = &dce110_funcs; | |
755 | ||
756 | return &clk_dce->base; | |
757 | } | |
758 | ||
759 | struct display_clock *dce112_disp_clk_create( | |
760 | struct dc_context *ctx, | |
761 | const struct dce_disp_clk_registers *regs, | |
762 | const struct dce_disp_clk_shift *clk_shift, | |
763 | const struct dce_disp_clk_mask *clk_mask) | |
764 | { | |
765 | struct dce_disp_clk *clk_dce = dm_alloc(sizeof(*clk_dce)); | |
766 | ||
767 | if (clk_dce == NULL) { | |
768 | BREAK_TO_DEBUGGER(); | |
769 | return NULL; | |
770 | } | |
771 | ||
e11b86ad DL |
772 | memcpy(clk_dce->max_clks_by_state, |
773 | dce112_max_clks_by_state, | |
774 | sizeof(dce112_max_clks_by_state)); | |
775 | ||
9a70eba7 DL |
776 | dce_disp_clk_construct( |
777 | clk_dce, ctx, regs, clk_shift, clk_mask); | |
778 | ||
779 | clk_dce->base.funcs = &dce112_funcs; | |
780 | ||
781 | return &clk_dce->base; | |
782 | } | |
783 | ||
b1a4eb99 | 784 | struct display_clock *dce120_disp_clk_create(struct dc_context *ctx) |
2c8ad2d5 AD |
785 | { |
786 | struct dce_disp_clk *clk_dce = dm_alloc(sizeof(*clk_dce)); | |
787 | struct dm_pp_clock_levels_with_voltage clk_level_info = {0}; | |
788 | ||
789 | if (clk_dce == NULL) { | |
790 | BREAK_TO_DEBUGGER(); | |
791 | return NULL; | |
792 | } | |
793 | ||
794 | memcpy(clk_dce->max_clks_by_state, | |
795 | dce120_max_clks_by_state, | |
796 | sizeof(dce120_max_clks_by_state)); | |
797 | ||
798 | dce_disp_clk_construct( | |
b1a4eb99 | 799 | clk_dce, ctx, NULL, NULL, NULL); |
2c8ad2d5 AD |
800 | |
801 | clk_dce->base.funcs = &dce120_funcs; | |
802 | ||
803 | /* new in dce120 */ | |
804 | if (!ctx->dc->debug.disable_pplib_clock_request && | |
805 | dm_pp_get_clock_levels_by_type_with_voltage( | |
806 | ctx, DM_PP_CLOCK_TYPE_DISPLAY_CLK, &clk_level_info) | |
807 | && clk_level_info.num_levels) | |
808 | clk_dce->max_displ_clk_in_khz = | |
809 | clk_level_info.data[clk_level_info.num_levels - 1].clocks_in_khz; | |
810 | else | |
811 | clk_dce->max_displ_clk_in_khz = 1133000; | |
812 | ||
813 | return &clk_dce->base; | |
814 | } | |
2c8ad2d5 | 815 | |
9a70eba7 DL |
816 | void dce_disp_clk_destroy(struct display_clock **disp_clk) |
817 | { | |
818 | struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(*disp_clk); | |
819 | ||
820 | dm_free(clk_dce); | |
821 | *disp_clk = NULL; | |
822 | } |