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