📄 pll.c
字号:
/* 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 + -