1967 lines · 61.7 KB · C
RawDownload
/* * 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 "dc_types.h"#include "core_types.h" #include "include/grph_object_id.h"#include "include/logger_interface.h" #include "dce_clock_source.h"#include "clk_mgr.h"#include "dccg.h" #include "reg_helper.h" #define REG(reg)\	(clk_src->regs->reg) #define CTX \	clk_src->base.ctx #define DC_LOGGER \	CTX->logger #undef FN#define FN(reg_name, field_name) \	clk_src->cs_shift->field_name, clk_src->cs_mask->field_name #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_LVDS:		ss_parm = clk_src->lvds_ss_params;		entrys_num = clk_src->lvds_ss_params_cnt;		break; 	case SIGNAL_TYPE_DISPLAY_PORT:	case SIGNAL_TYPE_DISPLAY_PORT_MST:	case SIGNAL_TYPE_EDP:	case SIGNAL_TYPE_VIRTUAL:		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;} /** * calculate_fb_and_fractional_fb_divider - Calculates feedback and fractional *                                          feedback dividers values * * @calc_pll_cs:	    Pointer to clock source information * @target_pix_clk_100hz:   Desired frequency in 100 Hz * @ref_divider:            Reference divider (already known) * @post_divider:           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 * * return: * 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_100hz,		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_100hz * 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 * 10ull); /*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 += 5ULL *			    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 = (uint32_t)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 - Calculates Feedback and *                                      Fractional Feedback divider values *		                        for passed Reference and Post divider, *                                      checking for tolerance. * @calc_pll_cs:	Pointer to clock source information * @pll_settings:	Pointer to PLL settings * @ref_divider:	Reference divider (already known) * @post_divider:	Post Divider (already known) * @tolerance:		Tolerance for Calculated Pixel Clock to be within * * return: *  It fills the PLLSettings structure with PLL Dividers values *  if calculated values are within required tolerance *  It returns	- true if error is within tolerance *		- false if error 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_100hz;	uint32_t abs_err;	uint64_t actual_calc_clk_100hz; 	calculate_fb_and_fractional_fb_divider(			calc_pll_cs,			pll_settings->adjusted_pix_clk_100hz,			ref_divider,			post_divider,			&feedback_divider,			&fract_feedback_divider); 	/*Actual calculated value*/	actual_calc_clk_100hz = (uint64_t)feedback_divider *					calc_pll_cs->fract_fb_divider_factor +							fract_feedback_divider;	actual_calc_clk_100hz *= (uint64_t)calc_pll_cs->ref_freq_khz * 10;	actual_calc_clk_100hz =		div_u64(actual_calc_clk_100hz,			ref_divider * post_divider *				calc_pll_cs->fract_fb_divider_factor); 	actual_calculated_clock_100hz = (uint32_t)(actual_calc_clk_100hz); 	abs_err = (actual_calculated_clock_100hz >					pll_settings->adjusted_pix_clk_100hz)			? actual_calculated_clock_100hz -					pll_settings->adjusted_pix_clk_100hz			: pll_settings->adjusted_pix_clk_100hz -						actual_calculated_clock_100hz; 	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_100hz =			actual_calculated_clock_100hz;		pll_settings->vco_freq =			(uint32_t)div_u64((u64)actual_calculated_clock_100hz * post_divider, 10);		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_100hz * err_tolerance) /									100000;	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 dce110_clk_src *clk_src,		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_100hz == 0) {		DC_LOG_ERROR(			"%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_100hz <						calc_pll_cs->min_vco_khz * 10) {			min_post_divider = calc_pll_cs->min_vco_khz * 10 /					pll_settings->adjusted_pix_clk_100hz;			if ((min_post_divider *					pll_settings->adjusted_pix_clk_100hz) <						calc_pll_cs->min_vco_khz * 10)				min_post_divider++;		} 		max_post_divider = calc_pll_cs->max_pix_clock_pll_post_divider;		if (max_post_divider * pll_settings->adjusted_pix_clk_100hz				> calc_pll_cs->max_vco_khz * 10)			max_post_divider = calc_pll_cs->max_vco_khz * 10 /					pll_settings->adjusted_pix_clk_100hz;	} /* 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) {		DC_LOG_ERROR(			"%s Post divider range is invalid", __func__);		return MAX_PLL_CALC_ERROR;	} 	if (min_ref_divider > max_ref_divider) {		DC_LOG_ERROR(			"%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_100hz = 0;	uint32_t requested_clk_100hz = 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_100hz = pix_clk_params->requested_pix_clk_100hz;		if (pix_clk_params->pixel_encoding != PIXEL_ENCODING_YCBCR422) {			switch (pix_clk_params->color_depth) {			case COLOR_DEPTH_101010:				requested_clk_100hz = (requested_clk_100hz * 5) >> 2;				break; /* x1.25*/			case COLOR_DEPTH_121212:				requested_clk_100hz = (requested_clk_100hz * 6) >> 2;				break; /* x1.5*/			case COLOR_DEPTH_161616:				requested_clk_100hz = requested_clk_100hz * 2;				break; /* x2.0*/			default:				break;			}		}		actual_pix_clk_100hz = requested_clk_100hz;	}		break; 	case SIGNAL_TYPE_DISPLAY_PORT:	case SIGNAL_TYPE_DISPLAY_PORT_MST:	case SIGNAL_TYPE_EDP:		requested_clk_100hz = pix_clk_params->requested_sym_clk * 10;		actual_pix_clk_100hz = pix_clk_params->requested_pix_clk_100hz;		break; 	default:		requested_clk_100hz = pix_clk_params->requested_pix_clk_100hz;		actual_pix_clk_100hz = pix_clk_params->requested_pix_clk_100hz;		break;	} 	bp_adjust_pixel_clock_params.pixel_clock = requested_clk_100hz / 10;	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 != 0;	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_100hz = actual_pix_clk_100hz;		pll_settings->adjusted_pix_clk_100hz =			bp_adjust_pixel_clock_params.adjusted_pixel_clock * 10;		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_helper (		struct dce110_clk_src *clk_src,		struct pll_settings *pll_settings,		struct pixel_clk_params *pix_clk_params){	uint32_t field = 0;	uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; 	/* 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 */	REG_GET(PLL_CNTL, PLL_REF_DIV_SRC, &field);	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_100hz / 10); 		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)) {

Lines 1–500 of 1967.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t