Commit | Line | Data |
---|---|---|
4562236b HW |
1 | /* |
2 | * Copyright 2012-15 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 "dm_services.h" | |
27 | ||
28 | ||
29 | #include "dc_types.h" | |
30 | #include "core_types.h" | |
31 | ||
32 | #include "include/grph_object_id.h" | |
33 | #include "include/logger_interface.h" | |
34 | ||
35 | #include "dce_clock_source.h" | |
36 | ||
37 | #include "reg_helper.h" | |
38 | ||
39 | #define REG(reg)\ | |
40 | (clk_src->regs->reg) | |
41 | ||
42 | #define CTX \ | |
43 | clk_src->base.ctx | |
5d4b05dd BL |
44 | |
45 | #define DC_LOGGER_INIT() | |
46 | ||
4562236b HW |
47 | #undef FN |
48 | #define FN(reg_name, field_name) \ | |
49 | clk_src->cs_shift->field_name, clk_src->cs_mask->field_name | |
50 | ||
51 | #define FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM 6 | |
52 | #define CALC_PLL_CLK_SRC_ERR_TOLERANCE 1 | |
53 | #define MAX_PLL_CALC_ERROR 0xFFFFFFFF | |
54 | ||
55 | static const struct spread_spectrum_data *get_ss_data_entry( | |
56 | struct dce110_clk_src *clk_src, | |
57 | enum signal_type signal, | |
58 | uint32_t pix_clk_khz) | |
59 | { | |
60 | ||
61 | uint32_t entrys_num; | |
62 | uint32_t i; | |
63 | struct spread_spectrum_data *ss_parm = NULL; | |
64 | struct spread_spectrum_data *ret = NULL; | |
65 | ||
66 | switch (signal) { | |
67 | case SIGNAL_TYPE_DVI_SINGLE_LINK: | |
68 | case SIGNAL_TYPE_DVI_DUAL_LINK: | |
69 | ss_parm = clk_src->dvi_ss_params; | |
70 | entrys_num = clk_src->dvi_ss_params_cnt; | |
71 | break; | |
72 | ||
73 | case SIGNAL_TYPE_HDMI_TYPE_A: | |
74 | ss_parm = clk_src->hdmi_ss_params; | |
75 | entrys_num = clk_src->hdmi_ss_params_cnt; | |
76 | break; | |
77 | ||
78 | case SIGNAL_TYPE_DISPLAY_PORT: | |
79 | case SIGNAL_TYPE_DISPLAY_PORT_MST: | |
80 | case SIGNAL_TYPE_EDP: | |
81 | case SIGNAL_TYPE_VIRTUAL: | |
82 | ss_parm = clk_src->dp_ss_params; | |
83 | entrys_num = clk_src->dp_ss_params_cnt; | |
84 | break; | |
85 | ||
86 | default: | |
87 | ss_parm = NULL; | |
88 | entrys_num = 0; | |
89 | break; | |
90 | } | |
91 | ||
92 | if (ss_parm == NULL) | |
93 | return ret; | |
94 | ||
95 | for (i = 0; i < entrys_num; ++i, ++ss_parm) { | |
96 | if (ss_parm->freq_range_khz >= pix_clk_khz) { | |
97 | ret = ss_parm; | |
98 | break; | |
99 | } | |
100 | } | |
101 | ||
102 | return ret; | |
103 | } | |
104 | ||
105 | /** | |
106 | * Function: calculate_fb_and_fractional_fb_divider | |
107 | * | |
108 | * * DESCRIPTION: Calculates feedback and fractional feedback dividers values | |
109 | * | |
110 | *PARAMETERS: | |
111 | * targetPixelClock Desired frequency in 10 KHz | |
112 | * ref_divider Reference divider (already known) | |
113 | * postDivider Post Divider (already known) | |
114 | * feedback_divider_param Pointer where to store | |
115 | * calculated feedback divider value | |
116 | * fract_feedback_divider_param Pointer where to store | |
117 | * calculated fract feedback divider value | |
118 | * | |
119 | *RETURNS: | |
120 | * It fills the locations pointed by feedback_divider_param | |
121 | * and fract_feedback_divider_param | |
122 | * It returns - true if feedback divider not 0 | |
123 | * - false should never happen) | |
124 | */ | |
125 | static bool calculate_fb_and_fractional_fb_divider( | |
126 | struct calc_pll_clock_source *calc_pll_cs, | |
127 | uint32_t target_pix_clk_khz, | |
128 | uint32_t ref_divider, | |
129 | uint32_t post_divider, | |
130 | uint32_t *feedback_divider_param, | |
131 | uint32_t *fract_feedback_divider_param) | |
132 | { | |
133 | uint64_t feedback_divider; | |
134 | ||
135 | feedback_divider = | |
136 | (uint64_t)(target_pix_clk_khz * ref_divider * post_divider); | |
137 | feedback_divider *= 10; | |
138 | /* additional factor, since we divide by 10 afterwards */ | |
139 | feedback_divider *= (uint64_t)(calc_pll_cs->fract_fb_divider_factor); | |
140 | feedback_divider = div_u64(feedback_divider, calc_pll_cs->ref_freq_khz); | |
141 | ||
142 | /*Round to the number of precision | |
143 | * The following code replace the old code (ullfeedbackDivider + 5)/10 | |
144 | * for example if the difference between the number | |
145 | * of fractional feedback decimal point and the fractional FB Divider precision | |
146 | * is 2 then the equation becomes (ullfeedbackDivider + 5*100) / (10*100))*/ | |
147 | ||
148 | feedback_divider += (uint64_t) | |
149 | (5 * calc_pll_cs->fract_fb_divider_precision_factor); | |
150 | feedback_divider = | |
151 | div_u64(feedback_divider, | |
152 | calc_pll_cs->fract_fb_divider_precision_factor * 10); | |
153 | feedback_divider *= (uint64_t) | |
154 | (calc_pll_cs->fract_fb_divider_precision_factor); | |
155 | ||
156 | *feedback_divider_param = | |
157 | div_u64_rem( | |
158 | feedback_divider, | |
159 | calc_pll_cs->fract_fb_divider_factor, | |
160 | fract_feedback_divider_param); | |
161 | ||
162 | if (*feedback_divider_param != 0) | |
163 | return true; | |
164 | return false; | |
165 | } | |
166 | ||
167 | /** | |
168 | *calc_fb_divider_checking_tolerance | |
169 | * | |
170 | *DESCRIPTION: Calculates Feedback and Fractional Feedback divider values | |
171 | * for passed Reference and Post divider, checking for tolerance. | |
172 | *PARAMETERS: | |
173 | * pll_settings Pointer to structure | |
174 | * ref_divider Reference divider (already known) | |
175 | * postDivider Post Divider (already known) | |
176 | * tolerance Tolerance for Calculated Pixel Clock to be within | |
177 | * | |
178 | *RETURNS: | |
179 | * It fills the PLLSettings structure with PLL Dividers values | |
180 | * if calculated values are within required tolerance | |
181 | * It returns - true if eror is within tolerance | |
182 | * - false if eror is not within tolerance | |
183 | */ | |
184 | static bool calc_fb_divider_checking_tolerance( | |
185 | struct calc_pll_clock_source *calc_pll_cs, | |
186 | struct pll_settings *pll_settings, | |
187 | uint32_t ref_divider, | |
188 | uint32_t post_divider, | |
189 | uint32_t tolerance) | |
190 | { | |
191 | uint32_t feedback_divider; | |
192 | uint32_t fract_feedback_divider; | |
193 | uint32_t actual_calculated_clock_khz; | |
194 | uint32_t abs_err; | |
195 | uint64_t actual_calc_clk_khz; | |
196 | ||
197 | calculate_fb_and_fractional_fb_divider( | |
198 | calc_pll_cs, | |
199 | pll_settings->adjusted_pix_clk, | |
200 | ref_divider, | |
201 | post_divider, | |
202 | &feedback_divider, | |
203 | &fract_feedback_divider); | |
204 | ||
205 | /*Actual calculated value*/ | |
206 | actual_calc_clk_khz = (uint64_t)(feedback_divider * | |
207 | calc_pll_cs->fract_fb_divider_factor) + | |
208 | fract_feedback_divider; | |
209 | actual_calc_clk_khz *= calc_pll_cs->ref_freq_khz; | |
210 | actual_calc_clk_khz = | |
211 | div_u64(actual_calc_clk_khz, | |
212 | ref_divider * post_divider * | |
213 | calc_pll_cs->fract_fb_divider_factor); | |
214 | ||
215 | actual_calculated_clock_khz = (uint32_t)(actual_calc_clk_khz); | |
216 | ||
217 | abs_err = (actual_calculated_clock_khz > | |
218 | pll_settings->adjusted_pix_clk) | |
219 | ? actual_calculated_clock_khz - | |
220 | pll_settings->adjusted_pix_clk | |
221 | : pll_settings->adjusted_pix_clk - | |
222 | actual_calculated_clock_khz; | |
223 | ||
224 | if (abs_err <= tolerance) { | |
225 | /*found good values*/ | |
226 | pll_settings->reference_freq = calc_pll_cs->ref_freq_khz; | |
227 | pll_settings->reference_divider = ref_divider; | |
228 | pll_settings->feedback_divider = feedback_divider; | |
229 | pll_settings->fract_feedback_divider = fract_feedback_divider; | |
230 | pll_settings->pix_clk_post_divider = post_divider; | |
231 | pll_settings->calculated_pix_clk = | |
232 | actual_calculated_clock_khz; | |
233 | pll_settings->vco_freq = | |
234 | actual_calculated_clock_khz * post_divider; | |
235 | return true; | |
236 | } | |
237 | return false; | |
238 | } | |
239 | ||
240 | static bool calc_pll_dividers_in_range( | |
241 | struct calc_pll_clock_source *calc_pll_cs, | |
242 | struct pll_settings *pll_settings, | |
243 | uint32_t min_ref_divider, | |
244 | uint32_t max_ref_divider, | |
245 | uint32_t min_post_divider, | |
246 | uint32_t max_post_divider, | |
247 | uint32_t err_tolerance) | |
248 | { | |
249 | uint32_t ref_divider; | |
250 | uint32_t post_divider; | |
251 | uint32_t tolerance; | |
252 | ||
253 | /* This is err_tolerance / 10000 = 0.0025 - acceptable error of 0.25% | |
254 | * This is errorTolerance / 10000 = 0.0001 - acceptable error of 0.01%*/ | |
255 | tolerance = (pll_settings->adjusted_pix_clk * err_tolerance) / | |
256 | 10000; | |
257 | if (tolerance < CALC_PLL_CLK_SRC_ERR_TOLERANCE) | |
258 | tolerance = CALC_PLL_CLK_SRC_ERR_TOLERANCE; | |
259 | ||
260 | for ( | |
261 | post_divider = max_post_divider; | |
262 | post_divider >= min_post_divider; | |
263 | --post_divider) { | |
264 | for ( | |
265 | ref_divider = min_ref_divider; | |
266 | ref_divider <= max_ref_divider; | |
267 | ++ref_divider) { | |
268 | if (calc_fb_divider_checking_tolerance( | |
269 | calc_pll_cs, | |
270 | pll_settings, | |
271 | ref_divider, | |
272 | post_divider, | |
273 | tolerance)) { | |
274 | return true; | |
275 | } | |
276 | } | |
277 | } | |
278 | ||
279 | return false; | |
280 | } | |
281 | ||
282 | static uint32_t calculate_pixel_clock_pll_dividers( | |
283 | struct calc_pll_clock_source *calc_pll_cs, | |
284 | struct pll_settings *pll_settings) | |
285 | { | |
286 | uint32_t err_tolerance; | |
287 | uint32_t min_post_divider; | |
288 | uint32_t max_post_divider; | |
289 | uint32_t min_ref_divider; | |
290 | uint32_t max_ref_divider; | |
291 | ||
292 | if (pll_settings->adjusted_pix_clk == 0) { | |
1296423b | 293 | DC_LOG_ERROR( |
4562236b HW |
294 | "%s Bad requested pixel clock", __func__); |
295 | return MAX_PLL_CALC_ERROR; | |
296 | } | |
297 | ||
298 | /* 1) Find Post divider ranges */ | |
299 | if (pll_settings->pix_clk_post_divider) { | |
300 | min_post_divider = pll_settings->pix_clk_post_divider; | |
301 | max_post_divider = pll_settings->pix_clk_post_divider; | |
302 | } else { | |
303 | min_post_divider = calc_pll_cs->min_pix_clock_pll_post_divider; | |
304 | if (min_post_divider * pll_settings->adjusted_pix_clk < | |
305 | calc_pll_cs->min_vco_khz) { | |
306 | min_post_divider = calc_pll_cs->min_vco_khz / | |
307 | pll_settings->adjusted_pix_clk; | |
308 | if ((min_post_divider * | |
309 | pll_settings->adjusted_pix_clk) < | |
310 | calc_pll_cs->min_vco_khz) | |
311 | min_post_divider++; | |
312 | } | |
313 | ||
314 | max_post_divider = calc_pll_cs->max_pix_clock_pll_post_divider; | |
315 | if (max_post_divider * pll_settings->adjusted_pix_clk | |
316 | > calc_pll_cs->max_vco_khz) | |
317 | max_post_divider = calc_pll_cs->max_vco_khz / | |
318 | pll_settings->adjusted_pix_clk; | |
319 | } | |
320 | ||
321 | /* 2) Find Reference divider ranges | |
322 | * When SS is enabled, or for Display Port even without SS, | |
323 | * pll_settings->referenceDivider is not zero. | |
324 | * So calculate PPLL FB and fractional FB divider | |
325 | * using the passed reference divider*/ | |
326 | ||
327 | if (pll_settings->reference_divider) { | |
328 | min_ref_divider = pll_settings->reference_divider; | |
329 | max_ref_divider = pll_settings->reference_divider; | |
330 | } else { | |
331 | min_ref_divider = ((calc_pll_cs->ref_freq_khz | |
332 | / calc_pll_cs->max_pll_input_freq_khz) | |
333 | > calc_pll_cs->min_pll_ref_divider) | |
334 | ? calc_pll_cs->ref_freq_khz | |
335 | / calc_pll_cs->max_pll_input_freq_khz | |
336 | : calc_pll_cs->min_pll_ref_divider; | |
337 | ||
338 | max_ref_divider = ((calc_pll_cs->ref_freq_khz | |
339 | / calc_pll_cs->min_pll_input_freq_khz) | |
340 | < calc_pll_cs->max_pll_ref_divider) | |
341 | ? calc_pll_cs->ref_freq_khz / | |
342 | calc_pll_cs->min_pll_input_freq_khz | |
343 | : calc_pll_cs->max_pll_ref_divider; | |
344 | } | |
345 | ||
346 | /* If some parameters are invalid we could have scenario when "min">"max" | |
347 | * which produced endless loop later. | |
348 | * We should investigate why we get the wrong parameters. | |
349 | * But to follow the similar logic when "adjustedPixelClock" is set to be 0 | |
350 | * it is better to return here than cause system hang/watchdog timeout later. | |
351 | * ## SVS Wed 15 Jul 2009 */ | |
352 | ||
353 | if (min_post_divider > max_post_divider) { | |
1296423b | 354 | DC_LOG_ERROR( |
4562236b HW |
355 | "%s Post divider range is invalid", __func__); |
356 | return MAX_PLL_CALC_ERROR; | |
357 | } | |
358 | ||
359 | if (min_ref_divider > max_ref_divider) { | |
1296423b | 360 | DC_LOG_ERROR( |
4562236b HW |
361 | "%s Reference divider range is invalid", __func__); |
362 | return MAX_PLL_CALC_ERROR; | |
363 | } | |
364 | ||
365 | /* 3) Try to find PLL dividers given ranges | |
366 | * starting with minimal error tolerance. | |
367 | * Increase error tolerance until PLL dividers found*/ | |
368 | err_tolerance = MAX_PLL_CALC_ERROR; | |
369 | ||
370 | while (!calc_pll_dividers_in_range( | |
371 | calc_pll_cs, | |
372 | pll_settings, | |
373 | min_ref_divider, | |
374 | max_ref_divider, | |
375 | min_post_divider, | |
376 | max_post_divider, | |
377 | err_tolerance)) | |
378 | err_tolerance += (err_tolerance > 10) | |
379 | ? (err_tolerance / 10) | |
380 | : 1; | |
381 | ||
382 | return err_tolerance; | |
383 | } | |
384 | ||
385 | static bool pll_adjust_pix_clk( | |
386 | struct dce110_clk_src *clk_src, | |
387 | struct pixel_clk_params *pix_clk_params, | |
388 | struct pll_settings *pll_settings) | |
389 | { | |
390 | uint32_t actual_pix_clk_khz = 0; | |
391 | uint32_t requested_clk_khz = 0; | |
392 | struct bp_adjust_pixel_clock_parameters bp_adjust_pixel_clock_params = { | |
393 | 0 }; | |
394 | enum bp_result bp_result; | |
4562236b HW |
395 | switch (pix_clk_params->signal_type) { |
396 | case SIGNAL_TYPE_HDMI_TYPE_A: { | |
397 | requested_clk_khz = pix_clk_params->requested_pix_clk; | |
cc4d99b8 CL |
398 | if (pix_clk_params->pixel_encoding != PIXEL_ENCODING_YCBCR422) { |
399 | switch (pix_clk_params->color_depth) { | |
400 | case COLOR_DEPTH_101010: | |
401 | requested_clk_khz = (requested_clk_khz * 5) >> 2; | |
402 | break; /* x1.25*/ | |
403 | case COLOR_DEPTH_121212: | |
404 | requested_clk_khz = (requested_clk_khz * 6) >> 2; | |
405 | break; /* x1.5*/ | |
406 | case COLOR_DEPTH_161616: | |
407 | requested_clk_khz = requested_clk_khz * 2; | |
408 | break; /* x2.0*/ | |
409 | default: | |
410 | break; | |
411 | } | |
4562236b | 412 | } |
4562236b HW |
413 | actual_pix_clk_khz = requested_clk_khz; |
414 | } | |
415 | break; | |
416 | ||
417 | case SIGNAL_TYPE_DISPLAY_PORT: | |
418 | case SIGNAL_TYPE_DISPLAY_PORT_MST: | |
419 | case SIGNAL_TYPE_EDP: | |
420 | requested_clk_khz = pix_clk_params->requested_sym_clk; | |
421 | actual_pix_clk_khz = pix_clk_params->requested_pix_clk; | |
422 | break; | |
423 | ||
424 | default: | |
425 | requested_clk_khz = pix_clk_params->requested_pix_clk; | |
426 | actual_pix_clk_khz = pix_clk_params->requested_pix_clk; | |
427 | break; | |
428 | } | |
429 | ||
430 | bp_adjust_pixel_clock_params.pixel_clock = requested_clk_khz; | |
431 | bp_adjust_pixel_clock_params. | |
432 | encoder_object_id = pix_clk_params->encoder_object_id; | |
433 | bp_adjust_pixel_clock_params.signal_type = pix_clk_params->signal_type; | |
434 | bp_adjust_pixel_clock_params. | |
435 | ss_enable = pix_clk_params->flags.ENABLE_SS; | |
436 | bp_result = clk_src->bios->funcs->adjust_pixel_clock( | |
437 | clk_src->bios, &bp_adjust_pixel_clock_params); | |
438 | if (bp_result == BP_RESULT_OK) { | |
439 | pll_settings->actual_pix_clk = actual_pix_clk_khz; | |
440 | pll_settings->adjusted_pix_clk = | |
441 | bp_adjust_pixel_clock_params.adjusted_pixel_clock; | |
442 | pll_settings->reference_divider = | |
443 | bp_adjust_pixel_clock_params.reference_divider; | |
444 | pll_settings->pix_clk_post_divider = | |
445 | bp_adjust_pixel_clock_params.pixel_clock_post_divider; | |
446 | ||
447 | return true; | |
448 | } | |
449 | ||
450 | return false; | |
451 | } | |
452 | ||
453 | /** | |
454 | * Calculate PLL Dividers for given Clock Value. | |
455 | * First will call VBIOS Adjust Exec table to check if requested Pixel clock | |
456 | * will be Adjusted based on usage. | |
457 | * Then it will calculate PLL Dividers for this Adjusted clock using preferred | |
458 | * method (Maximum VCO frequency). | |
459 | * | |
460 | * \return | |
461 | * Calculation error in units of 0.01% | |
462 | */ | |
463 | ||
464 | static uint32_t dce110_get_pix_clk_dividers_helper ( | |
465 | struct dce110_clk_src *clk_src, | |
466 | struct pll_settings *pll_settings, | |
467 | struct pixel_clk_params *pix_clk_params) | |
468 | { | |
4562236b HW |
469 | uint32_t field = 0; |
470 | uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; | |
5d4b05dd | 471 | DC_LOGGER_INIT(); |
4562236b HW |
472 | /* Check if reference clock is external (not pcie/xtalin) |
473 | * HW Dce80 spec: | |
474 | * 00 - PCIE_REFCLK, 01 - XTALIN, 02 - GENERICA, 03 - GENERICB | |
475 | * 04 - HSYNCA, 05 - GENLK_CLK, 06 - PCIE_REFCLK, 07 - DVOCLK0 */ | |
4562236b HW |
476 | REG_GET(PLL_CNTL, PLL_REF_DIV_SRC, &field); |
477 | pll_settings->use_external_clk = (field > 1); | |
478 | ||
479 | /* VBIOS by default enables DP SS (spread on IDCLK) for DCE 8.0 always | |
480 | * (we do not care any more from SI for some older DP Sink which | |
481 | * does not report SS support, no known issues) */ | |
482 | if ((pix_clk_params->flags.ENABLE_SS) || | |
483 | (dc_is_dp_signal(pix_clk_params->signal_type))) { | |
484 | ||
485 | const struct spread_spectrum_data *ss_data = get_ss_data_entry( | |
486 | clk_src, | |
487 | pix_clk_params->signal_type, | |
488 | pll_settings->adjusted_pix_clk); | |
489 | ||
490 | if (NULL != ss_data) | |
491 | pll_settings->ss_percentage = ss_data->percentage; | |
492 | } | |
493 | ||
494 | /* Check VBIOS AdjustPixelClock Exec table */ | |
495 | if (!pll_adjust_pix_clk(clk_src, pix_clk_params, pll_settings)) { | |
496 | /* Should never happen, ASSERT and fill up values to be able | |
497 | * to continue. */ | |
1296423b | 498 | DC_LOG_ERROR( |
4562236b HW |
499 | "%s: Failed to adjust pixel clock!!", __func__); |
500 | pll_settings->actual_pix_clk = | |
501 | pix_clk_params->requested_pix_clk; | |
502 | pll_settings->adjusted_pix_clk = | |
503 | pix_clk_params->requested_pix_clk; | |
504 | ||
505 | if (dc_is_dp_signal(pix_clk_params->signal_type)) | |
506 | pll_settings->adjusted_pix_clk = 100000; | |
507 | } | |
508 | ||
509 | /* Calculate Dividers */ | |
510 | if (pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) | |
511 | /*Calculate Dividers by HDMI object, no SS case or SS case */ | |
512 | pll_calc_error = | |
513 | calculate_pixel_clock_pll_dividers( | |
514 | &clk_src->calc_pll_hdmi, | |
515 | pll_settings); | |
516 | else | |
517 | /*Calculate Dividers by default object, no SS case or SS case */ | |
518 | pll_calc_error = | |
519 | calculate_pixel_clock_pll_dividers( | |
520 | &clk_src->calc_pll, | |
521 | pll_settings); | |
522 | ||
523 | return pll_calc_error; | |
524 | } | |
525 | ||
526 | static void dce112_get_pix_clk_dividers_helper ( | |
527 | struct dce110_clk_src *clk_src, | |
528 | struct pll_settings *pll_settings, | |
529 | struct pixel_clk_params *pix_clk_params) | |
530 | { | |
531 | uint32_t actualPixelClockInKHz; | |
532 | ||
533 | actualPixelClockInKHz = pix_clk_params->requested_pix_clk; | |
534 | /* Calculate Dividers */ | |
535 | if (pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) { | |
536 | switch (pix_clk_params->color_depth) { | |
537 | case COLOR_DEPTH_101010: | |
538 | actualPixelClockInKHz = (actualPixelClockInKHz * 5) >> 2; | |
539 | break; | |
540 | case COLOR_DEPTH_121212: | |
541 | actualPixelClockInKHz = (actualPixelClockInKHz * 6) >> 2; | |
542 | break; | |
543 | case COLOR_DEPTH_161616: | |
544 | actualPixelClockInKHz = actualPixelClockInKHz * 2; | |
545 | break; | |
546 | default: | |
547 | break; | |
548 | } | |
549 | } | |
550 | pll_settings->actual_pix_clk = actualPixelClockInKHz; | |
551 | pll_settings->adjusted_pix_clk = actualPixelClockInKHz; | |
552 | pll_settings->calculated_pix_clk = pix_clk_params->requested_pix_clk; | |
553 | } | |
554 | ||
555 | static uint32_t dce110_get_pix_clk_dividers( | |
556 | struct clock_source *cs, | |
557 | struct pixel_clk_params *pix_clk_params, | |
558 | struct pll_settings *pll_settings) | |
559 | { | |
560 | struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); | |
561 | uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; | |
5d4b05dd | 562 | DC_LOGGER_INIT(); |
4562236b HW |
563 | |
564 | if (pix_clk_params == NULL || pll_settings == NULL | |
565 | || pix_clk_params->requested_pix_clk == 0) { | |
1296423b | 566 | DC_LOG_ERROR( |
4562236b HW |
567 | "%s: Invalid parameters!!\n", __func__); |
568 | return pll_calc_error; | |
569 | } | |
570 | ||
571 | memset(pll_settings, 0, sizeof(*pll_settings)); | |
572 | ||
573 | if (cs->id == CLOCK_SOURCE_ID_DP_DTO || | |
574 | cs->id == CLOCK_SOURCE_ID_EXTERNAL) { | |
575 | pll_settings->adjusted_pix_clk = clk_src->ext_clk_khz; | |
576 | pll_settings->calculated_pix_clk = clk_src->ext_clk_khz; | |
577 | pll_settings->actual_pix_clk = | |
578 | pix_clk_params->requested_pix_clk; | |
579 | return 0; | |
580 | } | |
581 | ||
582 | switch (cs->ctx->dce_version) { | |
583 | case DCE_VERSION_8_0: | |
ebfdf0d0 AD |
584 | case DCE_VERSION_8_1: |
585 | case DCE_VERSION_8_3: | |
4562236b HW |
586 | case DCE_VERSION_10_0: |
587 | case DCE_VERSION_11_0: | |
588 | pll_calc_error = | |
589 | dce110_get_pix_clk_dividers_helper(clk_src, | |
590 | pll_settings, pix_clk_params); | |
591 | break; | |
592 | case DCE_VERSION_11_2: | |
0c75d5ac | 593 | case DCE_VERSION_11_22: |
2c8ad2d5 | 594 | case DCE_VERSION_12_0: |
ff5ef992 AD |
595 | #if defined(CONFIG_DRM_AMD_DC_DCN1_0) |
596 | case DCN_VERSION_1_0: | |
597 | #endif | |
3639fa68 | 598 | |
4562236b HW |
599 | dce112_get_pix_clk_dividers_helper(clk_src, |
600 | pll_settings, pix_clk_params); | |
601 | break; | |
602 | default: | |
603 | break; | |
604 | } | |
605 | ||
606 | return pll_calc_error; | |
607 | } | |
608 | ||
391e20d8 DD |
609 | static uint32_t dce110_get_pll_pixel_rate_in_hz( |
610 | struct clock_source *cs, | |
611 | struct pixel_clk_params *pix_clk_params, | |
612 | struct pll_settings *pll_settings) | |
613 | { | |
614 | uint32_t inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; | |
fb3466a4 | 615 | struct dc *dc_core = cs->ctx->dc; |
608ac7bb | 616 | struct dc_state *context = dc_core->current_state; |
391e20d8 DD |
617 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[inst]; |
618 | ||
619 | /* This function need separate to different DCE version, before separate, just use pixel clock */ | |
620 | return pipe_ctx->stream->phy_pix_clk; | |
503a7c6f | 621 | |
391e20d8 DD |
622 | } |
623 | ||
624 | static uint32_t dce110_get_dp_pixel_rate_from_combo_phy_pll( | |
625 | struct clock_source *cs, | |
626 | struct pixel_clk_params *pix_clk_params, | |
627 | struct pll_settings *pll_settings) | |
628 | { | |
629 | uint32_t inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; | |
fb3466a4 | 630 | struct dc *dc_core = cs->ctx->dc; |
608ac7bb | 631 | struct dc_state *context = dc_core->current_state; |
391e20d8 DD |
632 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[inst]; |
633 | ||
634 | /* This function need separate to different DCE version, before separate, just use pixel clock */ | |
635 | return pipe_ctx->stream->phy_pix_clk; | |
636 | } | |
637 | ||
638 | static uint32_t dce110_get_d_to_pixel_rate_in_hz( | |
639 | struct clock_source *cs, | |
640 | struct pixel_clk_params *pix_clk_params, | |
641 | struct pll_settings *pll_settings) | |
642 | { | |
643 | uint32_t inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; | |
644 | struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); | |
645 | int dto_enabled = 0; | |
646 | struct fixed31_32 pix_rate; | |
647 | ||
648 | REG_GET(PIXEL_RATE_CNTL[inst], DP_DTO0_ENABLE, &dto_enabled); | |
649 | ||
650 | if (dto_enabled) { | |
651 | uint32_t phase = 0; | |
652 | uint32_t modulo = 0; | |
653 | REG_GET(PHASE[inst], DP_DTO0_PHASE, &phase); | |
654 | REG_GET(MODULO[inst], DP_DTO0_MODULO, &modulo); | |
655 | ||
656 | if (modulo == 0) { | |
657 | return 0; | |
658 | } | |
659 | ||
eb0e5154 DL |
660 | pix_rate = dc_fixpt_from_int(clk_src->ref_freq_khz); |
661 | pix_rate = dc_fixpt_mul_int(pix_rate, 1000); | |
662 | pix_rate = dc_fixpt_mul_int(pix_rate, phase); | |
663 | pix_rate = dc_fixpt_div_int(pix_rate, modulo); | |
391e20d8 | 664 | |
eb0e5154 | 665 | return dc_fixpt_round(pix_rate); |
391e20d8 DD |
666 | } else { |
667 | return dce110_get_dp_pixel_rate_from_combo_phy_pll(cs, pix_clk_params, pll_settings); | |
668 | } | |
669 | } | |
670 | ||
671 | static uint32_t dce110_get_pix_rate_in_hz( | |
672 | struct clock_source *cs, | |
673 | struct pixel_clk_params *pix_clk_params, | |
674 | struct pll_settings *pll_settings) | |
675 | { | |
676 | uint32_t pix_rate = 0; | |
677 | switch (pix_clk_params->signal_type) { | |
678 | case SIGNAL_TYPE_DISPLAY_PORT: | |
679 | case SIGNAL_TYPE_DISPLAY_PORT_MST: | |
680 | case SIGNAL_TYPE_EDP: | |
681 | case SIGNAL_TYPE_VIRTUAL: | |
682 | pix_rate = dce110_get_d_to_pixel_rate_in_hz(cs, pix_clk_params, pll_settings); | |
683 | break; | |
684 | case SIGNAL_TYPE_HDMI_TYPE_A: | |
685 | default: | |
686 | pix_rate = dce110_get_pll_pixel_rate_in_hz(cs, pix_clk_params, pll_settings); | |
687 | break; | |
688 | } | |
689 | ||
690 | return pix_rate; | |
691 | } | |
692 | ||
4562236b HW |
693 | static bool disable_spread_spectrum(struct dce110_clk_src *clk_src) |
694 | { | |
695 | enum bp_result result; | |
696 | struct bp_spread_spectrum_parameters bp_ss_params = {0}; | |
697 | ||
698 | bp_ss_params.pll_id = clk_src->base.id; | |
699 | ||
700 | /*Call ASICControl to process ATOMBIOS Exec table*/ | |
701 | result = clk_src->bios->funcs->enable_spread_spectrum_on_ppll( | |
702 | clk_src->bios, | |
703 | &bp_ss_params, | |
704 | false); | |
705 | ||
706 | return result == BP_RESULT_OK; | |
707 | } | |
708 | ||
709 | static bool calculate_ss( | |
710 | const struct pll_settings *pll_settings, | |
711 | const struct spread_spectrum_data *ss_data, | |
712 | struct delta_sigma_data *ds_data) | |
713 | { | |
eb0e5154 DL |
714 | struct fixed31_32 fb_div; |
715 | struct fixed31_32 ss_amount; | |
716 | struct fixed31_32 ss_nslip_amount; | |
717 | struct fixed31_32 ss_ds_frac_amount; | |
718 | struct fixed31_32 ss_step_size; | |
719 | struct fixed31_32 modulation_time; | |
4562236b HW |
720 | |
721 | if (ds_data == NULL) | |
722 | return false; | |
723 | if (ss_data == NULL) | |
724 | return false; | |
725 | if (ss_data->percentage == 0) | |
726 | return false; | |
727 | if (pll_settings == NULL) | |
728 | return false; | |
729 | ||
730 | memset(ds_data, 0, sizeof(struct delta_sigma_data)); | |
731 | ||
732 | /* compute SS_AMOUNT_FBDIV & SS_AMOUNT_NFRAC_SLIP & SS_AMOUNT_DSFRAC*/ | |
733 | /* 6 decimal point support in fractional feedback divider */ | |
eb0e5154 | 734 | fb_div = dc_fixpt_from_fraction( |
4562236b | 735 | pll_settings->fract_feedback_divider, 1000000); |
eb0e5154 | 736 | fb_div = dc_fixpt_add_int(fb_div, pll_settings->feedback_divider); |
4562236b HW |
737 | |
738 | ds_data->ds_frac_amount = 0; | |
739 | /*spreadSpectrumPercentage is in the unit of .01%, | |
740 | * so have to divided by 100 * 100*/ | |
eb0e5154 DL |
741 | ss_amount = dc_fixpt_mul( |
742 | fb_div, dc_fixpt_from_fraction(ss_data->percentage, | |
4562236b | 743 | 100 * ss_data->percentage_divider)); |
eb0e5154 | 744 | ds_data->feedback_amount = dc_fixpt_floor(ss_amount); |
4562236b | 745 | |
eb0e5154 DL |
746 | ss_nslip_amount = dc_fixpt_sub(ss_amount, |
747 | dc_fixpt_from_int(ds_data->feedback_amount)); | |
748 | ss_nslip_amount = dc_fixpt_mul_int(ss_nslip_amount, 10); | |
749 | ds_data->nfrac_amount = dc_fixpt_floor(ss_nslip_amount); | |
4562236b | 750 | |
eb0e5154 DL |
751 | ss_ds_frac_amount = dc_fixpt_sub(ss_nslip_amount, |
752 | dc_fixpt_from_int(ds_data->nfrac_amount)); | |
753 | ss_ds_frac_amount = dc_fixpt_mul_int(ss_ds_frac_amount, 65536); | |
754 | ds_data->ds_frac_amount = dc_fixpt_floor(ss_ds_frac_amount); | |
4562236b HW |
755 | |
756 | /* compute SS_STEP_SIZE_DSFRAC */ | |
eb0e5154 | 757 | modulation_time = dc_fixpt_from_fraction( |
4562236b HW |
758 | pll_settings->reference_freq * 1000, |
759 | pll_settings->reference_divider * ss_data->modulation_freq_hz); | |
760 | ||
761 | if (ss_data->flags.CENTER_SPREAD) | |
eb0e5154 | 762 | modulation_time = dc_fixpt_div_int(modulation_time, 4); |
4562236b | 763 | else |
eb0e5154 | 764 | modulation_time = dc_fixpt_div_int(modulation_time, 2); |
4562236b | 765 | |
eb0e5154 | 766 | ss_step_size = dc_fixpt_div(ss_amount, modulation_time); |
4562236b | 767 | /* SS_STEP_SIZE_DSFRAC_DEC = Int(SS_STEP_SIZE * 2 ^ 16 * 10)*/ |
eb0e5154 DL |
768 | ss_step_size = dc_fixpt_mul_int(ss_step_size, 65536 * 10); |
769 | ds_data->ds_frac_size = dc_fixpt_floor(ss_step_size); | |
4562236b HW |
770 | |
771 | return true; | |
772 | } | |
773 | ||
774 | static bool enable_spread_spectrum( | |
775 | struct dce110_clk_src *clk_src, | |
776 | enum signal_type signal, struct pll_settings *pll_settings) | |
777 | { | |
778 | struct bp_spread_spectrum_parameters bp_params = {0}; | |
779 | struct delta_sigma_data d_s_data; | |
780 | const struct spread_spectrum_data *ss_data = NULL; | |
781 | ||
782 | ss_data = get_ss_data_entry( | |
783 | clk_src, | |
784 | signal, | |
785 | pll_settings->calculated_pix_clk); | |
786 | ||
787 | /* Pixel clock PLL has been programmed to generate desired pixel clock, | |
788 | * now enable SS on pixel clock */ | |
789 | /* TODO is it OK to return true not doing anything ??*/ | |
790 | if (ss_data != NULL && pll_settings->ss_percentage != 0) { | |
791 | if (calculate_ss(pll_settings, ss_data, &d_s_data)) { | |
792 | bp_params.ds.feedback_amount = | |
793 | d_s_data.feedback_amount; | |
794 | bp_params.ds.nfrac_amount = | |
795 | d_s_data.nfrac_amount; | |
796 | bp_params.ds.ds_frac_size = d_s_data.ds_frac_size; | |
797 | bp_params.ds_frac_amount = | |
798 | d_s_data.ds_frac_amount; | |
799 | bp_params.flags.DS_TYPE = 1; | |
800 | bp_params.pll_id = clk_src->base.id; | |
801 | bp_params.percentage = ss_data->percentage; | |
802 | if (ss_data->flags.CENTER_SPREAD) | |
803 | bp_params.flags.CENTER_SPREAD = 1; | |
804 | if (ss_data->flags.EXTERNAL_SS) | |
805 | bp_params.flags.EXTERNAL_SS = 1; | |
806 | ||
807 | if (BP_RESULT_OK != | |
808 | clk_src->bios->funcs-> | |
809 | enable_spread_spectrum_on_ppll( | |
810 | clk_src->bios, | |
811 | &bp_params, | |
812 | true)) | |
813 | return false; | |
814 | } else | |
815 | return false; | |
816 | } | |
817 | return true; | |
818 | } | |
819 | ||
820 | static void dce110_program_pixel_clk_resync( | |
821 | struct dce110_clk_src *clk_src, | |
822 | enum signal_type signal_type, | |
823 | enum dc_color_depth colordepth) | |
824 | { | |
4562236b HW |
825 | REG_UPDATE(RESYNC_CNTL, |
826 | DCCG_DEEP_COLOR_CNTL1, 0); | |
827 | /* | |
828 | 24 bit mode: TMDS clock = 1.0 x pixel clock (1:1) | |
829 | 30 bit mode: TMDS clock = 1.25 x pixel clock (5:4) | |
830 | 36 bit mode: TMDS clock = 1.5 x pixel clock (3:2) | |
831 | 48 bit mode: TMDS clock = 2 x pixel clock (2:1) | |
832 | */ | |
833 | if (signal_type != SIGNAL_TYPE_HDMI_TYPE_A) | |
834 | return; | |
835 | ||
836 | switch (colordepth) { | |
837 | case COLOR_DEPTH_888: | |
838 | REG_UPDATE(RESYNC_CNTL, | |
839 | DCCG_DEEP_COLOR_CNTL1, 0); | |
840 | break; | |
841 | case COLOR_DEPTH_101010: | |
842 | REG_UPDATE(RESYNC_CNTL, | |
843 | DCCG_DEEP_COLOR_CNTL1, 1); | |
844 | break; | |
845 | case COLOR_DEPTH_121212: | |
846 | REG_UPDATE(RESYNC_CNTL, | |
847 | DCCG_DEEP_COLOR_CNTL1, 2); | |
848 | break; | |
849 | case COLOR_DEPTH_161616: | |
850 | REG_UPDATE(RESYNC_CNTL, | |
851 | DCCG_DEEP_COLOR_CNTL1, 3); | |
852 | break; | |
853 | default: | |
854 | break; | |
855 | } | |
856 | } | |
857 | ||
858 | static void dce112_program_pixel_clk_resync( | |
859 | struct dce110_clk_src *clk_src, | |
860 | enum signal_type signal_type, | |
861 | enum dc_color_depth colordepth, | |
862 | bool enable_ycbcr420) | |
863 | { | |
ab7044d0 TC |
864 | uint32_t deep_color_cntl = 0; |
865 | uint32_t double_rate_enable = 0; | |
866 | ||
4562236b HW |
867 | /* |
868 | 24 bit mode: TMDS clock = 1.0 x pixel clock (1:1) | |
869 | 30 bit mode: TMDS clock = 1.25 x pixel clock (5:4) | |
870 | 36 bit mode: TMDS clock = 1.5 x pixel clock (3:2) | |
871 | 48 bit mode: TMDS clock = 2 x pixel clock (2:1) | |
872 | */ | |
ab7044d0 TC |
873 | if (signal_type == SIGNAL_TYPE_HDMI_TYPE_A) { |
874 | double_rate_enable = enable_ycbcr420 ? 1 : 0; | |
4562236b | 875 | |
ab7044d0 TC |
876 | switch (colordepth) { |
877 | case COLOR_DEPTH_888: | |
878 | deep_color_cntl = 0; | |
879 | break; | |
880 | case COLOR_DEPTH_101010: | |
881 | deep_color_cntl = 1; | |
882 | break; | |
883 | case COLOR_DEPTH_121212: | |
884 | deep_color_cntl = 2; | |
885 | break; | |
886 | case COLOR_DEPTH_161616: | |
887 | deep_color_cntl = 3; | |
888 | break; | |
889 | default: | |
890 | break; | |
891 | } | |
4562236b | 892 | } |
ab7044d0 TC |
893 | |
894 | if (clk_src->cs_mask->PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE) | |
895 | REG_UPDATE_2(PIXCLK_RESYNC_CNTL, | |
896 | PHYPLLA_DCCG_DEEP_COLOR_CNTL, deep_color_cntl, | |
897 | PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE, double_rate_enable); | |
898 | else | |
899 | REG_UPDATE(PIXCLK_RESYNC_CNTL, | |
900 | PHYPLLA_DCCG_DEEP_COLOR_CNTL, deep_color_cntl); | |
901 | ||
4562236b HW |
902 | } |
903 | ||
904 | static bool dce110_program_pix_clk( | |
ae799430 | 905 | struct clock_source *clock_source, |
4562236b HW |
906 | struct pixel_clk_params *pix_clk_params, |
907 | struct pll_settings *pll_settings) | |
908 | { | |
ae799430 | 909 | struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source); |
4562236b HW |
910 | struct bp_pixel_clock_parameters bp_pc_params = {0}; |
911 | ||
ff5ef992 AD |
912 | #if defined(CONFIG_DRM_AMD_DC_DCN1_0) |
913 | if (IS_FPGA_MAXIMUS_DC(clock_source->ctx->dce_environment)) { | |
914 | unsigned int inst = pix_clk_params->controller_id - CONTROLLER_ID_D0; | |
73535feb | 915 | unsigned dp_dto_ref_kHz = 700000; |
ff5ef992 AD |
916 | unsigned clock_kHz = pll_settings->actual_pix_clk; |
917 | ||
ff5ef992 AD |
918 | /* Set DTO values: phase = target clock, modulo = reference clock */ |
919 | REG_WRITE(PHASE[inst], clock_kHz); | |
920 | REG_WRITE(MODULO[inst], dp_dto_ref_kHz); | |
921 | ||
922 | /* Enable DTO */ | |
923 | REG_UPDATE(PIXEL_RATE_CNTL[inst], DP_DTO0_ENABLE, 1); | |
924 | return true; | |
925 | } | |
926 | #endif | |
4562236b HW |
927 | /* First disable SS |
928 | * ATOMBIOS will enable by default SS on PLL for DP, | |
929 | * do not disable it here | |
930 | */ | |
ae799430 | 931 | if (clock_source->id != CLOCK_SOURCE_ID_EXTERNAL && |
4562236b | 932 | !dc_is_dp_signal(pix_clk_params->signal_type) && |
ae799430 DL |
933 | clock_source->ctx->dce_version <= DCE_VERSION_11_0) |
934 | disable_spread_spectrum(clk_src); | |
4562236b HW |
935 | |
936 | /*ATOMBIOS expects pixel rate adjusted by deep color ratio)*/ | |
937 | bp_pc_params.controller_id = pix_clk_params->controller_id; | |
ae799430 | 938 | bp_pc_params.pll_id = clock_source->id; |
4562236b HW |
939 | bp_pc_params.target_pixel_clock = pll_settings->actual_pix_clk; |
940 | bp_pc_params.encoder_object_id = pix_clk_params->encoder_object_id; | |
941 | bp_pc_params.signal_type = pix_clk_params->signal_type; | |
942 | ||
ae799430 | 943 | switch (clock_source->ctx->dce_version) { |
4562236b | 944 | case DCE_VERSION_8_0: |
ebfdf0d0 AD |
945 | case DCE_VERSION_8_1: |
946 | case DCE_VERSION_8_3: | |
4562236b HW |
947 | case DCE_VERSION_10_0: |
948 | case DCE_VERSION_11_0: | |
949 | bp_pc_params.reference_divider = pll_settings->reference_divider; | |
950 | bp_pc_params.feedback_divider = pll_settings->feedback_divider; | |
951 | bp_pc_params.fractional_feedback_divider = | |
952 | pll_settings->fract_feedback_divider; | |
953 | bp_pc_params.pixel_clock_post_divider = | |
954 | pll_settings->pix_clk_post_divider; | |
955 | bp_pc_params.flags.SET_EXTERNAL_REF_DIV_SRC = | |
956 | pll_settings->use_external_clk; | |
957 | ||
ae799430 DL |
958 | if (clk_src->bios->funcs->set_pixel_clock( |
959 | clk_src->bios, &bp_pc_params) != BP_RESULT_OK) | |
4562236b HW |
960 | return false; |
961 | /* Enable SS | |
962 | * ATOMBIOS will enable by default SS for DP on PLL ( DP ID clock), | |
963 | * based on HW display PLL team, SS control settings should be programmed | |
964 | * during PLL Reset, but they do not have effect | |
965 | * until SS_EN is asserted.*/ | |
ae799430 | 966 | if (clock_source->id != CLOCK_SOURCE_ID_EXTERNAL |
77f36b27 ZF |
967 | && !dc_is_dp_signal(pix_clk_params->signal_type)) { |
968 | ||
969 | if (pix_clk_params->flags.ENABLE_SS) | |
970 | if (!enable_spread_spectrum(clk_src, | |
971 | pix_clk_params->signal_type, | |
972 | pll_settings)) | |
973 | return false; | |
974 | ||
975 | /* Resync deep color DTO */ | |
976 | dce110_program_pixel_clk_resync(clk_src, | |
977 | pix_clk_params->signal_type, | |
978 | pix_clk_params->color_depth); | |
4562236b | 979 | } |
27e947b0 | 980 | |
4562236b | 981 | break; |
ae799430 | 982 | case DCE_VERSION_11_2: |
0c75d5ac | 983 | case DCE_VERSION_11_22: |
2c8ad2d5 | 984 | case DCE_VERSION_12_0: |
ff5ef992 AD |
985 | #if defined(CONFIG_DRM_AMD_DC_DCN1_0) |
986 | case DCN_VERSION_1_0: | |
987 | #endif | |
3639fa68 | 988 | |
ae799430 DL |
989 | if (clock_source->id != CLOCK_SOURCE_ID_DP_DTO) { |
990 | bp_pc_params.flags.SET_GENLOCK_REF_DIV_SRC = | |
991 | pll_settings->use_external_clk; | |
992 | bp_pc_params.flags.SET_XTALIN_REF_SRC = | |
993 | !pll_settings->use_external_clk; | |
994 | if (pix_clk_params->flags.SUPPORT_YCBCR420) { | |
ae799430 DL |
995 | bp_pc_params.flags.SUPPORT_YUV_420 = 1; |
996 | } | |
997 | } | |
998 | if (clk_src->bios->funcs->set_pixel_clock( | |
999 | clk_src->bios, &bp_pc_params) != BP_RESULT_OK) | |
1000 | return false; | |
1001 | /* Resync deep color DTO */ | |
1002 | if (clock_source->id != CLOCK_SOURCE_ID_DP_DTO) | |
1003 | dce112_program_pixel_clk_resync(clk_src, | |
1004 | pix_clk_params->signal_type, | |
1005 | pix_clk_params->color_depth, | |
1006 | pix_clk_params->flags.SUPPORT_YCBCR420); | |
1007 | break; | |
4562236b HW |
1008 | default: |
1009 | break; | |
1010 | } | |
1011 | ||
1012 | return true; | |
1013 | } | |
1014 | ||
1015 | static bool dce110_clock_source_power_down( | |
1016 | struct clock_source *clk_src) | |
1017 | { | |
1018 | struct dce110_clk_src *dce110_clk_src = TO_DCE110_CLK_SRC(clk_src); | |
1019 | enum bp_result bp_result; | |
1020 | struct bp_pixel_clock_parameters bp_pixel_clock_params = {0}; | |
1021 | ||
1022 | if (clk_src->dp_clk_src) | |
1023 | return true; | |
1024 | ||
1025 | /* If Pixel Clock is 0 it means Power Down Pll*/ | |
1026 | bp_pixel_clock_params.controller_id = CONTROLLER_ID_UNDEFINED; | |
1027 | bp_pixel_clock_params.pll_id = clk_src->id; | |
1028 | bp_pixel_clock_params.flags.FORCE_PROGRAMMING_OF_PLL = 1; | |
1029 | ||
1030 | /*Call ASICControl to process ATOMBIOS Exec table*/ | |
1031 | bp_result = dce110_clk_src->bios->funcs->set_pixel_clock( | |
1032 | dce110_clk_src->bios, | |
1033 | &bp_pixel_clock_params); | |
1034 | ||
1035 | return bp_result == BP_RESULT_OK; | |
1036 | } | |
1037 | ||
1038 | /*****************************************/ | |
1039 | /* Constructor */ | |
1040 | /*****************************************/ | |
1041 | static const struct clock_source_funcs dce110_clk_src_funcs = { | |
1042 | .cs_power_down = dce110_clock_source_power_down, | |
1043 | .program_pix_clk = dce110_program_pix_clk, | |
391e20d8 DD |
1044 | .get_pix_clk_dividers = dce110_get_pix_clk_dividers, |
1045 | .get_pix_rate_in_hz = dce110_get_pix_rate_in_hz | |
4562236b HW |
1046 | }; |
1047 | ||
1048 | static void get_ss_info_from_atombios( | |
1049 | struct dce110_clk_src *clk_src, | |
1050 | enum as_signal_type as_signal, | |
1051 | struct spread_spectrum_data *spread_spectrum_data[], | |
1052 | uint32_t *ss_entries_num) | |
1053 | { | |
1054 | enum bp_result bp_result = BP_RESULT_FAILURE; | |
1055 | struct spread_spectrum_info *ss_info; | |
1056 | struct spread_spectrum_data *ss_data; | |
1057 | struct spread_spectrum_info *ss_info_cur; | |
1058 | struct spread_spectrum_data *ss_data_cur; | |
1059 | uint32_t i; | |
5d4b05dd | 1060 | DC_LOGGER_INIT(); |
4562236b | 1061 | if (ss_entries_num == NULL) { |
1296423b | 1062 | DC_LOG_SYNC( |
4562236b HW |
1063 | "Invalid entry !!!\n"); |
1064 | return; | |
1065 | } | |
1066 | if (spread_spectrum_data == NULL) { | |
1296423b | 1067 | DC_LOG_SYNC( |
4562236b HW |
1068 | "Invalid array pointer!!!\n"); |
1069 | return; | |
1070 | } | |
1071 | ||
1072 | spread_spectrum_data[0] = NULL; | |
1073 | *ss_entries_num = 0; | |
1074 | ||
1075 | *ss_entries_num = clk_src->bios->funcs->get_ss_entry_number( | |
1076 | clk_src->bios, | |
1077 | as_signal); | |
1078 | ||
1079 | if (*ss_entries_num == 0) | |
1080 | return; | |
1081 | ||
6396bb22 KC |
1082 | ss_info = kcalloc(*ss_entries_num, |
1083 | sizeof(struct spread_spectrum_info), | |
2004f45e | 1084 | GFP_KERNEL); |
4562236b HW |
1085 | ss_info_cur = ss_info; |
1086 | if (ss_info == NULL) | |
1087 | return; | |
1088 | ||
6396bb22 KC |
1089 | ss_data = kcalloc(*ss_entries_num, |
1090 | sizeof(struct spread_spectrum_data), | |
2004f45e | 1091 | GFP_KERNEL); |
4562236b HW |
1092 | if (ss_data == NULL) |
1093 | goto out_free_info; | |
1094 | ||
1095 | for (i = 0, ss_info_cur = ss_info; | |
1096 | i < (*ss_entries_num); | |
1097 | ++i, ++ss_info_cur) { | |
1098 | ||
1099 | bp_result = clk_src->bios->funcs->get_spread_spectrum_info( | |
1100 | clk_src->bios, | |
1101 | as_signal, | |
1102 | i, | |
1103 | ss_info_cur); | |
1104 | ||
1105 | if (bp_result != BP_RESULT_OK) | |
1106 | goto out_free_data; | |
1107 | } | |
1108 | ||
1109 | for (i = 0, ss_info_cur = ss_info, ss_data_cur = ss_data; | |
1110 | i < (*ss_entries_num); | |
1111 | ++i, ++ss_info_cur, ++ss_data_cur) { | |
1112 | ||
1113 | if (ss_info_cur->type.STEP_AND_DELAY_INFO != false) { | |
1296423b | 1114 | DC_LOG_SYNC( |
4562236b HW |
1115 | "Invalid ATOMBIOS SS Table!!!\n"); |
1116 | goto out_free_data; | |
1117 | } | |
1118 | ||
1119 | /* for HDMI check SS percentage, | |
1120 | * if it is > 6 (0.06%), the ATOMBIOS table info is invalid*/ | |
1121 | if (as_signal == AS_SIGNAL_TYPE_HDMI | |
1122 | && ss_info_cur->spread_spectrum_percentage > 6){ | |
1123 | /* invalid input, do nothing */ | |
1296423b | 1124 | DC_LOG_SYNC( |
4562236b | 1125 | "Invalid SS percentage "); |
1296423b | 1126 | DC_LOG_SYNC( |
4562236b HW |
1127 | "for HDMI in ATOMBIOS info Table!!!\n"); |
1128 | continue; | |
1129 | } | |
1130 | if (ss_info_cur->spread_percentage_divider == 1000) { | |
1131 | /* Keep previous precision from ATOMBIOS for these | |
1132 | * in case new precision set by ATOMBIOS for these | |
1133 | * (otherwise all code in DCE specific classes | |
1134 | * for all previous ASICs would need | |
1135 | * to be updated for SS calculations, | |
1136 | * Audio SS compensation and DP DTO SS compensation | |
1137 | * which assumes fixed SS percentage Divider = 100)*/ | |
1138 | ss_info_cur->spread_spectrum_percentage /= 10; | |
1139 | ss_info_cur->spread_percentage_divider = 100; | |
1140 | } | |
1141 | ||
1142 | ss_data_cur->freq_range_khz = ss_info_cur->target_clock_range; | |
1143 | ss_data_cur->percentage = | |
1144 | ss_info_cur->spread_spectrum_percentage; | |
1145 | ss_data_cur->percentage_divider = | |
1146 | ss_info_cur->spread_percentage_divider; | |
1147 | ss_data_cur->modulation_freq_hz = | |
1148 | ss_info_cur->spread_spectrum_range; | |
1149 | ||
1150 | if (ss_info_cur->type.CENTER_MODE) | |
1151 | ss_data_cur->flags.CENTER_SPREAD = 1; | |
1152 | ||
1153 | if (ss_info_cur->type.EXTERNAL) | |
1154 | ss_data_cur->flags.EXTERNAL_SS = 1; | |
1155 | ||
1156 | } | |
1157 | ||
1158 | *spread_spectrum_data = ss_data; | |
2004f45e | 1159 | kfree(ss_info); |
4562236b HW |
1160 | return; |
1161 | ||
1162 | out_free_data: | |
2004f45e | 1163 | kfree(ss_data); |
4562236b HW |
1164 | *ss_entries_num = 0; |
1165 | out_free_info: | |
2004f45e | 1166 | kfree(ss_info); |
4562236b HW |
1167 | } |
1168 | ||
1169 | static void ss_info_from_atombios_create( | |
1170 | struct dce110_clk_src *clk_src) | |
1171 | { | |
1172 | get_ss_info_from_atombios( | |
1173 | clk_src, | |
1174 | AS_SIGNAL_TYPE_DISPLAY_PORT, | |
1175 | &clk_src->dp_ss_params, | |
1176 | &clk_src->dp_ss_params_cnt); | |
1177 | get_ss_info_from_atombios( | |
1178 | clk_src, | |
1179 | AS_SIGNAL_TYPE_HDMI, | |
1180 | &clk_src->hdmi_ss_params, | |
1181 | &clk_src->hdmi_ss_params_cnt); | |
1182 | get_ss_info_from_atombios( | |
1183 | clk_src, | |
1184 | AS_SIGNAL_TYPE_DVI, | |
1185 | &clk_src->dvi_ss_params, | |
1186 | &clk_src->dvi_ss_params_cnt); | |
1187 | } | |
1188 | ||
1189 | static bool calc_pll_max_vco_construct( | |
1190 | struct calc_pll_clock_source *calc_pll_cs, | |
1191 | struct calc_pll_clock_source_init_data *init_data) | |
1192 | { | |
1193 | uint32_t i; | |
1515a47b | 1194 | struct dc_firmware_info fw_info = { { 0 } }; |
4562236b HW |
1195 | if (calc_pll_cs == NULL || |
1196 | init_data == NULL || | |
1197 | init_data->bp == NULL) | |
1198 | return false; | |
1199 | ||
1200 | if (init_data->bp->funcs->get_firmware_info( | |
1201 | init_data->bp, | |
1202 | &fw_info) != BP_RESULT_OK) | |
1203 | return false; | |
1204 | ||
1205 | calc_pll_cs->ctx = init_data->ctx; | |
1206 | calc_pll_cs->ref_freq_khz = fw_info.pll_info.crystal_frequency; | |
1207 | calc_pll_cs->min_vco_khz = | |
1208 | fw_info.pll_info.min_output_pxl_clk_pll_frequency; | |
1209 | calc_pll_cs->max_vco_khz = | |
1210 | fw_info.pll_info.max_output_pxl_clk_pll_frequency; | |
1211 | ||
1212 | if (init_data->max_override_input_pxl_clk_pll_freq_khz != 0) | |
1213 | calc_pll_cs->max_pll_input_freq_khz = | |
1214 | init_data->max_override_input_pxl_clk_pll_freq_khz; | |
1215 | else | |
1216 | calc_pll_cs->max_pll_input_freq_khz = | |
1217 | fw_info.pll_info.max_input_pxl_clk_pll_frequency; | |
1218 | ||
1219 | if (init_data->min_override_input_pxl_clk_pll_freq_khz != 0) | |
1220 | calc_pll_cs->min_pll_input_freq_khz = | |
1221 | init_data->min_override_input_pxl_clk_pll_freq_khz; | |
1222 | else | |
1223 | calc_pll_cs->min_pll_input_freq_khz = | |
1224 | fw_info.pll_info.min_input_pxl_clk_pll_frequency; | |
1225 | ||
1226 | calc_pll_cs->min_pix_clock_pll_post_divider = | |
1227 | init_data->min_pix_clk_pll_post_divider; | |
1228 | calc_pll_cs->max_pix_clock_pll_post_divider = | |
1229 | init_data->max_pix_clk_pll_post_divider; | |
1230 | calc_pll_cs->min_pll_ref_divider = | |
1231 | init_data->min_pll_ref_divider; | |
1232 | calc_pll_cs->max_pll_ref_divider = | |
1233 | init_data->max_pll_ref_divider; | |
1234 | ||
1235 | if (init_data->num_fract_fb_divider_decimal_point == 0 || | |
1236 | init_data->num_fract_fb_divider_decimal_point_precision > | |
1237 | init_data->num_fract_fb_divider_decimal_point) { | |
1296423b | 1238 | DC_LOG_ERROR( |
4562236b HW |
1239 | "The dec point num or precision is incorrect!"); |
1240 | return false; | |
1241 | } | |
1242 | if (init_data->num_fract_fb_divider_decimal_point_precision == 0) { | |
1296423b | 1243 | DC_LOG_ERROR( |
4562236b HW |
1244 | "Incorrect fract feedback divider precision num!"); |
1245 | return false; | |
1246 | } | |
1247 | ||
1248 | calc_pll_cs->fract_fb_divider_decimal_points_num = | |
1249 | init_data->num_fract_fb_divider_decimal_point; | |
1250 | calc_pll_cs->fract_fb_divider_precision = | |
1251 | init_data->num_fract_fb_divider_decimal_point_precision; | |
1252 | calc_pll_cs->fract_fb_divider_factor = 1; | |
1253 | for (i = 0; i < calc_pll_cs->fract_fb_divider_decimal_points_num; ++i) | |
1254 | calc_pll_cs->fract_fb_divider_factor *= 10; | |
1255 | ||
1256 | calc_pll_cs->fract_fb_divider_precision_factor = 1; | |
1257 | for ( | |
1258 | i = 0; | |
1259 | i < (calc_pll_cs->fract_fb_divider_decimal_points_num - | |
1260 | calc_pll_cs->fract_fb_divider_precision); | |
1261 | ++i) | |
1262 | calc_pll_cs->fract_fb_divider_precision_factor *= 10; | |
1263 | ||
1264 | return true; | |
1265 | } | |
1266 | ||
1267 | bool dce110_clk_src_construct( | |
1268 | struct dce110_clk_src *clk_src, | |
1269 | struct dc_context *ctx, | |
1270 | struct dc_bios *bios, | |
1271 | enum clock_source_id id, | |
1272 | const struct dce110_clk_src_regs *regs, | |
1273 | const struct dce110_clk_src_shift *cs_shift, | |
1274 | const struct dce110_clk_src_mask *cs_mask) | |
1275 | { | |
1515a47b | 1276 | struct dc_firmware_info fw_info = { { 0 } }; |
4562236b HW |
1277 | struct calc_pll_clock_source_init_data calc_pll_cs_init_data_hdmi; |
1278 | struct calc_pll_clock_source_init_data calc_pll_cs_init_data; | |
1279 | ||
1280 | clk_src->base.ctx = ctx; | |
1281 | clk_src->bios = bios; | |
1282 | clk_src->base.id = id; | |
1283 | clk_src->base.funcs = &dce110_clk_src_funcs; | |
1284 | ||
1285 | clk_src->regs = regs; | |
1286 | clk_src->cs_shift = cs_shift; | |
1287 | clk_src->cs_mask = cs_mask; | |
1288 | ||
1289 | if (clk_src->bios->funcs->get_firmware_info( | |
1290 | clk_src->bios, &fw_info) != BP_RESULT_OK) { | |
1291 | ASSERT_CRITICAL(false); | |
1292 | goto unexpected_failure; | |
1293 | } | |
1294 | ||
1295 | clk_src->ext_clk_khz = | |
1296 | fw_info.external_clock_source_frequency_for_dp; | |
1297 | ||
1298 | switch (clk_src->base.ctx->dce_version) { | |
1299 | case DCE_VERSION_8_0: | |
ebfdf0d0 AD |
1300 | case DCE_VERSION_8_1: |
1301 | case DCE_VERSION_8_3: | |
4562236b HW |
1302 | case DCE_VERSION_10_0: |
1303 | case DCE_VERSION_11_0: | |
1304 | ||
1305 | /* structure normally used with PLL ranges from ATOMBIOS; DS on by default */ | |
1306 | calc_pll_cs_init_data.bp = bios; | |
1307 | calc_pll_cs_init_data.min_pix_clk_pll_post_divider = 1; | |
1308 | calc_pll_cs_init_data.max_pix_clk_pll_post_divider = | |
1309 | clk_src->cs_mask->PLL_POST_DIV_PIXCLK; | |
1310 | calc_pll_cs_init_data.min_pll_ref_divider = 1; | |
1311 | calc_pll_cs_init_data.max_pll_ref_divider = clk_src->cs_mask->PLL_REF_DIV; | |
1312 | /* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ | |
1313 | calc_pll_cs_init_data.min_override_input_pxl_clk_pll_freq_khz = 0; | |
1314 | /* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ | |
1315 | calc_pll_cs_init_data.max_override_input_pxl_clk_pll_freq_khz = 0; | |
1316 | /*numberOfFractFBDividerDecimalPoints*/ | |
1317 | calc_pll_cs_init_data.num_fract_fb_divider_decimal_point = | |
1318 | FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; | |
1319 | /*number of decimal point to round off for fractional feedback divider value*/ | |
1320 | calc_pll_cs_init_data.num_fract_fb_divider_decimal_point_precision = | |
1321 | FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; | |
1322 | calc_pll_cs_init_data.ctx = ctx; | |
1323 | ||
1324 | /*structure for HDMI, no SS or SS% <= 0.06% for 27 MHz Ref clock */ | |
1325 | calc_pll_cs_init_data_hdmi.bp = bios; | |
1326 | calc_pll_cs_init_data_hdmi.min_pix_clk_pll_post_divider = 1; | |
1327 | calc_pll_cs_init_data_hdmi.max_pix_clk_pll_post_divider = | |
1328 | clk_src->cs_mask->PLL_POST_DIV_PIXCLK; | |
1329 | calc_pll_cs_init_data_hdmi.min_pll_ref_divider = 1; | |
1330 | calc_pll_cs_init_data_hdmi.max_pll_ref_divider = clk_src->cs_mask->PLL_REF_DIV; | |
1331 | /* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ | |
1332 | calc_pll_cs_init_data_hdmi.min_override_input_pxl_clk_pll_freq_khz = 13500; | |
1333 | /* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ | |
1334 | calc_pll_cs_init_data_hdmi.max_override_input_pxl_clk_pll_freq_khz = 27000; | |
1335 | /*numberOfFractFBDividerDecimalPoints*/ | |
1336 | calc_pll_cs_init_data_hdmi.num_fract_fb_divider_decimal_point = | |
1337 | FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; | |
1338 | /*number of decimal point to round off for fractional feedback divider value*/ | |
1339 | calc_pll_cs_init_data_hdmi.num_fract_fb_divider_decimal_point_precision = | |
1340 | FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM; | |
1341 | calc_pll_cs_init_data_hdmi.ctx = ctx; | |
1342 | ||
1343 | clk_src->ref_freq_khz = fw_info.pll_info.crystal_frequency; | |
1344 | ||
1345 | if (clk_src->base.id == CLOCK_SOURCE_ID_EXTERNAL) | |
1346 | return true; | |
1347 | ||
1348 | /* PLL only from here on */ | |
1349 | ss_info_from_atombios_create(clk_src); | |
1350 | ||
1351 | if (!calc_pll_max_vco_construct( | |
1352 | &clk_src->calc_pll, | |
1353 | &calc_pll_cs_init_data)) { | |
1354 | ASSERT_CRITICAL(false); | |
1355 | goto unexpected_failure; | |
1356 | } | |
1357 | ||
e8c963d6 CL |
1358 | |
1359 | calc_pll_cs_init_data_hdmi. | |
1360 | min_override_input_pxl_clk_pll_freq_khz = clk_src->ref_freq_khz/2; | |
1361 | calc_pll_cs_init_data_hdmi. | |
1362 | max_override_input_pxl_clk_pll_freq_khz = clk_src->ref_freq_khz; | |
1363 | ||
4562236b HW |
1364 | |
1365 | if (!calc_pll_max_vco_construct( | |
1366 | &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { | |
1367 | ASSERT_CRITICAL(false); | |
1368 | goto unexpected_failure; | |
1369 | } | |
1370 | break; | |
1371 | default: | |
1372 | break; | |
1373 | } | |
1374 | ||
1375 | return true; | |
1376 | ||
1377 | unexpected_failure: | |
1378 | return false; | |
1379 | } | |
1380 |