diff options
author | Alex Deucher <alex@botch2.(none)> | 2007-12-11 11:57:27 -0500 |
---|---|---|
committer | Alex Deucher <alex@botch2.(none)> | 2007-12-11 11:57:27 -0500 |
commit | f3d2ec3a5ae61215c792018320158750e7aa937c (patch) | |
tree | b166a8e2d7afba01dc6a51266ac08b072cf6ba40 | |
parent | 9b125312ab6edc585e4f5931a6a6de81e13b6acc (diff) |
RADEON: rewrite PLL computation
Algorithm adapted from BeOS radeon driver with some tweaks by me.
Actually calulate and use the reference divider rather than using the bios default.
Also, always calculate the PLL, rather than falling back to bios dividers.
This should fix bugs 12913, 13590, 13533, possibly others.
-rw-r--r-- | src/radeon.h | 10 | ||||
-rw-r--r-- | src/radeon_crtc.c | 197 | ||||
-rw-r--r-- | src/radeon_driver.c | 9 |
3 files changed, 157 insertions, 59 deletions
diff --git a/src/radeon.h b/src/radeon.h index fe491e87..b7f92888 100644 --- a/src/radeon.h +++ b/src/radeon.h @@ -288,6 +288,7 @@ typedef struct { CARD32 dot_clock_freq; CARD32 pll_output_freq; int feedback_div; + int reference_div; int post_div; /* PLL registers */ @@ -300,6 +301,7 @@ typedef struct { CARD32 dot_clock_freq_2; CARD32 pll_output_freq_2; int feedback_div_2; + int reference_div_2; int post_div_2; /* PLL2 registers */ @@ -370,6 +372,14 @@ typedef struct { CARD32 min_pll_freq; CARD32 max_pll_freq; CARD16 xclk; + + CARD32 min_ref_div; + CARD32 max_ref_div; + CARD32 min_feedback_div; + CARD32 max_feedback_div; + CARD32 pll_in_min; + CARD32 pll_in_max; + CARD32 best_vco; } RADEONPLLRec, *RADEONPLLPtr; typedef struct { diff --git a/src/radeon_crtc.c b/src/radeon_crtc.c index b1d216de..8984428a 100644 --- a/src/radeon_crtc.c +++ b/src/radeon_crtc.c @@ -612,13 +612,96 @@ static int RADEONDiv(int n, int d) return (n + (d / 2)) / d; } +static CARD32 RADEONDiv64(CARD64 n, CARD32 d) +{ + return (n + (d / 2)) / d; +} + +static void +RADEONComputePLL(RADEONPLLPtr pll, + unsigned long freq, + CARD32 *chosen_dot_clock_freq, + CARD32 *chosen_feedback_div, + CARD32 *chosen_reference_div, + CARD32 *chosen_post_div) +{ + int post_divs[] = {1, 2, 4, 8, 3, 6, 12, 0}; + + int i; + + CARD32 best_vco = pll->best_vco; + CARD32 best_post_div = 1; + CARD32 best_ref_div = 1; + CARD32 best_feedback_div = 1; + CARD32 best_freq = 1; + CARD32 best_error = 0xffffffff; + CARD32 best_vco_diff = 1; + + ErrorF("freq: %d\n", freq); + + for (i = 0; post_divs[i]; i++) { + int post_div = post_divs[i]; + CARD32 ref_div; + CARD32 vco = (freq / 10000) * post_div; + + if (vco < pll->min_pll_freq || vco > pll->max_pll_freq) + continue; + + for (ref_div = pll->min_ref_div; ref_div <= pll->max_ref_div; ++ref_div) { + CARD32 feedback_div, current_freq, error, vco_diff; + CARD32 pll_in = pll->reference_freq / ref_div; + + if (pll_in < pll->pll_in_min || pll_in > pll->pll_in_max) + continue; + + feedback_div = RADEONDiv64((CARD64)freq * ref_div * post_div, + pll->reference_freq * 10000); + + if (feedback_div < pll->min_feedback_div || feedback_div > pll->max_feedback_div) + continue; + + current_freq = RADEONDiv64((CARD64)pll->reference_freq * 10000 * feedback_div, + ref_div * post_div); + + error = abs(current_freq - freq); + vco_diff = abs(vco - best_vco); + + if ((best_vco == 0 && error < best_error) || + (best_vco != 0 && + (error < best_error - 100 || + (abs(error - best_error) < 100 && vco_diff < best_vco_diff )))) { + best_post_div = post_div; + best_ref_div = ref_div; + best_feedback_div = feedback_div; + best_freq = current_freq; + best_error = error; + best_vco_diff = vco_diff; + } + } + } + + ErrorF("best_freq: %d\n", best_freq); + ErrorF("best_feedback_div: %d\n", best_feedback_div); + ErrorF("best_ref_div: %d\n", best_ref_div); + ErrorF("best_post_div: %d\n", best_post_div); + + *chosen_dot_clock_freq = best_freq; + *chosen_feedback_div = best_feedback_div; + *chosen_reference_div = best_ref_div; + *chosen_post_div = best_post_div; + +} + /* Define PLL registers for requested video mode */ static void -RADEONInitPLLRegisters(ScrnInfoPtr pScrn, RADEONInfoPtr info, - RADEONSavePtr save, RADEONPLLPtr pll, - double dot_clock) +RADEONInitPLLRegisters(ScrnInfoPtr pScrn, RADEONSavePtr save, + RADEONPLLPtr pll, DisplayModePtr mode) { - unsigned long freq = dot_clock * 100; + RADEONInfoPtr info = RADEONPTR(pScrn); + CARD32 feedback_div = 0; + CARD32 reference_div = 0; + CARD32 post_divider = 0; + CARD32 freq = 0; struct { int divider; @@ -640,21 +723,20 @@ RADEONInitPLLRegisters(ScrnInfoPtr pScrn, RADEONInfoPtr info, { 0, 0 } }; + RADEONComputePLL(pll, mode->Clock * 1000, &freq, &feedback_div, &reference_div, &post_divider); + +#if 0 if (info->UseBiosDividers) { save->ppll_ref_div = info->RefDivider; save->ppll_div_3 = info->FeedbackDivider | (info->PostDivider << 16); save->htotal_cntl = 0; return; } - - if (freq > pll->max_pll_freq) freq = pll->max_pll_freq; - if (freq * 12 < pll->min_pll_freq) freq = pll->min_pll_freq / 12; +#endif for (post_div = &post_divs[0]; post_div->divider; ++post_div) { - save->pll_output_freq = post_div->divider * freq; - - if (save->pll_output_freq >= pll->min_pll_freq - && save->pll_output_freq <= pll->max_pll_freq) break; + if (post_div->divider == post_divider) + break; } if (!post_div->divider) { @@ -662,20 +744,20 @@ RADEONInitPLLRegisters(ScrnInfoPtr pScrn, RADEONInfoPtr info, post_div = &post_divs[0]; } - save->dot_clock_freq = freq; - save->feedback_div = RADEONDiv(pll->reference_div - * save->pll_output_freq, - pll->reference_freq); - save->post_div = post_div->divider; + save->dot_clock_freq = freq / 10000; + save->feedback_div = feedback_div; + save->reference_div = reference_div; + save->post_div = post_divider; xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG, - "dc=%u, of=%u, fd=%d, pd=%d\n", + "dc=%u, of=%u, fd=%d, rd=%d, pd=%d\n", (unsigned)save->dot_clock_freq, (unsigned)save->pll_output_freq, save->feedback_div, + save->reference_div, save->post_div); - save->ppll_ref_div = pll->reference_div; + save->ppll_ref_div = save->reference_div; #if defined(__powerpc__) /* apparently programming this otherwise causes a hang??? */ @@ -685,21 +767,23 @@ RADEONInitPLLRegisters(ScrnInfoPtr pScrn, RADEONInfoPtr info, #endif save->ppll_div_3 = (save->feedback_div | (post_div->bitvalue << 16)); - save->htotal_cntl = 0; + save->htotal_cntl = mode->HTotal & 0x7; - save->vclk_ecp_cntl = (info->SavedReg.vclk_ecp_cntl & - ~RADEON_VCLK_SRC_SEL_MASK) | RADEON_VCLK_SRC_SEL_PPLLCLK; + save->vclk_ecp_cntl = (info->SavedReg.vclk_ecp_cntl & + ~RADEON_VCLK_SRC_SEL_MASK) | RADEON_VCLK_SRC_SEL_PPLLCLK; } /* Define PLL2 registers for requested video mode */ static void RADEONInitPLL2Registers(ScrnInfoPtr pScrn, RADEONSavePtr save, - RADEONPLLPtr pll, double dot_clock, - int no_odd_postdiv) + RADEONPLLPtr pll, DisplayModePtr mode) { - RADEONInfoPtr info = RADEONPTR(pScrn); - unsigned long freq = dot_clock * 100; + RADEONInfoPtr info = RADEONPTR(pScrn); + CARD32 feedback_div = 0; + CARD32 reference_div = 0; + CARD32 post_divider = 0; + CARD32 freq = 0; struct { int divider; @@ -720,18 +804,11 @@ RADEONInitPLL2Registers(ScrnInfoPtr pScrn, RADEONSavePtr save, { 0, 0 } }; - if (freq > pll->max_pll_freq) freq = pll->max_pll_freq; - if (freq * 12 < pll->min_pll_freq) freq = pll->min_pll_freq / 12; + RADEONComputePLL(pll, mode->Clock * 1000, &freq, &feedback_div, &reference_div, &post_divider); for (post_div = &post_divs[0]; post_div->divider; ++post_div) { - /* Odd post divider value don't work properly on the second digital - * output - */ - if (no_odd_postdiv && (post_div->divider & 1)) - continue; - save->pll_output_freq_2 = post_div->divider * freq; - if (save->pll_output_freq_2 >= pll->min_pll_freq - && save->pll_output_freq_2 <= pll->max_pll_freq) break; + if (post_div->divider == post_divider) + break; } if (!post_div->divider) { @@ -739,27 +816,29 @@ RADEONInitPLL2Registers(ScrnInfoPtr pScrn, RADEONSavePtr save, post_div = &post_divs[0]; } - save->dot_clock_freq_2 = freq; - save->feedback_div_2 = RADEONDiv(pll->reference_div - * save->pll_output_freq_2, - pll->reference_freq); - save->post_div_2 = post_div->divider; + save->dot_clock_freq_2 = freq / 10000; + save->feedback_div_2 = feedback_div; + save->reference_div_2 = reference_div; + save->post_div_2 = post_divider; xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG, - "dc=%u, of=%u, fd=%d, pd=%d\n", + "dc=%u, of=%u, fd=%d, rd=%d, pd=%d\n", (unsigned)save->dot_clock_freq_2, (unsigned)save->pll_output_freq_2, save->feedback_div_2, + save->reference_div_2, save->post_div_2); - save->p2pll_ref_div = pll->reference_div; + save->p2pll_ref_div = save->reference_div_2; + save->p2pll_div_0 = (save->feedback_div_2 | (post_div->bitvalue << 16)); - save->htotal_cntl2 = 0; - save->pixclks_cntl = ((info->SavedReg.pixclks_cntl & - ~(RADEON_PIX2CLK_SRC_SEL_MASK)) | - RADEON_PIX2CLK_SRC_SEL_P2PLLCLK); + save->htotal_cntl2 = mode->HTotal & 0x7; + + save->pixclks_cntl = ((info->SavedReg.pixclks_cntl & + ~(RADEON_PIX2CLK_SRC_SEL_MASK)) | + RADEON_PIX2CLK_SRC_SEL_P2PLLCLK); } @@ -837,25 +916,25 @@ radeon_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, ErrorF("init crtc1\n"); RADEONInitCrtcRegisters(crtc, &info->ModeReg, adjusted_mode); RADEONInitCrtcBase(crtc, &info->ModeReg, x, y); - dot_clock = adjusted_mode->Clock / 1000.0; - if (dot_clock) { + dot_clock = adjusted_mode->Clock / 1000.0; + if (dot_clock) { ErrorF("init pll1\n"); - RADEONInitPLLRegisters(pScrn, info, &info->ModeReg, &info->pll, dot_clock); - } else { - info->ModeReg.ppll_ref_div = info->SavedReg.ppll_ref_div; - info->ModeReg.ppll_div_3 = info->SavedReg.ppll_div_3; - info->ModeReg.htotal_cntl = info->SavedReg.htotal_cntl; - } + RADEONInitPLLRegisters(pScrn, &info->ModeReg, &info->pll, adjusted_mode); + } else { + info->ModeReg.ppll_ref_div = info->SavedReg.ppll_ref_div; + info->ModeReg.ppll_div_3 = info->SavedReg.ppll_div_3; + info->ModeReg.htotal_cntl = info->SavedReg.htotal_cntl; + } break; case 1: ErrorF("init crtc2\n"); - RADEONInitCrtc2Registers(crtc, &info->ModeReg, adjusted_mode); + RADEONInitCrtc2Registers(crtc, &info->ModeReg, adjusted_mode); RADEONInitCrtc2Base(crtc, &info->ModeReg, x, y); - dot_clock = adjusted_mode->Clock / 1000.0; - if (dot_clock) { + dot_clock = adjusted_mode->Clock / 1000.0; + if (dot_clock) { ErrorF("init pll2\n"); - RADEONInitPLL2Registers(pScrn, &info->ModeReg, &info->pll, dot_clock, no_odd_post_div); - } + RADEONInitPLL2Registers(pScrn, &info->ModeReg, &info->pll, adjusted_mode); + } break; } diff --git a/src/radeon_driver.c b/src/radeon_driver.c index 50f78eb9..4b5008e5 100644 --- a/src/radeon_driver.c +++ b/src/radeon_driver.c @@ -1063,6 +1063,15 @@ static void RADEONGetClockInfo(ScrnInfoPtr pScrn) info->RamWidth / 16); } + /* card limits for computing PLLs */ + pll->min_ref_div = 2; + pll->max_ref_div = 0x3ff; + pll->min_feedback_div = 4; + pll->max_feedback_div = 0x7ff; + pll->pll_in_min = 40; + pll->pll_in_max = 100; + pll->best_vco = 0; + xf86DrvMsg (pScrn->scrnIndex, X_INFO, "PLL parameters: rf=%u rd=%u min=%u max=%u; xclk=%u\n", pll->reference_freq, |