diff mbox

[14/29] drm/amd/dal: Add clock source HW programming

Message ID 1455211209-26733-15-git-send-email-harry.wentland@amd.com (mailing list archive)
State New, archived
Headers show

Commit Message

Harry Wentland Feb. 11, 2016, 5:19 p.m. UTC
Adds pixel clock programming and functionality to
power down clock sources.

Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
---
 .../drm/amd/dal/dc/dce110/dce110_clock_source.c    | 1162 ++++++++++++++++++++
 .../drm/amd/dal/dc/dce110/dce110_clock_source.h    |   64 ++
 2 files changed, 1226 insertions(+)
 create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.h
diff mbox

Patch

diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.c
new file mode 100644
index 000000000000..e1bac1f77b79
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.c
@@ -0,0 +1,1162 @@ 
+/*
+ * Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include "dm_services.h"
+
+/* include DCE11 register header files */
+#include "dce/dce_11_0_d.h"
+#include "dce/dce_11_0_sh_mask.h"
+
+#include "dc_types.h"
+#include "core_types.h"
+
+#include "include/grph_object_id.h"
+#include "include/logger_interface.h"
+
+#include "dce110_clock_source.h"
+
+#define FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM 6
+#define CALC_PLL_CLK_SRC_ERR_TOLERANCE 1
+#define MAX_PLL_CALC_ERROR 0xFFFFFFFF
+
+static const struct spread_spectrum_data *get_ss_data_entry(
+		struct dce110_clk_src *clk_src,
+		enum signal_type signal,
+		uint32_t pix_clk_khz)
+{
+
+	uint32_t entrys_num;
+	uint32_t i;
+	struct spread_spectrum_data *ss_parm = NULL;
+	struct spread_spectrum_data *ret = NULL;
+
+	switch (signal) {
+	case SIGNAL_TYPE_DVI_SINGLE_LINK:
+	case SIGNAL_TYPE_DVI_DUAL_LINK:
+		ss_parm = clk_src->dvi_ss_params;
+		entrys_num = clk_src->dvi_ss_params_cnt;
+		break;
+
+	case SIGNAL_TYPE_HDMI_TYPE_A:
+		ss_parm = clk_src->hdmi_ss_params;
+		entrys_num = clk_src->hdmi_ss_params_cnt;
+		break;
+
+	case SIGNAL_TYPE_DISPLAY_PORT:
+	case SIGNAL_TYPE_DISPLAY_PORT_MST:
+	case SIGNAL_TYPE_EDP:
+		ss_parm = clk_src->dp_ss_params;
+		entrys_num = clk_src->dp_ss_params_cnt;
+		break;
+
+	default:
+		ss_parm = NULL;
+		entrys_num = 0;
+		break;
+	}
+
+	if (ss_parm == NULL)
+		return ret;
+
+	for (i = 0; i < entrys_num; ++i, ++ss_parm) {
+		if (ss_parm->freq_range_khz >= pix_clk_khz) {
+			ret = ss_parm;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/**
+* Function: calculate_fb_and_fractional_fb_divider
+*
+* * DESCRIPTION: Calculates feedback and fractional feedback dividers values
+*
+*PARAMETERS:
+* targetPixelClock             Desired frequency in 10 KHz
+* ref_divider                  Reference divider (already known)
+* postDivider                  Post Divider (already known)
+* feedback_divider_param       Pointer where to store
+*					calculated feedback divider value
+* fract_feedback_divider_param Pointer where to store
+*					calculated fract feedback divider value
+*
+*RETURNS:
+* It fills the locations pointed by feedback_divider_param
+*					and fract_feedback_divider_param
+* It returns	- true if feedback divider not 0
+*		- false should never happen)
+*/
+static bool calculate_fb_and_fractional_fb_divider(
+		struct calc_pll_clock_source *calc_pll_cs,
+		uint32_t target_pix_clk_khz,
+		uint32_t ref_divider,
+		uint32_t post_divider,
+		uint32_t *feedback_divider_param,
+		uint32_t *fract_feedback_divider_param)
+{
+	uint64_t feedback_divider;
+
+	feedback_divider =
+		(uint64_t)(target_pix_clk_khz * ref_divider * post_divider);
+	feedback_divider *= 10;
+	/* additional factor, since we divide by 10 afterwards */
+	feedback_divider *= (uint64_t)(calc_pll_cs->fract_fb_divider_factor);
+	feedback_divider = div_u64(feedback_divider, calc_pll_cs->ref_freq_khz);
+
+/*Round to the number of precision
+ * The following code replace the old code (ullfeedbackDivider + 5)/10
+ * for example if the difference between the number
+ * of fractional feedback decimal point and the fractional FB Divider precision
+ * is 2 then the equation becomes (ullfeedbackDivider + 5*100) / (10*100))*/
+
+	feedback_divider += (uint64_t)
+			(5 * calc_pll_cs->fract_fb_divider_precision_factor);
+	feedback_divider =
+		div_u64(feedback_divider,
+			calc_pll_cs->fract_fb_divider_precision_factor * 10);
+	feedback_divider *= (uint64_t)
+			(calc_pll_cs->fract_fb_divider_precision_factor);
+
+	*feedback_divider_param =
+		div_u64_rem(
+			feedback_divider,
+			calc_pll_cs->fract_fb_divider_factor,
+			fract_feedback_divider_param);
+
+	if (*feedback_divider_param != 0)
+		return true;
+	return false;
+}
+
+/**
+*calc_fb_divider_checking_tolerance
+*
+*DESCRIPTION: Calculates Feedback and Fractional Feedback divider values
+*		for passed Reference and Post divider, checking for tolerance.
+*PARAMETERS:
+* pll_settings		Pointer to structure
+* ref_divider		Reference divider (already known)
+* postDivider		Post Divider (already known)
+* tolerance		Tolerance for Calculated Pixel Clock to be within
+*
+*RETURNS:
+* It fills the PLLSettings structure with PLL Dividers values
+* if calculated values are within required tolerance
+* It returns	- true if eror is within tolerance
+*		- false if eror is not within tolerance
+*/
+static bool calc_fb_divider_checking_tolerance(
+		struct calc_pll_clock_source *calc_pll_cs,
+		struct pll_settings *pll_settings,
+		uint32_t ref_divider,
+		uint32_t post_divider,
+		uint32_t tolerance)
+{
+	uint32_t feedback_divider;
+	uint32_t fract_feedback_divider;
+	uint32_t actual_calculated_clock_khz;
+	uint32_t abs_err;
+	uint64_t actual_calc_clk_khz;
+
+	calculate_fb_and_fractional_fb_divider(
+			calc_pll_cs,
+			pll_settings->adjusted_pix_clk,
+			ref_divider,
+			post_divider,
+			&feedback_divider,
+			&fract_feedback_divider);
+
+	/*Actual calculated value*/
+	actual_calc_clk_khz = (uint64_t)(feedback_divider *
+					calc_pll_cs->fract_fb_divider_factor) +
+							fract_feedback_divider;
+	actual_calc_clk_khz *= calc_pll_cs->ref_freq_khz;
+	actual_calc_clk_khz =
+		div_u64(actual_calc_clk_khz,
+			ref_divider * post_divider *
+				calc_pll_cs->fract_fb_divider_factor);
+
+	actual_calculated_clock_khz = (uint32_t)(actual_calc_clk_khz);
+
+	abs_err = (actual_calculated_clock_khz >
+					pll_settings->adjusted_pix_clk)
+			? actual_calculated_clock_khz -
+					pll_settings->adjusted_pix_clk
+			: pll_settings->adjusted_pix_clk -
+						actual_calculated_clock_khz;
+
+	if (abs_err <= tolerance) {
+		/*found good values*/
+		pll_settings->reference_freq = calc_pll_cs->ref_freq_khz;
+		pll_settings->reference_divider = ref_divider;
+		pll_settings->feedback_divider = feedback_divider;
+		pll_settings->fract_feedback_divider = fract_feedback_divider;
+		pll_settings->pix_clk_post_divider = post_divider;
+		pll_settings->calculated_pix_clk =
+			actual_calculated_clock_khz;
+		pll_settings->vco_freq =
+			actual_calculated_clock_khz * post_divider;
+		return true;
+	}
+	return false;
+}
+
+
+static bool calc_pll_dividers_in_range(
+		struct calc_pll_clock_source *calc_pll_cs,
+		struct pll_settings *pll_settings,
+		uint32_t min_ref_divider,
+		uint32_t max_ref_divider,
+		uint32_t min_post_divider,
+		uint32_t max_post_divider,
+		uint32_t err_tolerance)
+{
+	uint32_t ref_divider;
+	uint32_t post_divider;
+	uint32_t tolerance;
+
+/* This is err_tolerance / 10000 = 0.0025 - acceptable error of 0.25%
+ * This is errorTolerance / 10000 = 0.0001 - acceptable error of 0.01%*/
+	tolerance = (pll_settings->adjusted_pix_clk * err_tolerance) /
+									10000;
+	if (tolerance < CALC_PLL_CLK_SRC_ERR_TOLERANCE)
+		tolerance = CALC_PLL_CLK_SRC_ERR_TOLERANCE;
+
+	for (
+			post_divider = max_post_divider;
+			post_divider >= min_post_divider;
+			--post_divider) {
+		for (
+				ref_divider = min_ref_divider;
+				ref_divider <= max_ref_divider;
+				++ref_divider) {
+			if (calc_fb_divider_checking_tolerance(
+					calc_pll_cs,
+					pll_settings,
+					ref_divider,
+					post_divider,
+					tolerance)) {
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+static uint32_t calculate_pixel_clock_pll_dividers(
+		struct calc_pll_clock_source *calc_pll_cs,
+		struct pll_settings *pll_settings)
+{
+	uint32_t err_tolerance;
+	uint32_t min_post_divider;
+	uint32_t max_post_divider;
+	uint32_t min_ref_divider;
+	uint32_t max_ref_divider;
+
+	if (pll_settings->adjusted_pix_clk == 0) {
+		dal_logger_write(calc_pll_cs->ctx->logger,
+			LOG_MAJOR_ERROR,
+			LOG_MINOR_COMPONENT_GPU,
+			"%s Bad requested pixel clock", __func__);
+		return MAX_PLL_CALC_ERROR;
+	}
+
+/* 1) Find Post divider ranges */
+	if (pll_settings->pix_clk_post_divider) {
+		min_post_divider = pll_settings->pix_clk_post_divider;
+		max_post_divider = pll_settings->pix_clk_post_divider;
+	} else {
+		min_post_divider = calc_pll_cs->min_pix_clock_pll_post_divider;
+		if (min_post_divider * pll_settings->adjusted_pix_clk <
+						calc_pll_cs->min_vco_khz) {
+			min_post_divider = calc_pll_cs->min_vco_khz /
+					pll_settings->adjusted_pix_clk;
+			if ((min_post_divider *
+					pll_settings->adjusted_pix_clk) <
+						calc_pll_cs->min_vco_khz)
+				min_post_divider++;
+		}
+
+		max_post_divider = calc_pll_cs->max_pix_clock_pll_post_divider;
+		if (max_post_divider * pll_settings->adjusted_pix_clk
+				> calc_pll_cs->max_vco_khz)
+			max_post_divider = calc_pll_cs->max_vco_khz /
+					pll_settings->adjusted_pix_clk;
+	}
+
+/* 2) Find Reference divider ranges
+ * When SS is enabled, or for Display Port even without SS,
+ * pll_settings->referenceDivider is not zero.
+ * So calculate PPLL FB and fractional FB divider
+ * using the passed reference divider*/
+
+	if (pll_settings->reference_divider) {
+		min_ref_divider = pll_settings->reference_divider;
+		max_ref_divider = pll_settings->reference_divider;
+	} else {
+		min_ref_divider = ((calc_pll_cs->ref_freq_khz
+				/ calc_pll_cs->max_pll_input_freq_khz)
+				> calc_pll_cs->min_pll_ref_divider)
+			? calc_pll_cs->ref_freq_khz
+					/ calc_pll_cs->max_pll_input_freq_khz
+			: calc_pll_cs->min_pll_ref_divider;
+
+		max_ref_divider = ((calc_pll_cs->ref_freq_khz
+				/ calc_pll_cs->min_pll_input_freq_khz)
+				< calc_pll_cs->max_pll_ref_divider)
+			? calc_pll_cs->ref_freq_khz /
+					calc_pll_cs->min_pll_input_freq_khz
+			: calc_pll_cs->max_pll_ref_divider;
+	}
+
+/* If some parameters are invalid we could have scenario when  "min">"max"
+ * which produced endless loop later.
+ * We should investigate why we get the wrong parameters.
+ * But to follow the similar logic when "adjustedPixelClock" is set to be 0
+ * it is better to return here than cause system hang/watchdog timeout later.
+ *  ## SVS Wed 15 Jul 2009 */
+
+	if (min_post_divider > max_post_divider) {
+		dal_logger_write(calc_pll_cs->ctx->logger,
+			LOG_MAJOR_ERROR,
+			LOG_MINOR_COMPONENT_GPU,
+			"%s Post divider range is invalid", __func__);
+		return MAX_PLL_CALC_ERROR;
+	}
+
+	if (min_ref_divider > max_ref_divider) {
+		dal_logger_write(calc_pll_cs->ctx->logger,
+			LOG_MAJOR_ERROR,
+			LOG_MINOR_COMPONENT_GPU,
+			"%s Reference divider range is invalid", __func__);
+		return MAX_PLL_CALC_ERROR;
+	}
+
+/* 3) Try to find PLL dividers given ranges
+ * starting with minimal error tolerance.
+ * Increase error tolerance until PLL dividers found*/
+	err_tolerance = MAX_PLL_CALC_ERROR;
+
+	while (!calc_pll_dividers_in_range(
+			calc_pll_cs,
+			pll_settings,
+			min_ref_divider,
+			max_ref_divider,
+			min_post_divider,
+			max_post_divider,
+			err_tolerance))
+		err_tolerance += (err_tolerance > 10)
+				? (err_tolerance / 10)
+				: 1;
+
+	return err_tolerance;
+}
+
+static bool pll_adjust_pix_clk(
+		struct dce110_clk_src *clk_src,
+		struct pixel_clk_params *pix_clk_params,
+		struct pll_settings *pll_settings)
+{
+	uint32_t actual_pix_clk_khz = 0;
+	uint32_t requested_clk_khz = 0;
+	struct bp_adjust_pixel_clock_parameters bp_adjust_pixel_clock_params = {
+							0 };
+	enum bp_result bp_result;
+
+	switch (pix_clk_params->signal_type) {
+	case SIGNAL_TYPE_HDMI_TYPE_A: {
+		requested_clk_khz = pix_clk_params->requested_pix_clk;
+
+		switch (pix_clk_params->color_depth) {
+		case COLOR_DEPTH_101010:
+			requested_clk_khz = (requested_clk_khz * 5) >> 2;
+			break; /* x1.25*/
+		case COLOR_DEPTH_121212:
+			requested_clk_khz = (requested_clk_khz * 6) >> 2;
+			break; /* x1.5*/
+		case COLOR_DEPTH_161616:
+			requested_clk_khz = requested_clk_khz * 2;
+			break; /* x2.0*/
+		default:
+			break;
+		}
+
+		actual_pix_clk_khz = requested_clk_khz;
+	}
+		break;
+
+	case SIGNAL_TYPE_DISPLAY_PORT:
+	case SIGNAL_TYPE_DISPLAY_PORT_MST:
+	case SIGNAL_TYPE_EDP:
+		requested_clk_khz = pix_clk_params->requested_sym_clk;
+		actual_pix_clk_khz = pix_clk_params->requested_pix_clk;
+		break;
+
+	default:
+		requested_clk_khz = pix_clk_params->requested_pix_clk;
+		actual_pix_clk_khz = pix_clk_params->requested_pix_clk;
+		break;
+	}
+
+	bp_adjust_pixel_clock_params.pixel_clock = requested_clk_khz;
+	bp_adjust_pixel_clock_params.
+		encoder_object_id = pix_clk_params->encoder_object_id;
+	bp_adjust_pixel_clock_params.signal_type = pix_clk_params->signal_type;
+	bp_adjust_pixel_clock_params.
+		ss_enable = pix_clk_params->flags.ENABLE_SS;
+	bp_result = clk_src->bios->funcs->adjust_pixel_clock(
+			clk_src->bios, &bp_adjust_pixel_clock_params);
+	if (bp_result == BP_RESULT_OK) {
+		pll_settings->actual_pix_clk = actual_pix_clk_khz;
+		pll_settings->adjusted_pix_clk =
+			bp_adjust_pixel_clock_params.adjusted_pixel_clock;
+		pll_settings->reference_divider =
+			bp_adjust_pixel_clock_params.reference_divider;
+		pll_settings->pix_clk_post_divider =
+			bp_adjust_pixel_clock_params.pixel_clock_post_divider;
+
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * Calculate PLL Dividers for given Clock Value.
+ * First will call VBIOS Adjust Exec table to check if requested Pixel clock
+ * will be Adjusted based on usage.
+ * Then it will calculate PLL Dividers for this Adjusted clock using preferred
+ * method (Maximum VCO frequency).
+ *
+ * \return
+ *     Calculation error in units of 0.01%
+ */
+static uint32_t dce110_get_pix_clk_dividers(
+		struct clock_source *cs,
+		struct pixel_clk_params *pix_clk_params,
+		struct pll_settings *pll_settings)
+{
+	struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs);
+	uint32_t pll_calc_error = MAX_PLL_CALC_ERROR;
+	uint32_t addr = 0;
+	uint32_t value = 0;
+	uint32_t field = 0;
+
+	if (pix_clk_params == NULL || pll_settings == NULL
+			|| pix_clk_params->requested_pix_clk == 0) {
+		dal_logger_write(clk_src->base.ctx->logger,
+			LOG_MAJOR_ERROR,
+			LOG_MINOR_COMPONENT_GPU,
+			"%s: Invalid parameters!!\n", __func__);
+		return pll_calc_error;
+	}
+
+	dm_memset(pll_settings, 0, sizeof(*pll_settings));
+
+	if (cs->id == CLOCK_SOURCE_ID_EXTERNAL) {
+		pll_settings->adjusted_pix_clk = clk_src->ext_clk_khz;
+		pll_settings->calculated_pix_clk = clk_src->ext_clk_khz;
+		pll_settings->actual_pix_clk =
+					pix_clk_params->requested_pix_clk;
+		return 0;
+	}
+	/* PLL only after this point */
+
+	/* Check if reference clock is external (not pcie/xtalin)
+	* HW Dce80 spec:
+	* 00 - PCIE_REFCLK, 01 - XTALIN,    02 - GENERICA,    03 - GENERICB
+	* 04 - HSYNCA,      05 - GENLK_CLK, 06 - PCIE_REFCLK, 07 - DVOCLK0 */
+	addr = clk_src->offsets.pll_cntl;
+	value = dm_read_reg(clk_src->base.ctx, addr);
+	field = get_reg_field_value(value, PLL_CNTL, PLL_REF_DIV_SRC);
+	pll_settings->use_external_clk = (field > 1);
+
+	/* VBIOS by default enables DP SS (spread on IDCLK) for DCE 8.0 always
+	 * (we do not care any more from SI for some older DP Sink which
+	 * does not report SS support, no known issues) */
+	if ((pix_clk_params->flags.ENABLE_SS) ||
+			(dc_is_dp_signal(pix_clk_params->signal_type))) {
+
+		const struct spread_spectrum_data *ss_data = get_ss_data_entry(
+					clk_src,
+					pix_clk_params->signal_type,
+					pll_settings->adjusted_pix_clk);
+
+		if (NULL != ss_data)
+			pll_settings->ss_percentage = ss_data->percentage;
+	}
+
+	/* Check VBIOS AdjustPixelClock Exec table */
+	if (!pll_adjust_pix_clk(clk_src, pix_clk_params, pll_settings)) {
+		/* Should never happen, ASSERT and fill up values to be able
+		 * to continue. */
+		dal_logger_write(clk_src->base.ctx->logger,
+			LOG_MAJOR_ERROR,
+			LOG_MINOR_COMPONENT_GPU,
+			"%s: Failed to adjust pixel clock!!", __func__);
+		pll_settings->actual_pix_clk =
+				pix_clk_params->requested_pix_clk;
+		pll_settings->adjusted_pix_clk =
+				pix_clk_params->requested_pix_clk;
+
+		if (dc_is_dp_signal(pix_clk_params->signal_type))
+			pll_settings->adjusted_pix_clk = 100000;
+	}
+
+	/* Calculate Dividers */
+	if (pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A)
+		/*Calculate Dividers by HDMI object, no SS case or SS case */
+		pll_calc_error =
+			calculate_pixel_clock_pll_dividers(
+					&clk_src->calc_pll_hdmi,
+					pll_settings);
+	else
+		/*Calculate Dividers by default object, no SS case or SS case */
+		pll_calc_error =
+			calculate_pixel_clock_pll_dividers(
+					&clk_src->calc_pll,
+					pll_settings);
+
+	return pll_calc_error;
+}
+
+static bool disable_spread_spectrum(struct dce110_clk_src *clk_src)
+{
+	enum bp_result result;
+	struct bp_spread_spectrum_parameters bp_ss_params = {0};
+
+	bp_ss_params.pll_id = clk_src->base.id;
+
+	/*Call ASICControl to process ATOMBIOS Exec table*/
+	result = clk_src->bios->funcs->enable_spread_spectrum_on_ppll(
+			clk_src->bios,
+			&bp_ss_params,
+			false);
+
+	return result == BP_RESULT_OK;
+}
+
+static bool calculate_ss(
+		const struct pll_settings *pll_settings,
+		const struct spread_spectrum_data *ss_data,
+		struct delta_sigma_data *ds_data)
+{
+	struct fixed32_32 fb_div;
+	struct fixed32_32 ss_amount;
+	struct fixed32_32 ss_nslip_amount;
+	struct fixed32_32 ss_ds_frac_amount;
+	struct fixed32_32 ss_step_size;
+	struct fixed32_32 modulation_time;
+
+	if (ds_data == NULL)
+		return false;
+	if (ss_data == NULL)
+		return false;
+	if (ss_data->percentage == 0)
+		return false;
+	if (pll_settings == NULL)
+		return false;
+
+
+	dm_memset(ds_data, 0, sizeof(struct delta_sigma_data));
+
+
+
+	/* compute SS_AMOUNT_FBDIV & SS_AMOUNT_NFRAC_SLIP & SS_AMOUNT_DSFRAC*/
+	/* 6 decimal point support in fractional feedback divider */
+	fb_div  = dal_fixed32_32_from_fraction(
+		pll_settings->fract_feedback_divider, 1000000);
+	fb_div = dal_fixed32_32_add_int(fb_div, pll_settings->feedback_divider);
+
+	ds_data->ds_frac_amount = 0;
+	/*spreadSpectrumPercentage is in the unit of .01%,
+	 * so have to divided by 100 * 100*/
+	ss_amount = dal_fixed32_32_mul(
+		fb_div, dal_fixed32_32_from_fraction(ss_data->percentage,
+					100 * ss_data->percentage_divider));
+	ds_data->feedback_amount = dal_fixed32_32_floor(ss_amount);
+
+	ss_nslip_amount = dal_fixed32_32_sub(ss_amount,
+		dal_fixed32_32_from_int(ds_data->feedback_amount));
+	ss_nslip_amount = dal_fixed32_32_mul_int(ss_nslip_amount, 10);
+	ds_data->nfrac_amount = dal_fixed32_32_floor(ss_nslip_amount);
+
+	ss_ds_frac_amount = dal_fixed32_32_sub(ss_nslip_amount,
+		dal_fixed32_32_from_int(ds_data->nfrac_amount));
+	ss_ds_frac_amount = dal_fixed32_32_mul_int(ss_ds_frac_amount, 65536);
+	ds_data->ds_frac_amount = dal_fixed32_32_floor(ss_ds_frac_amount);
+
+	/* compute SS_STEP_SIZE_DSFRAC */
+	modulation_time = dal_fixed32_32_from_fraction(
+		pll_settings->reference_freq * 1000,
+		pll_settings->reference_divider * ss_data->modulation_freq_hz);
+
+
+	if (ss_data->flags.CENTER_SPREAD)
+		modulation_time = dal_fixed32_32_div_int(modulation_time, 4);
+	else
+		modulation_time = dal_fixed32_32_div_int(modulation_time, 2);
+
+	ss_step_size = dal_fixed32_32_div(ss_amount, modulation_time);
+	/* SS_STEP_SIZE_DSFRAC_DEC = Int(SS_STEP_SIZE * 2 ^ 16 * 10)*/
+	ss_step_size = dal_fixed32_32_mul_int(ss_step_size, 65536 * 10);
+	ds_data->ds_frac_size =  dal_fixed32_32_floor(ss_step_size);
+
+	return true;
+}
+
+static bool enable_spread_spectrum(
+		struct dce110_clk_src *clk_src,
+		enum signal_type signal, struct pll_settings *pll_settings)
+{
+	struct bp_spread_spectrum_parameters bp_params = {0};
+	struct delta_sigma_data d_s_data;
+	const struct spread_spectrum_data *ss_data = NULL;
+
+	ss_data = get_ss_data_entry(
+			clk_src,
+			signal,
+			pll_settings->calculated_pix_clk);
+
+/* Pixel clock PLL has been programmed to generate desired pixel clock,
+ * now enable SS on pixel clock */
+/* TODO is it OK to return true not doing anything ??*/
+	if (ss_data != NULL && pll_settings->ss_percentage != 0) {
+		if (calculate_ss(pll_settings, ss_data, &d_s_data)) {
+			bp_params.ds.feedback_amount =
+					d_s_data.feedback_amount;
+			bp_params.ds.nfrac_amount =
+					d_s_data.nfrac_amount;
+			bp_params.ds.ds_frac_size = d_s_data.ds_frac_size;
+			bp_params.ds_frac_amount =
+					d_s_data.ds_frac_amount;
+			bp_params.flags.DS_TYPE = 1;
+			bp_params.pll_id = clk_src->base.id;
+			bp_params.percentage = ss_data->percentage;
+			if (ss_data->flags.CENTER_SPREAD)
+				bp_params.flags.CENTER_SPREAD = 1;
+			if (ss_data->flags.EXTERNAL_SS)
+				bp_params.flags.EXTERNAL_SS = 1;
+
+			if (BP_RESULT_OK !=
+				clk_src->bios->funcs->
+					enable_spread_spectrum_on_ppll(
+							clk_src->bios,
+							&bp_params,
+							true))
+				return false;
+		} else
+			return false;
+	}
+	return true;
+}
+
+static void program_pixel_clk_resync(
+		struct dce110_clk_src *clk_src,
+		enum signal_type signal_type,
+		enum dc_color_depth colordepth)
+{
+	uint32_t value = 0;
+
+	value = dm_read_reg(clk_src->base.ctx, clk_src->offsets.pixclk_resync_cntl);
+
+	set_reg_field_value(
+		value,
+		0,
+		PIXCLK1_RESYNC_CNTL,
+		DCCG_DEEP_COLOR_CNTL1);
+
+	/*
+	 24 bit mode: TMDS clock = 1.0 x pixel clock  (1:1)
+	 30 bit mode: TMDS clock = 1.25 x pixel clock (5:4)
+	 36 bit mode: TMDS clock = 1.5 x pixel clock  (3:2)
+	 48 bit mode: TMDS clock = 2 x pixel clock    (2:1)
+	 */
+	if (signal_type != SIGNAL_TYPE_HDMI_TYPE_A)
+		return;
+
+	switch (colordepth) {
+	case COLOR_DEPTH_888:
+		set_reg_field_value(
+			value,
+			0,
+			PIXCLK1_RESYNC_CNTL,
+			DCCG_DEEP_COLOR_CNTL1);
+		break;
+	case COLOR_DEPTH_101010:
+		set_reg_field_value(
+			value,
+			1,
+			PIXCLK1_RESYNC_CNTL,
+			DCCG_DEEP_COLOR_CNTL1);
+		break;
+	case COLOR_DEPTH_121212:
+		set_reg_field_value(
+			value,
+			2,
+			PIXCLK1_RESYNC_CNTL,
+			DCCG_DEEP_COLOR_CNTL1);
+		break;
+	case COLOR_DEPTH_161616:
+		set_reg_field_value(
+			value,
+			3,
+			PIXCLK1_RESYNC_CNTL,
+			DCCG_DEEP_COLOR_CNTL1);
+		break;
+	default:
+		break;
+	}
+
+	dm_write_reg(
+		clk_src->base.ctx,
+		clk_src->offsets.pixclk_resync_cntl,
+		value);
+}
+
+static bool dce110_program_pix_clk(
+		struct clock_source *clk_src,
+		struct pixel_clk_params *pix_clk_params,
+		struct pll_settings *pll_settings)
+{
+	struct dce110_clk_src *dce110_clk_src = TO_DCE110_CLK_SRC(clk_src);
+	struct bp_pixel_clock_parameters bp_pc_params = {0};
+
+	/* First disable SS
+	 * ATOMBIOS will enable by default SS on PLL for DP,
+	 * do not disable it here
+	 */
+	if (clk_src->id != CLOCK_SOURCE_ID_EXTERNAL &&
+			!dc_is_dp_signal(pix_clk_params->signal_type))
+		disable_spread_spectrum(dce110_clk_src);
+
+	/*ATOMBIOS expects pixel rate adjusted by deep color ratio)*/
+	bp_pc_params.controller_id = pix_clk_params->controller_id;
+	bp_pc_params.pll_id = clk_src->id;
+	bp_pc_params.target_pixel_clock =
+			pll_settings->actual_pix_clk;
+	bp_pc_params.reference_divider = pll_settings->reference_divider;
+	bp_pc_params.feedback_divider = pll_settings->feedback_divider;
+	bp_pc_params.fractional_feedback_divider =
+			pll_settings->fract_feedback_divider;
+	bp_pc_params.pixel_clock_post_divider =
+			pll_settings->pix_clk_post_divider;
+	bp_pc_params.encoder_object_id = pix_clk_params->encoder_object_id;
+	bp_pc_params.signal_type = pix_clk_params->signal_type;
+	bp_pc_params.flags.SET_EXTERNAL_REF_DIV_SRC =
+					pll_settings->use_external_clk;
+
+	if (dce110_clk_src->bios->funcs->set_pixel_clock(
+			dce110_clk_src->bios, &bp_pc_params) != BP_RESULT_OK)
+		return false;
+
+/* Enable SS
+ * ATOMBIOS will enable by default SS for DP on PLL ( DP ID clock),
+ * based on HW display PLL team, SS control settings should be programmed
+ * during PLL Reset, but they do not have effect
+ * until SS_EN is asserted.*/
+	if (clk_src->id != CLOCK_SOURCE_ID_EXTERNAL
+		&& pix_clk_params->flags.ENABLE_SS && !dc_is_dp_signal(
+						pix_clk_params->signal_type))
+		if (!enable_spread_spectrum(dce110_clk_src,
+						pix_clk_params->signal_type,
+						pll_settings))
+			return false;
+
+/* Resync deep color DTO */
+	if (clk_src->id != CLOCK_SOURCE_ID_EXTERNAL)
+		program_pixel_clk_resync(dce110_clk_src,
+					pix_clk_params->signal_type,
+					pix_clk_params->color_depth);
+
+	return true;
+}
+
+static bool dce110_clock_source_power_down(
+		struct clock_source *clk_src)
+{
+	struct dce110_clk_src *dce110_clk_src = TO_DCE110_CLK_SRC(clk_src);
+	enum bp_result bp_result;
+	struct bp_pixel_clock_parameters bp_pixel_clock_params = {0};
+
+	if (clk_src->id == CLOCK_SOURCE_ID_EXTERNAL)
+		return true;
+
+	/* If Pixel Clock is 0 it means Power Down Pll*/
+	bp_pixel_clock_params.controller_id = CONTROLLER_ID_UNDEFINED;
+	bp_pixel_clock_params.pll_id = clk_src->id;
+	bp_pixel_clock_params.flags.FORCE_PROGRAMMING_OF_PLL = 1;
+
+	/*Call ASICControl to process ATOMBIOS Exec table*/
+	bp_result = dce110_clk_src->bios->funcs->set_pixel_clock(
+			dce110_clk_src->bios,
+			&bp_pixel_clock_params);
+
+	return bp_result == BP_RESULT_OK;
+}
+
+/*****************************************/
+/* Constructor                           */
+/*****************************************/
+static struct clock_source_funcs dce110_clk_src_funcs = {
+	.cs_power_down = dce110_clock_source_power_down,
+	.program_pix_clk = dce110_program_pix_clk,
+	.get_pix_clk_dividers = dce110_get_pix_clk_dividers
+};
+
+
+static void get_ss_info_from_atombios(
+		struct dce110_clk_src *clk_src,
+		enum as_signal_type as_signal,
+		struct spread_spectrum_data *spread_spectrum_data[],
+		uint32_t *ss_entries_num)
+{
+	enum bp_result bp_result = BP_RESULT_FAILURE;
+	struct spread_spectrum_info *ss_info;
+	struct spread_spectrum_data *ss_data;
+	struct spread_spectrum_info *ss_info_cur;
+	struct spread_spectrum_data *ss_data_cur;
+	uint32_t i;
+
+	if (ss_entries_num == NULL) {
+		dal_logger_write(clk_src->base.ctx->logger,
+			LOG_MAJOR_SYNC,
+			LOG_MINOR_SYNC_HW_CLOCK_ADJUST,
+			"Invalid entry !!!\n");
+		return;
+	}
+	if (spread_spectrum_data == NULL) {
+		dal_logger_write(clk_src->base.ctx->logger,
+			LOG_MAJOR_SYNC,
+			LOG_MINOR_SYNC_HW_CLOCK_ADJUST,
+			"Invalid array pointer!!!\n");
+		return;
+	}
+
+	spread_spectrum_data[0] = NULL;
+	*ss_entries_num = 0;
+
+	*ss_entries_num = clk_src->bios->funcs->get_ss_entry_number(
+			clk_src->bios,
+			as_signal);
+
+	if (*ss_entries_num == 0)
+		return;
+
+	ss_info = dm_alloc(clk_src->base.ctx, sizeof(struct spread_spectrum_info)
+				* (*ss_entries_num));
+	ss_info_cur = ss_info;
+	if (ss_info == NULL)
+		return;
+
+	ss_data = dm_alloc(clk_src->base.ctx, sizeof(struct spread_spectrum_data) *
+							(*ss_entries_num));
+	if (ss_data == NULL)
+		goto out_free_info;
+
+	for (i = 0, ss_info_cur = ss_info;
+		i < (*ss_entries_num);
+		++i, ++ss_info_cur) {
+
+		bp_result = clk_src->bios->funcs->get_spread_spectrum_info(
+				clk_src->bios,
+				as_signal,
+				i,
+				ss_info_cur);
+
+		if (bp_result != BP_RESULT_OK)
+			goto out_free_data;
+	}
+
+	for (i = 0, ss_info_cur = ss_info, ss_data_cur = ss_data;
+		i < (*ss_entries_num);
+		++i, ++ss_info_cur, ++ss_data_cur) {
+
+		if (ss_info_cur->type.STEP_AND_DELAY_INFO != false) {
+			dal_logger_write(clk_src->base.ctx->logger,
+				LOG_MAJOR_SYNC,
+				LOG_MINOR_SYNC_HW_CLOCK_ADJUST,
+				"Invalid ATOMBIOS SS Table!!!\n");
+			goto out_free_data;
+		}
+
+		/* for HDMI check SS percentage,
+		 * if it is > 6 (0.06%), the ATOMBIOS table info is invalid*/
+		if (as_signal == AS_SIGNAL_TYPE_HDMI
+				&& ss_info_cur->spread_spectrum_percentage > 6){
+			/* invalid input, do nothing */
+			dal_logger_write(clk_src->base.ctx->logger,
+				LOG_MAJOR_SYNC,
+				LOG_MINOR_SYNC_HW_CLOCK_ADJUST,
+				"Invalid SS percentage ");
+			dal_logger_write(clk_src->base.ctx->logger,
+				LOG_MAJOR_SYNC,
+				LOG_MINOR_SYNC_HW_CLOCK_ADJUST,
+				"for HDMI in ATOMBIOS info Table!!!\n");
+			continue;
+		}
+		if (ss_info_cur->spread_percentage_divider == 1000) {
+			/* Keep previous precision from ATOMBIOS for these
+			* in case new precision set by ATOMBIOS for these
+			* (otherwise all code in DCE specific classes
+			* for all previous ASICs would need
+			* to be updated for SS calculations,
+			* Audio SS compensation and DP DTO SS compensation
+			* which assumes fixed SS percentage Divider = 100)*/
+			ss_info_cur->spread_spectrum_percentage /= 10;
+			ss_info_cur->spread_percentage_divider = 100;
+		}
+
+		ss_data_cur->freq_range_khz = ss_info_cur->target_clock_range;
+		ss_data_cur->percentage =
+				ss_info_cur->spread_spectrum_percentage;
+		ss_data_cur->percentage_divider =
+				ss_info_cur->spread_percentage_divider;
+		ss_data_cur->modulation_freq_hz =
+				ss_info_cur->spread_spectrum_range;
+
+		if (ss_info_cur->type.CENTER_MODE)
+			ss_data_cur->flags.CENTER_SPREAD = 1;
+
+		if (ss_info_cur->type.EXTERNAL)
+			ss_data_cur->flags.EXTERNAL_SS = 1;
+
+	}
+
+	*spread_spectrum_data = ss_data;
+	dm_free(clk_src->base.ctx, ss_info);
+	return;
+
+out_free_data:
+	dm_free(clk_src->base.ctx, ss_data);
+	*ss_entries_num = 0;
+out_free_info:
+	dm_free(clk_src->base.ctx, ss_info);
+}
+
+static void ss_info_from_atombios_create(
+	struct dce110_clk_src *clk_src)
+{
+	get_ss_info_from_atombios(
+		clk_src,
+		AS_SIGNAL_TYPE_DISPLAY_PORT,
+		&clk_src->dp_ss_params,
+		&clk_src->dp_ss_params_cnt);
+	get_ss_info_from_atombios(
+		clk_src,
+		AS_SIGNAL_TYPE_HDMI,
+		&clk_src->hdmi_ss_params,
+		&clk_src->hdmi_ss_params_cnt);
+	get_ss_info_from_atombios(
+		clk_src,
+		AS_SIGNAL_TYPE_DVI,
+		&clk_src->dvi_ss_params,
+		&clk_src->dvi_ss_params_cnt);
+}
+
+static bool calc_pll_max_vco_construct(
+			struct calc_pll_clock_source *calc_pll_cs,
+			struct calc_pll_clock_source_init_data *init_data)
+{
+	uint32_t i;
+	struct firmware_info fw_info = { { 0 } };
+	if (calc_pll_cs == NULL ||
+			init_data == NULL ||
+			init_data->bp == NULL)
+		return false;
+
+	if (init_data->bp->funcs->get_firmware_info(
+				init_data->bp,
+				&fw_info) != BP_RESULT_OK)
+		return false;
+
+	calc_pll_cs->ctx = init_data->ctx;
+	calc_pll_cs->ref_freq_khz = fw_info.pll_info.crystal_frequency;
+	calc_pll_cs->min_vco_khz =
+			fw_info.pll_info.min_output_pxl_clk_pll_frequency;
+	calc_pll_cs->max_vco_khz =
+			fw_info.pll_info.max_output_pxl_clk_pll_frequency;
+
+	if (init_data->max_override_input_pxl_clk_pll_freq_khz != 0)
+		calc_pll_cs->max_pll_input_freq_khz =
+			init_data->max_override_input_pxl_clk_pll_freq_khz;
+	else
+		calc_pll_cs->max_pll_input_freq_khz =
+			fw_info.pll_info.max_input_pxl_clk_pll_frequency;
+
+	if (init_data->min_override_input_pxl_clk_pll_freq_khz != 0)
+		calc_pll_cs->min_pll_input_freq_khz =
+			init_data->min_override_input_pxl_clk_pll_freq_khz;
+	else
+		calc_pll_cs->min_pll_input_freq_khz =
+			fw_info.pll_info.min_input_pxl_clk_pll_frequency;
+
+	calc_pll_cs->min_pix_clock_pll_post_divider =
+			init_data->min_pix_clk_pll_post_divider;
+	calc_pll_cs->max_pix_clock_pll_post_divider =
+			init_data->max_pix_clk_pll_post_divider;
+	calc_pll_cs->min_pll_ref_divider =
+			init_data->min_pll_ref_divider;
+	calc_pll_cs->max_pll_ref_divider =
+			init_data->max_pll_ref_divider;
+
+	if (init_data->num_fract_fb_divider_decimal_point == 0 ||
+		init_data->num_fract_fb_divider_decimal_point_precision >
+				init_data->num_fract_fb_divider_decimal_point) {
+		dal_logger_write(calc_pll_cs->ctx->logger,
+			LOG_MAJOR_ERROR,
+			LOG_MINOR_COMPONENT_GPU,
+			"The dec point num or precision is incorrect!");
+		return false;
+	}
+	if (init_data->num_fract_fb_divider_decimal_point_precision == 0) {
+		dal_logger_write(calc_pll_cs->ctx->logger,
+			LOG_MAJOR_ERROR,
+			LOG_MINOR_COMPONENT_GPU,
+			"Incorrect fract feedback divider precision num!");
+		return false;
+	}
+
+	calc_pll_cs->fract_fb_divider_decimal_points_num =
+				init_data->num_fract_fb_divider_decimal_point;
+	calc_pll_cs->fract_fb_divider_precision =
+			init_data->num_fract_fb_divider_decimal_point_precision;
+	calc_pll_cs->fract_fb_divider_factor = 1;
+	for (i = 0; i < calc_pll_cs->fract_fb_divider_decimal_points_num; ++i)
+		calc_pll_cs->fract_fb_divider_factor *= 10;
+
+	calc_pll_cs->fract_fb_divider_precision_factor = 1;
+	for (
+		i = 0;
+		i < (calc_pll_cs->fract_fb_divider_decimal_points_num -
+				calc_pll_cs->fract_fb_divider_precision);
+		++i)
+		calc_pll_cs->fract_fb_divider_precision_factor *= 10;
+
+	return true;
+}
+
+bool dce110_clk_src_construct(
+	struct dce110_clk_src *clk_src,
+	struct dc_context *ctx,
+	struct dc_bios *bios,
+	enum clock_source_id id,
+	const struct dce110_clk_src_reg_offsets *reg_offsets)
+{
+	struct firmware_info fw_info = { { 0 } };
+/* structure normally used with PLL ranges from ATOMBIOS; DS on by default */
+	struct calc_pll_clock_source_init_data calc_pll_cs_init_data = {
+		bios,
+		1, /* minPixelClockPLLPostDivider */
+		PLL_POST_DIV__PLL_POST_DIV_PIXCLK_MASK,
+		/* maxPixelClockPLLPostDivider*/
+		1,/* minPLLRefDivider*/
+		PLL_REF_DIV__PLL_REF_DIV_MASK,/* maxPLLRefDivider*/
+		0,
+/* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/
+		0,
+/* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/
+		FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM,
+/*numberOfFractFBDividerDecimalPoints*/
+		FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM,
+/*number of decimal point to round off for fractional feedback divider value*/
+		ctx
+	};
+/*structure for HDMI, no SS or SS% <= 0.06% for 27 MHz Ref clock */
+	struct calc_pll_clock_source_init_data calc_pll_cs_init_data_hdmi = {
+		bios,
+		1, /* minPixelClockPLLPostDivider */
+		PLL_POST_DIV__PLL_POST_DIV_PIXCLK_MASK,
+		/* maxPixelClockPLLPostDivider*/
+		1,/* minPLLRefDivider*/
+		PLL_REF_DIV__PLL_REF_DIV_MASK,/* maxPLLRefDivider*/
+		13500,
+	/* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/
+		27000,
+	/* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/
+		FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM,
+		/*numberOfFractFBDividerDecimalPoints*/
+		FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM,
+/*number of decimal point to round off for fractional feedback divider value*/
+		ctx
+	};
+
+	clk_src->base.ctx = ctx;
+	clk_src->bios = bios;
+	clk_src->base.id = id;
+	clk_src->base.funcs = &dce110_clk_src_funcs;
+	clk_src->offsets = *reg_offsets;
+
+	if (clk_src->bios->funcs->get_firmware_info(
+			clk_src->bios, &fw_info) != BP_RESULT_OK) {
+		ASSERT_CRITICAL(false);
+		goto unexpected_failure;
+	}
+
+	clk_src->ext_clk_khz =
+			fw_info.external_clock_source_frequency_for_dp;
+	clk_src->ref_freq_khz = fw_info.pll_info.crystal_frequency;
+
+	if (clk_src->base.id == CLOCK_SOURCE_ID_EXTERNAL)
+		return true;
+
+	/* PLL only from here on */
+	ss_info_from_atombios_create(clk_src);
+
+	if (!calc_pll_max_vco_construct(
+			&clk_src->calc_pll,
+			&calc_pll_cs_init_data)) {
+		ASSERT_CRITICAL(false);
+		goto unexpected_failure;
+	}
+
+	if (clk_src->ref_freq_khz == 48000) {
+		calc_pll_cs_init_data_hdmi.
+			min_override_input_pxl_clk_pll_freq_khz = 24000;
+		calc_pll_cs_init_data_hdmi.
+			max_override_input_pxl_clk_pll_freq_khz = 48000;
+	} else if (clk_src->ref_freq_khz == 100000) {
+		calc_pll_cs_init_data_hdmi.
+			min_override_input_pxl_clk_pll_freq_khz = 25000;
+		calc_pll_cs_init_data_hdmi.
+			max_override_input_pxl_clk_pll_freq_khz = 50000;
+	}
+
+	if (!calc_pll_max_vco_construct(
+			&clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) {
+		ASSERT_CRITICAL(false);
+		goto unexpected_failure;
+	}
+
+	return true;
+
+unexpected_failure:
+	return false;
+}
+
+
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.h
new file mode 100644
index 000000000000..4fa82dad271f
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.h
@@ -0,0 +1,64 @@ 
+/* Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#ifndef __DC_CLOCK_SOURCE_DCE110_H__
+#define __DC_CLOCK_SOURCE_DCE110_H__
+
+#include "../inc/clock_source.h"
+
+#define TO_DCE110_CLK_SRC(clk_src)\
+	container_of(clk_src, struct dce110_clk_src, base)
+
+struct dce110_clk_src_reg_offsets {
+	uint32_t pll_cntl;
+	uint32_t pixclk_resync_cntl;
+};
+
+struct dce110_clk_src {
+	struct clock_source base;
+	struct dce110_clk_src_reg_offsets offsets;
+	struct dc_bios *bios;
+
+	struct spread_spectrum_data *dp_ss_params;
+	uint32_t dp_ss_params_cnt;
+	struct spread_spectrum_data *hdmi_ss_params;
+	uint32_t hdmi_ss_params_cnt;
+	struct spread_spectrum_data *dvi_ss_params;
+	uint32_t dvi_ss_params_cnt;
+
+	uint32_t ext_clk_khz;
+	uint32_t ref_freq_khz;
+
+	struct calc_pll_clock_source calc_pll;
+	struct calc_pll_clock_source calc_pll_hdmi;
+};
+
+bool dce110_clk_src_construct(
+	struct dce110_clk_src *clk_src,
+	struct dc_context *ctx,
+	struct dc_bios *bios,
+	enum clock_source_id,
+	const struct dce110_clk_src_reg_offsets *reg_offsets);
+
+#endif