diff options
author | Alex Deucher <alexdeucher@gmail.com> | 2010-02-22 17:27:24 -0500 |
---|---|---|
committer | Alex Deucher <alexdeucher@gmail.com> | 2010-02-22 17:27:24 -0500 |
commit | 212e152536c4325e6799018891d9aee132681f48 (patch) | |
tree | a53dc9542eaef9a9ab659fd64ebfaf92b3f4e318 /src/radeon_crtc.c | |
parent | e68d3a3890fc81c51f2006b5548da1e8756ad2fd (diff) |
radeon: update new pll algo
- add support for pre-avivo chips
- add support for fixed post/ref dividers
- add support for non-fractional fb dividers
By default avivo chips use the new algo and
pre-avivo chips use the old algo. Use the
"NewPLL" option to toggle between them (set to
TRUE for the new algo, FALSE for the old).
Diffstat (limited to 'src/radeon_crtc.c')
-rw-r--r-- | src/radeon_crtc.c | 227 |
1 files changed, 163 insertions, 64 deletions
diff --git a/src/radeon_crtc.c b/src/radeon_crtc.c index 764839c0..5deff8dc 100644 --- a/src/radeon_crtc.c +++ b/src/radeon_crtc.c @@ -132,15 +132,15 @@ static uint32_t RADEONDiv(CARD64 n, uint32_t d) return (n + (d / 2)) / d; } -void -RADEONComputePLL(RADEONPLLPtr pll, - unsigned long freq, - uint32_t *chosen_dot_clock_freq, - uint32_t *chosen_feedback_div, - uint32_t *chosen_frac_feedback_div, - uint32_t *chosen_reference_div, - uint32_t *chosen_post_div, - int flags) +static void +RADEONComputePLL_old(RADEONPLLPtr pll, + unsigned long freq, + uint32_t *chosen_dot_clock_freq, + uint32_t *chosen_feedback_div, + uint32_t *chosen_frac_feedback_div, + uint32_t *chosen_reference_div, + uint32_t *chosen_post_div, + int flags) { uint32_t min_ref_div = pll->min_ref_div; uint32_t max_ref_div = pll->max_ref_div; @@ -306,79 +306,178 @@ RADEONComputePLL(RADEONPLLPtr pll, } -void -RADEONComputePLL_AVIVO(RADEONPLLPtr pll, - unsigned long freq, - uint32_t *chosen_dot_clock_freq, - uint32_t *chosen_feedback_div, - uint32_t *chosen_frac_feedback_div, - uint32_t *chosen_reference_div, - uint32_t *chosen_post_div, - int flags) +static Bool +calc_fb_div(RADEONPLLPtr pll, + unsigned long freq, + int flags, + int post_div, + int ref_div, + int *fb_div, + int *fb_div_frac) { - float m, n, frac_n, p, f_vco, f_pclk, best_freq; - float pll_out_max = pll->pll_out_max; - float pll_out_min = pll->pll_out_min; - float reference_freq = pll->reference_freq; - float pll_in_max = pll->pll_in_max; - float pll_in_min = pll->pll_in_min; float ffreq = freq / 10; - float error = 100; - - ErrorF("ffreq: %f\n", ffreq * 10); + float vco_freq = ffreq * post_div; + float feedback_divider = vco_freq * ref_div / pll->reference_freq; - /* 1 - max p */ - p = floor(pll_out_max / ffreq); + if (flags & RADEON_PLL_USE_FRAC_FB_DIV) { + feedback_divider = floor((feedback_divider * 10.0) + 0.5) * 0.1; - /* 2 - min m */ - m = ceil(reference_freq / pll_in_max); + *fb_div = floor(feedback_divider); + *fb_div_frac = fmod(feedback_divider, 1.0) * 10.0; - while (error > 0.25) { - /* 3 - n */ - n = (ffreq / reference_freq) * m * p; + } else { + *fb_div = floor(feedback_divider + 0.5); + *fb_div_frac = 0; + } + if ((*fb_div < pll->min_feedback_div) || (*fb_div > pll->max_feedback_div)) + return FALSE; + else + return TRUE; +} - /* 4 - vco out freq */ - f_vco = (n / m) * reference_freq; +static Bool +calc_fb_ref_div(RADEONPLLPtr pll, + unsigned long freq, + int flags, + int post_div, + int *fb_div, + int *fb_div_frac, + int *ref_div) +{ + float ffreq = freq / 10; + float max_error = ffreq * 0.0025; + float vco, error, pll_out; - /* 5 - pclk freq */ - f_pclk = f_vco / p; + for ((*ref_div) = pll->min_ref_div; (*ref_div) < pll->max_ref_div; ++(*ref_div)) { + if (calc_fb_div(pll, freq, flags, post_div, (*ref_div), fb_div, fb_div_frac)) { + vco = pll->reference_freq * ((*fb_div) + ((*fb_div_frac) * 0.1)) / (*ref_div); - /* 6 - error */ - error = (abs(f_pclk - ffreq) / f_pclk) * 100; + if ((vco < pll->pll_out_min) || (vco > pll->pll_out_max)) + continue; - best_freq = reference_freq * (n / (m * p)); + pll_out = vco / post_div; - /* min error 0.25% */ - if (error < 0.25) - break; + error = pll_out - ffreq; + if ((fabs(error) <= max_error) && (error >= 0)) + return TRUE; + } + } + return FALSE; +} - /* 7 - check m */ - m++; - if ((reference_freq / m) >= pll_in_min) - continue; +static void +RADEONComputePLL_new(RADEONPLLPtr pll, + unsigned long freq, + uint32_t *chosen_dot_clock_freq, + uint32_t *chosen_feedback_div, + uint32_t *chosen_frac_feedback_div, + uint32_t *chosen_reference_div, + uint32_t *chosen_post_div, + int flags) +{ + float ffreq = freq / 10; + float vco_frequency; + int fb_div = 0, fb_div_frac = 0, post_div = 0, ref_div = 0; + uint32_t best_freq = 0; + + if (flags & RADEON_PLL_USE_POST_DIV) { + post_div = pll->post_div; + if ((post_div < pll->min_post_div) || (post_div > pll->max_post_div)) + goto done; + vco_frequency = ffreq * post_div; + if ((vco_frequency < pll->pll_out_min) || (vco_frequency > pll->pll_out_max)) + goto done; + + if (flags & RADEON_PLL_USE_REF_DIV) { + ref_div = pll->reference_div; + if ((ref_div < pll->min_ref_div) || (ref_div > pll->max_ref_div)) + goto done; + if (!calc_fb_div(pll, freq, flags, post_div, ref_div, &fb_div, &fb_div_frac)) + goto done; + } + } else { + for (post_div = pll->max_post_div; post_div >= pll->min_post_div; --post_div) { + if (flags & RADEON_PLL_LEGACY) { + if ((post_div == 5) || + (post_div == 7) || + (post_div == 9) || + (post_div == 10) || + (post_div == 11)) + continue; + } + if ((flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1)) + continue; - /* 8 - check p */ - m = ceil(reference_freq / pll_in_max); - p--; - if ((p * ffreq) >= pll_out_min) - continue; - else - FatalError("Couldn't find valid PLL dividers\n"); + vco_frequency = ffreq * post_div; + if ((vco_frequency < pll->pll_out_min) || (vco_frequency > pll->pll_out_max)) + continue; + if (flags & RADEON_PLL_USE_REF_DIV) { + ref_div = pll->reference_div; + if ((ref_div < pll->min_ref_div) || (ref_div > pll->max_ref_div)) + goto done; + if (calc_fb_div(pll, freq, flags, post_div, ref_div, &fb_div, &fb_div_frac)) + break; + } else { + if (calc_fb_ref_div(pll, freq, flags, post_div, &fb_div, &fb_div_frac, &ref_div)) + break; + } + } } - frac_n = (n - (int)n) * 10; + best_freq = pll->reference_freq * 10 * fb_div; + best_freq += pll->reference_freq * fb_div_frac; + best_freq = best_freq / (ref_div * post_div); + + ErrorF("best_freq: %u\n", (unsigned int)best_freq); + ErrorF("best_feedback_div: %u\n", (unsigned int)fb_div); + ErrorF("best_frac_feedback_div: %u\n", (unsigned int)fb_div_frac); + ErrorF("best_ref_div: %u\n", (unsigned int)ref_div); + ErrorF("best_post_div: %u\n", (unsigned int)post_div); - ErrorF("best_freq: %u\n", (unsigned int)best_freq * 10); - ErrorF("best_feedback_div: %d.%d\n", (int)n, (int)frac_n); - ErrorF("best_ref_div: %d\n", (int)m); - ErrorF("best_post_div: %d\n", (int)p); +done: + if (best_freq == 0) + FatalError("Couldn't find valid PLL dividers\n"); *chosen_dot_clock_freq = best_freq; - *chosen_feedback_div = (uint32_t)n; - *chosen_frac_feedback_div = (uint32_t)frac_n; - *chosen_reference_div = (uint32_t)m; - *chosen_post_div = (uint32_t)p; + *chosen_feedback_div = fb_div; + *chosen_frac_feedback_div = fb_div_frac; + *chosen_reference_div = ref_div; + *chosen_post_div = post_div; + +} + +void +RADEONComputePLL(ScrnInfoPtr pScrn, + RADEONPLLPtr pll, + unsigned long freq, + uint32_t *chosen_dot_clock_freq, + uint32_t *chosen_feedback_div, + uint32_t *chosen_frac_feedback_div, + uint32_t *chosen_reference_div, + uint32_t *chosen_post_div, + int flags) +{ + RADEONInfoPtr info = RADEONPTR(pScrn); + if (IS_AVIVO_VARIANT) { + if (xf86ReturnOptValBool(info->Options, OPTION_NEW_PLL, TRUE)) + RADEONComputePLL_new(pll, freq, chosen_dot_clock_freq, + chosen_feedback_div, chosen_frac_feedback_div, + chosen_reference_div, chosen_post_div, flags); + else + RADEONComputePLL_old(pll, freq, chosen_dot_clock_freq, + chosen_feedback_div, chosen_frac_feedback_div, + chosen_reference_div, chosen_post_div, flags); + } else { + if (xf86ReturnOptValBool(info->Options, OPTION_NEW_PLL, FALSE)) + RADEONComputePLL_new(pll, freq, chosen_dot_clock_freq, + chosen_feedback_div, chosen_frac_feedback_div, + chosen_reference_div, chosen_post_div, flags); + else + RADEONComputePLL_old(pll, freq, chosen_dot_clock_freq, + chosen_feedback_div, chosen_frac_feedback_div, + chosen_reference_div, chosen_post_div, flags); + } } static void |