⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pll.c

📁 ati driver
💻 C
📖 第 1 页 / 共 2 页
字号:
/*	Copyright (c) 2002/03, Thomas Kurschel		Part of Radeon accelerant			Takes care of PLL*/#include "radeon_accelerant.h"#include "../regs/pll_regs.h"#include "../shared/pll_access.h"#include "../common/utils.h"#include <stdlib.h>#include "set_mode.h"static void Radeon_PLLWaitForReadUpdateComplete( 	accelerator_info *ai, int crtc_idx ){	int i;		// we should wait forever, but	// 1. this is unsafe	// 2. some r300 loop forever (reported by XFree86)	for( i = 0; i < 10000; ++i ) {		if( (Radeon_INPLL( ai->regs, ai->si->asic, crtc_idx == 0 ? RADEON_PPLL_REF_DIV : RADEON_P2PLL_REF_DIV ) 			& RADEON_PPLL_ATOMIC_UPDATE_R) == 0 )			return;	}}static void Radeon_PLLWriteUpdate( 	accelerator_info *ai, int crtc_idx ){	Radeon_PLLWaitForReadUpdateComplete( ai, crtc_idx );	    Radeon_OUTPLLP( ai->regs, ai->si->asic,     	crtc_idx == 0 ? RADEON_PPLL_REF_DIV : RADEON_P2PLL_REF_DIV,    	RADEON_PPLL_ATOMIC_UPDATE_W,     	~RADEON_PPLL_ATOMIC_UPDATE_W );}// calculate PLL dividers// pll - info about PLL// freq - whished frequency in Hz// fixed_post_div - if != 0, fixed divider to be used// dividers - filled with proper dividersvoid Radeon_CalcPLLDividers( 	const pll_info *pll, uint32 freq, uint fixed_post_div, pll_dividers *dividers ){	// the PLL gets the reference	//		pll_in = ref_freq / ref_div	// this must be within pll_in_min..pll_in_max	// the VCO of the PLL has the frequency	//		vco = pll_in * feedback_div * extra_feedback_div	//		    = ref_freq / ref_div * feedback_div * extra_feedback_div	// where pre_feedback_div is hard-wired	// this must be within vco_min..vco_max	// the pixel clock is calculated as	//		pll_out = vco / post_div / extra_post_div	//		        = ref_freq * feedback_div * extra_feedback_div / (ref_div * post_div * extra_post_div)	// where extra_post_div _may_ be choosable between 1 and 2		// synonyms are:	//		ref_div = M	//		feedback_div = N	//		post_div = P		int 		min_post_div_idx, max_post_div_idx,		post_div_idx, extra_post_div_idx,		best_post_div_idx, best_extra_post_div_idx;			uint32		best_ref_div, best_feedback_div, best_freq;	int32		best_error, best_vco_dev;			best_error = 999999999;		// make compiler happy	best_post_div_idx = 0;	best_extra_post_div_idx = 0;	best_ref_div = 1;	best_feedback_div = 1;	best_freq = 1;	best_vco_dev = 1;	if( fixed_post_div == 0 ) {		min_post_div_idx = 0;		for( 			max_post_div_idx = 0; 			pll->post_divs[max_post_div_idx].divider != 0; 			++max_post_div_idx )			;		--max_post_div_idx;	} else {		for( 			min_post_div_idx = 0; 			pll->post_divs[min_post_div_idx].divider != fixed_post_div; 			++min_post_div_idx )			;		max_post_div_idx = min_post_div_idx;				//SHOW_FLOW( 2, "idx of fixed post divider: %d", min_post_div_idx );	}		// post dividers are quite restrictive, so they provide little search space only	for( extra_post_div_idx = 0; pll->extra_post_divs[extra_post_div_idx].divider != 0; ++extra_post_div_idx ) {		for( post_div_idx = min_post_div_idx; post_div_idx <= max_post_div_idx; ++post_div_idx ) {			uint32 ref_div;			uint32 post_div = 				pll->post_divs[post_div_idx].divider 				* pll->extra_post_divs[extra_post_div_idx].divider;					// post devider determines VCO frequency, so determine and verify it;			// freq is in Hz, everything else is in 10 kHz units			// we use 10 kHz units as long as possible to avoid uint32 overflows			uint32 vco = (freq / 10000) * post_div;						//SHOW_FLOW( 2, "post_div=%d, vco=%d", post_div, vco );						if( vco < pll->vco_min || vco > pll->vco_max )				continue;							//SHOW_FLOW0( 2, "jau" );			// we can either iterate through feedback or reference dividers;			// usually, there are fewer possible reference dividers, so I picked them			for( ref_div = pll->min_ref_div; ref_div <= pll->max_ref_div; ++ref_div ) {				uint32 feedback_div, cur_freq;				int32 error, vco_dev;								// this implies the frequency of the lock unit				uint32 pll_in = pll->ref_freq / ref_div;								if( pll_in < pll->pll_in_min || pll_in > pll->pll_in_max )					continue;								// well, only one variable is left					// timing is almost certainly valid, time to use Hz units				feedback_div = RoundDiv64( 					(int64)freq * ref_div * post_div, 					pll->ref_freq * 10000 * pll->extra_feedback_div);								if( feedback_div < pll->min_feedback_div ||					feedback_div > pll->max_feedback_div )					continue;									// let's see what we've got				cur_freq = RoundDiv64( 					(int64)pll->ref_freq * 10000 * feedback_div * pll->extra_feedback_div, 					ref_div * post_div );									// absolute error in terms of output clock									error = abs( cur_freq - freq );				// deviation from perfect VCO clock				vco_dev = abs( vco - pll->best_vco );								// if there is no optimal VCO frequency, choose setting with less error;				// if there is an optimal VCO frequency, choose new settings if				// - error is reduced significantly (100 Hz or more), or				// - output frequency is almost the same (less then 100 Hz difference) but				//	 VCO frequency is closer to best frequency 				if( (pll->best_vco == 0 && error < best_error) ||					(pll->best_vco != 0 && 					 (error < best_error - 100 ||					 (abs( error - best_error ) < 100 && vco_dev < best_vco_dev ))))				{					//SHOW_FLOW( 2, "got freq=%d, best_freq=%d", freq, cur_freq );					best_post_div_idx = post_div_idx;					best_extra_post_div_idx = extra_post_div_idx;					best_ref_div = ref_div;					best_feedback_div = feedback_div;					best_freq = cur_freq;					best_error = error;					best_vco_dev = vco_dev;				}			}		}	}	dividers->post_code = pll->post_divs[best_post_div_idx].code;	dividers->post = pll->post_divs[best_post_div_idx].divider;	dividers->extra_post_code = pll->post_divs[best_extra_post_div_idx].code;	dividers->extra_post = pll->post_divs[best_extra_post_div_idx].divider;	dividers->ref = best_ref_div;	dividers->feedback = best_feedback_div;	dividers->freq = best_freq;	/*SHOW_FLOW( 2, "post_code=%d, post=%d, extra_post_code=%d, extra_post=%d, ref=%d, feedback=%d, freq=%d",		dividers->post_code, dividers->post, dividers->extra_post_code, 		dividers->extra_post, dividers->ref, dividers->feedback, dividers->freq );*/}// with a TV timing given, find a corresponding CRT timing.// both timing must meet at the end of a frame, but as the PLL has a // limited frequency granularity, you don't really get a CRT timing // with precisely the same frame rate; the solution is to tweak the CRT// image a bit by making it wider/taller/smaller until the frame rate// drift is under a given threshold; // we follow two aims:// 	- primary, keep frame rate in sync//  - secondary, only tweak as much as unavoidablevoid Radeon_MatchCRTPLL( 	const pll_info *pll, 	uint32 tv_v_total, uint32 tv_h_total, uint32 tv_frame_size_adjust, uint32 freq, 	const display_mode *mode, uint32 max_v_tweak, uint32 max_h_tweak, 	uint32 max_frame_rate_drift, uint32 fixed_post_div, 	pll_dividers *dividers,	display_mode *tweaked_mode ){	uint32 v_tweak;	int32 v_tweak_dir;	uint32 pix_per_tv_frame;		SHOW_FLOW( 2, "fixed post divider: %d", fixed_post_div );		// number of TV pixels per frame	pix_per_tv_frame = tv_v_total * tv_h_total + tv_frame_size_adjust;		// starting with original data we tweak total horizontal and vertical size	// more and more until we find a proper CRT clock frequency	for( v_tweak = 0; v_tweak <= max_v_tweak; ++v_tweak ) {		for( v_tweak_dir = -1; v_tweak_dir <= 1; v_tweak_dir += 2 ) {			uint32 h_tweak;			int32 h_tweak_dir;						uint32 v_total = mode->timing.v_total + v_tweak * v_tweak_dir;						for( h_tweak = 0; h_tweak <= max_h_tweak; ++h_tweak ) {				for( h_tweak_dir = -1; h_tweak_dir <= 1; h_tweak_dir += 2 ) {					uint32 pix_per_crt_frame, frame_rate_drift;					uint32 crt_freq;					uint32 abs_crt_error;										uint32 h_total = mode->timing.h_total + h_tweak * h_tweak_dir;										// number of CRT pixels per frame					pix_per_crt_frame = v_total * h_total;										// frame rate must be:					//	frame_rate = freq / pix_per_tv_half_frame					// because of interlace, we must use half frames					//	pix_per_tv_half_frame = pix_per_tv_frame / 2					// to get a CRT image with the same frame rate, we get					//	crt_freq = frame_rate * pix_per_crt_frame					//	         = freq / (pix_per_tv_frame / 2) * pix_per_crt_frame					// formula is reordered as usual to improve accuracy					crt_freq = (uint64)freq * pix_per_crt_frame * 2 / pix_per_tv_frame;										Radeon_CalcPLLDividers( pll, crt_freq, fixed_post_div, dividers );							// get absolute CRT clock error per second					abs_crt_error = abs( dividers->freq - crt_freq );										//SHOW_INFO( 2, "whished=%d, is=%d", crt_freq, dividers->freq );										// convert it to relative CRT clock error:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -