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