diff options
author | Jiří Paleček <jpalecek@web.de> | 2008-06-23 15:53:58 -0400 |
---|---|---|
committer | Alex Deucher <alex@botch2.com> | 2008-06-23 15:53:58 -0400 |
commit | 72feaa37ea07620f5f2ead438dbc72a1c8883cd3 (patch) | |
tree | 239935051ab53d1e6a3d2e871626ae9c86399d71 /src/radeon_crtc.c | |
parent | 9c2f909ea437a63a408d2398ecabe0b378dbb982 (diff) |
RADEON: PLL tweaks
Patch from Jiří Paleček (see debian bug 465864) with some tweaks
by me.
- abort rather than programming bad dividers if no pll dividers can be found
- improve the pll selection algorithm
- in general, prefer lower ref dividers
I've tested this patch on a wide variety of chips (r1xx-r6xx) and clocks.
Diffstat (limited to 'src/radeon_crtc.c')
-rw-r--r-- | src/radeon_crtc.c | 99 |
1 files changed, 69 insertions, 30 deletions
diff --git a/src/radeon_crtc.c b/src/radeon_crtc.c index 9eb36ed5..8f2d4fc8 100644 --- a/src/radeon_crtc.c +++ b/src/radeon_crtc.c @@ -132,7 +132,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_freq = 1; + uint32_t best_freq = -1; uint32_t best_error = 0xffffffff; uint32_t best_vco_diff = 1; uint32_t post_div; @@ -143,10 +143,21 @@ RADEONComputePLL(RADEONPLLPtr pll, if (flags & RADEON_PLL_USE_REF_DIV) min_ref_div = max_ref_div = pll->reference_div; + else { + max_ref_div = 2*max_ref_div - min_ref_div; + while (min_ref_div < max_ref_div-1) { + uint32_t mid=(min_ref_div+max_ref_div)/2; + uint32_t pll_in = pll->reference_freq / mid; + if (pll_in < pll->pll_in_min) + max_ref_div = mid; + else if (pll_in > pll->pll_in_max) + min_ref_div = mid; + else break; + } + } for (post_div = pll->min_post_div; post_div <= pll->max_post_div; ++post_div) { uint32_t ref_div; - uint32_t vco = (freq / 10000) * post_div; if ((flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1)) continue; @@ -161,45 +172,71 @@ RADEONComputePLL(RADEONPLLPtr pll, continue; } - if (vco < pll->pll_out_min || vco > pll->pll_out_max) - continue; - for (ref_div = min_ref_div; ref_div <= max_ref_div; ++ref_div) { uint32_t feedback_div, current_freq, error, vco_diff; uint32_t pll_in = pll->reference_freq / ref_div; + uint32_t min_feed_div = pll->min_feedback_div; + uint32_t max_feed_div = pll->max_feedback_div+1; if (pll_in < pll->pll_in_min || pll_in > pll->pll_in_max) continue; - feedback_div = RADEONDiv((CARD64)freq * ref_div * post_div, - pll->reference_freq * 10000); + while (min_feed_div < max_feed_div) { + uint32_t vco; - if (feedback_div < pll->min_feedback_div || feedback_div > pll->max_feedback_div) - continue; + feedback_div = (min_feed_div+max_feed_div)/2; + + vco = RADEONDiv((CARD64)pll->reference_freq * feedback_div, + ref_div); - 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) || - (ref_div == pll->reference_div) || - (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; + if (vco < pll->pll_out_min) { + min_feed_div = feedback_div+1; + continue; + } else if(vco > pll->pll_out_max) { + max_feed_div = feedback_div; + 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)) { + 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; + } + } + + if (current_freq < freq) + min_feed_div = feedback_div+1; + else + max_feed_div = feedback_div; } } - if (!(flags & RADEON_PLL_DCE3)) { - if (best_freq == freq) - break; - } } ErrorF("best_freq: %u\n", (unsigned int)best_freq); @@ -207,6 +244,8 @@ RADEONComputePLL(RADEONPLLPtr pll, ErrorF("best_ref_div: %u\n", (unsigned int)best_ref_div); ErrorF("best_post_div: %u\n", (unsigned int)best_post_div); + if (best_freq == -1) + FatalError("Couldn't find valid PLL dividers\n"); *chosen_dot_clock_freq = best_freq / 10000; *chosen_feedback_div = best_feedback_div; *chosen_reference_div = best_ref_div; |