📄 pll.c
字号:
// rel_error = abs_crt_error / crt_freq // now to absolute TV clock error per second: // abs_tv_error = rel_error * tv_freq // and finally to TV clock error per frame: // frame_rate_drift = abs_tv_error / frame_rate // = abs_crt_error / crt_freq * tv_freq / frame_rate // this can be simplified by using: // tv_freq = pix_per_tv_frame * frame_rate // so we get: // frame_rate_drift = abs_crt_error / crt_freq * pix_per_tv_frame * frame_rate / frame_rate // = abs_crt_error / crt_freq * pix_per_tv_frame frame_rate_drift = (uint64)abs_crt_error * pix_per_tv_frame / freq; // if drift is within threshold, we take this setting and stop // searching (later iteration will increasingly tweak screen size, // and we don't really want that) if( frame_rate_drift <= max_frame_rate_drift ) { SHOW_INFO( 2, "frame_rate_drift=%d, crt_freq=%d, v_total=%d, h_total=%d", frame_rate_drift, crt_freq, v_total, h_total ); tweaked_mode->timing.pixel_clock = crt_freq; tweaked_mode->timing.v_total = v_total; tweaked_mode->timing.h_total = h_total; return; } } } } }}// table to map divider to register valuestatic pll_divider_map post_divs[] = { { 1, 0 }, { 2, 1 }, { 4, 2 }, { 8, 3 }, { 3, 4 },// { 16, 5 }, // at least for pll2 of M6, this value is reserved { 6, 6 }, { 12, 7 }, { 0, 0 }};// normal PLLs have no extra post dividerstatic pll_divider_map extra_post_divs[] = { { 1, 1 }, { 0, 0 }};// extra post-divider provided by Rage Theatrestatic pll_divider_map external_extra_post_divs[] = { { 1, 0 }, { 2, 1 }, { 0, 0 }};// post-dividers of Rage Theatrestatic pll_divider_map tv_post_divs[] = { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 8, 8 }, { 9, 9 }, { 10, 10 }, { 11, 11 }, { 12, 12 }, { 13, 13 }, { 14, 14 }, { 15, 15 }, { 0, 0 }};// get PLL parameters of TV PLLvoid Radeon_GetTVPLLConfiguration( const general_pll_info *general_pll, pll_info *pll, bool internal_encoder ){ pll->post_divs = tv_post_divs; pll->extra_post_divs = internal_encoder ? extra_post_divs : external_extra_post_divs; pll->ref_freq = general_pll->ref_freq; pll->vco_min = 10000; pll->vco_max = 25000; // I'm not sure about the upper limit pll->min_ref_div = 4; pll->max_ref_div = 0x3ff; // in the original code, they set it to 330kHz if PAL is requested and // quartz is 27 MHz, but I don't see how these circumstances can effect the // mimimal PLL input frequency pll->pll_in_min = 20;//40; // in the original code, they don't define an upper limit pll->pll_in_max = 100; pll->extra_feedback_div = 1; pll->min_feedback_div = 4; pll->max_feedback_div = 0x7ff; pll->best_vco = 21000;}// get PLL parameters of CRT PLL used in conjunction with TV-outvoid Radeon_GetTVCRTPLLConfiguration( const general_pll_info *general_pll, pll_info *pll, bool internal_tv_encoder ){ pll->post_divs = post_divs; pll->extra_post_divs = extra_post_divs; pll->ref_freq = general_pll->ref_freq; // in sample code, these limits are set in a strange way; // as a first shot, I use the BIOS provided limits /*pll->vco_min = general_pll->min_pll_freq; pll->vco_max = general_pll->max_pll_freq;*/ // in sample code, they use a variable post divider during calculation, but // use a fixed post divider for programming - the variable post divider is // multiplied to the feedback divider; // because of the fixed post divider (3), the VCO always runs far out of // its stable frequency range, so we have hack the limits pll->vco_min = 4000; pll->vco_max = general_pll->max_pll_freq; // in sample code, lower limit is 4, but in register spec they say everything but 0/1 pll->min_ref_div = 2; pll->max_ref_div = 0x3ff; pll->pll_in_min = 20; pll->pll_in_max = 100; pll->extra_feedback_div = 1; pll->min_feedback_div = 4; pll->max_feedback_div = 0x7ff; pll->best_vco = internal_tv_encoder ? 17500 : 21000;}// calc PLL dividers for CRT// mode->timing.pixel_clock must be in Hz because required accuracy in TV-Out modevoid Radeon_CalcCRTPLLDividers( const general_pll_info *general_pll, const display_mode *mode, pll_dividers *dividers ){ pll_info pll; pll.post_divs = post_divs; pll.extra_post_divs = extra_post_divs; pll.ref_freq = general_pll->ref_freq; pll.vco_min = general_pll->min_pll_freq; pll.vco_max = general_pll->max_pll_freq; pll.min_ref_div = 2; pll.max_ref_div = 0x3ff; pll.pll_in_min = 40; pll.pll_in_max = 100; pll.extra_feedback_div = 1; pll.min_feedback_div = 4; pll.max_feedback_div = 0x7ff; pll.best_vco = 0; SHOW_FLOW( 2, "freq=%ld", mode->timing.pixel_clock ); Radeon_CalcPLLDividers( &pll, mode->timing.pixel_clock, 0, dividers );}// calculate PLL registers // mode->timing.pixel_clock must be in Hz because required accuracy in TV-Out mode// (old: freq is in 10kHz)void Radeon_CalcPLLRegisters( const display_mode *mode, const pll_dividers *dividers, pll_regs *values ){ values->dot_clock_freq = dividers->freq; values->feedback_div = dividers->feedback; values->post_div = dividers->post; values->pll_output_freq = dividers->freq * dividers->post; values->ppll_ref_div = dividers->ref; values->ppll_div_3 = (dividers->feedback | (dividers->post_code << 16)); // this is mad: the PLL controls the horizontal length in sub-byte precision! values->htotal_cntl = mode->timing.h_total & 7; SHOW_FLOW( 2, "dot_clock_freq=%ld, pll_output_freq=%ld, ref_div=%d, feedback_div=%d, post_div=%d", values->dot_clock_freq, values->pll_output_freq, values->ppll_ref_div, values->feedback_div, values->post_div );}// write values into PLL registersvoid Radeon_ProgramPLL( accelerator_info *ai, int crtc_idx, pll_regs *values ){ vuint8 *regs = ai->regs; radeon_type asic = ai->si->asic; SHOW_FLOW0( 2, "" ); // use some other PLL for pixel clock source to not fiddling with PLL // while somebody is using it Radeon_OUTPLLP( regs, asic, crtc_idx == 0 ? RADEON_VCLK_ECP_CNTL : RADEON_PIXCLKS_CNTL, RADEON_VCLK_SRC_CPU_CLK, ~RADEON_VCLK_SRC_SEL_MASK ); Radeon_OUTPLLP( regs, asic, crtc_idx == 0 ? RADEON_PPLL_CNTL : RADEON_P2PLL_CNTL, RADEON_PPLL_RESET | RADEON_PPLL_ATOMIC_UPDATE_EN | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN, ~(RADEON_PPLL_RESET | RADEON_PPLL_ATOMIC_UPDATE_EN | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN) ); // select divider 3 (well, only required for first PLL) OUTREGP( regs, RADEON_CLOCK_CNTL_INDEX, RADEON_PLL_DIV_SEL_DIV3, ~RADEON_PLL_DIV_SEL_MASK ); if( ai->si->new_pll && crtc_idx == 0 ) { // starting with r300, the reference divider of the first PLL was // moved to another bit position; at the old location, you only // find the "BIOS suggested divider"; no clue why they did that Radeon_OUTPLLP( regs, asic, RADEON_PPLL_REF_DIV, values->ppll_ref_div << RADEON_PPLL_REF_DIV_ACC_SHIFT, ~RADEON_PPLL_REF_DIV_ACC_MASK ); } else { Radeon_OUTPLLP( regs, asic, crtc_idx == 0 ? RADEON_PPLL_REF_DIV : RADEON_P2PLL_REF_DIV, values->ppll_ref_div, ~RADEON_PPLL_REF_DIV_MASK ); } Radeon_OUTPLLP( regs, asic, crtc_idx == 0 ? RADEON_PPLL_DIV_3 : RADEON_P2PLL_DIV_0, values->ppll_div_3, ~RADEON_PPLL_FB3_DIV_MASK ); Radeon_OUTPLLP( regs, asic, crtc_idx == 0 ? RADEON_PPLL_DIV_3 : RADEON_P2PLL_DIV_0, values->ppll_div_3, ~RADEON_PPLL_POST3_DIV_MASK ); Radeon_PLLWriteUpdate( ai, crtc_idx ); Radeon_PLLWaitForReadUpdateComplete( ai, crtc_idx ); Radeon_OUTPLL( regs, asic, crtc_idx == 0 ? RADEON_HTOTAL_CNTL : RADEON_HTOTAL2_CNTL, values->htotal_cntl ); Radeon_OUTPLLP( regs, asic, crtc_idx == 0 ? RADEON_PPLL_CNTL : RADEON_P2PLL_CNTL, 0, ~(RADEON_PPLL_RESET | RADEON_PPLL_SLEEP | RADEON_PPLL_ATOMIC_UPDATE_EN | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN) ); // there is no way to check whether PLL has settled, so wait a bit snooze( 5000 ); // use PLL for pixel clock again Radeon_OUTPLLP( regs, asic, crtc_idx == 0 ? RADEON_VCLK_ECP_CNTL : RADEON_PIXCLKS_CNTL, RADEON_VCLK_SRC_PPLL_CLK, ~RADEON_VCLK_SRC_SEL_MASK );}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -