diff options
author | Alex Deucher <alexdeucher@gmail.com> | 2009-04-22 11:42:18 -0400 |
---|---|---|
committer | Alex Deucher <alexdeucher@gmail.com> | 2009-04-22 11:42:18 -0400 |
commit | 24e4b73b4fbbb2c790e6120ede3caaa4e7e58359 (patch) | |
tree | 65d7192bce5b1a3f19464a693db48f2d11e58ba0 /src/radeon_crtc.c | |
parent | efa0825a86a8dc0f03ebb42c576ed26189e9d4bb (diff) |
radeon pll: add support for fractional feedback divs
Allows us to hit dot clocks much closer, especially on
chips with non-27 Mhz reference clocks like most IGP chips.
This should fix most flickering and blanking problems with
non-exact dot clocks.
Diffstat (limited to 'src/radeon_crtc.c')
-rw-r--r-- | src/radeon_crtc.c | 84 |
1 files changed, 51 insertions, 33 deletions
diff --git a/src/radeon_crtc.c b/src/radeon_crtc.c index cd0d55e8..a67d3748 100644 --- a/src/radeon_crtc.c +++ b/src/radeon_crtc.c @@ -129,6 +129,7 @@ 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) @@ -139,6 +140,7 @@ RADEONComputePLL(RADEONPLLPtr pll, uint32_t best_post_div = 1; uint32_t best_ref_div = 1; uint32_t best_feedback_div = 1; + uint32_t best_frac_feedback_div = 0; uint32_t best_freq = -1; uint32_t best_error = 0xffffffff; uint32_t best_vco_diff = 1; @@ -189,11 +191,15 @@ RADEONComputePLL(RADEONPLLPtr pll, while (min_feed_div < max_feed_div) { uint32_t vco; + uint32_t min_frac_feed_div = pll->min_frac_feedback_div; + uint32_t max_frac_feed_div = pll->max_frac_feedback_div+1; + uint32_t frac_feedback_div; + CARD64 tmp; feedback_div = (min_feed_div+max_feed_div)/2; - vco = RADEONDiv((CARD64)pll->reference_freq * feedback_div, - ref_div); + tmp = (CARD64)pll->reference_freq * feedback_div; + vco = RADEONDiv(tmp, ref_div); if (vco < pll->pll_out_min) { min_feed_div = feedback_div+1; @@ -203,45 +209,55 @@ RADEONComputePLL(RADEONPLLPtr pll, continue; } - current_freq = RADEONDiv((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; - } else if (current_freq == freq) { - if (best_freq == -1) { - 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; - } else if (((flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) || - ((flags & RADEON_PLL_PREFER_HIGH_REF_DIV) && (ref_div > best_ref_div)) || - ((flags & RADEON_PLL_PREFER_LOW_FB_DIV) && (feedback_div < best_feedback_div)) || - ((flags & RADEON_PLL_PREFER_HIGH_FB_DIV) && (feedback_div > best_feedback_div)) || - ((flags & RADEON_PLL_PREFER_LOW_POST_DIV) && (post_div < best_post_div)) || - ((flags & RADEON_PLL_PREFER_HIGH_POST_DIV) && (post_div > best_post_div))) { + while (min_frac_feed_div < max_frac_feed_div) { + frac_feedback_div = (min_frac_feed_div+max_frac_feed_div)/2; + tmp = (CARD64)pll->reference_freq * 10000 * feedback_div; + tmp += (CARD64)pll->reference_freq * 1000 * frac_feedback_div; + current_freq = RADEONDiv(tmp, 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_frac_feedback_div = frac_feedback_div; best_freq = current_freq; best_error = error; best_vco_diff = vco_diff; + } else if (current_freq == freq) { + if (best_freq == -1) { + best_post_div = post_div; + best_ref_div = ref_div; + best_feedback_div = feedback_div; + best_frac_feedback_div = frac_feedback_div; + best_freq = current_freq; + best_error = error; + best_vco_diff = vco_diff; + } else if (((flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) || + ((flags & RADEON_PLL_PREFER_HIGH_REF_DIV) && (ref_div > best_ref_div)) || + ((flags & RADEON_PLL_PREFER_LOW_FB_DIV) && (feedback_div < best_feedback_div)) || + ((flags & RADEON_PLL_PREFER_HIGH_FB_DIV) && (feedback_div > best_feedback_div)) || + ((flags & RADEON_PLL_PREFER_LOW_POST_DIV) && (post_div < best_post_div)) || + ((flags & RADEON_PLL_PREFER_HIGH_POST_DIV) && (post_div > best_post_div))) { + best_post_div = post_div; + best_ref_div = ref_div; + best_feedback_div = feedback_div; + best_frac_feedback_div = frac_feedback_div; + best_freq = current_freq; + best_error = error; + best_vco_diff = vco_diff; + } } + if (current_freq < freq) + min_frac_feed_div = frac_feedback_div+1; + else + max_frac_feed_div = frac_feedback_div; } - if (current_freq < freq) min_feed_div = feedback_div+1; else @@ -252,6 +268,7 @@ RADEONComputePLL(RADEONPLLPtr pll, ErrorF("best_freq: %u\n", (unsigned int)best_freq); ErrorF("best_feedback_div: %u\n", (unsigned int)best_feedback_div); + ErrorF("best_frac_feedback_div: %u\n", (unsigned int)best_frac_feedback_div); ErrorF("best_ref_div: %u\n", (unsigned int)best_ref_div); ErrorF("best_post_div: %u\n", (unsigned int)best_post_div); @@ -259,6 +276,7 @@ RADEONComputePLL(RADEONPLLPtr pll, FatalError("Couldn't find valid PLL dividers\n"); *chosen_dot_clock_freq = best_freq / 10000; *chosen_feedback_div = best_feedback_div; + *chosen_frac_feedback_div = best_frac_feedback_div; *chosen_reference_div = best_ref_div; *chosen_post_div = best_post_div; |