From 72feaa37ea07620f5f2ead438dbc72a1c8883cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pale=C4=8Dek?= Date: Mon, 23 Jun 2008 15:53:58 -0400 Subject: RADEON: PLL tweaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/atombios_crtc.c | 3 +- src/legacy_crtc.c | 2 +- src/radeon.h | 2 +- src/radeon_crtc.c | 99 +++++++++++++++++++++++++++++++++++++---------------- 4 files changed, 72 insertions(+), 34 deletions(-) diff --git a/src/atombios_crtc.c b/src/atombios_crtc.c index b5b7ca8c..363addfe 100644 --- a/src/atombios_crtc.c +++ b/src/atombios_crtc.c @@ -185,8 +185,7 @@ atombios_crtc_set_pll(xf86CrtcPtr crtc, DisplayModePtr mode, int pll_flags) if (IS_AVIVO_VARIANT) { uint32_t temp; - if (IS_DCE3_VARIANT) - pll_flags |= RADEON_PLL_DCE3; + pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV; RADEONComputePLL(&info->pll, mode->Clock, &temp, &fb_div, &ref_div, &post_div, pll_flags); sclock = temp; diff --git a/src/legacy_crtc.c b/src/legacy_crtc.c index 747bc6ed..17ae8c44 100644 --- a/src/legacy_crtc.c +++ b/src/legacy_crtc.c @@ -1730,7 +1730,7 @@ legacy_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, RADEONInfoPtr info = RADEONPTR(pScrn); int i = 0; double dot_clock = 0; - int pll_flags = RADEON_PLL_LEGACY; + int pll_flags = RADEON_PLL_LEGACY | RADEON_PLL_PREFER_LOW_REF_DIV; Bool update_tv_routing = FALSE; Bool tilingChanged = FALSE; diff --git a/src/radeon.h b/src/radeon.h index cdd84ea7..4f77c3b9 100644 --- a/src/radeon.h +++ b/src/radeon.h @@ -220,7 +220,7 @@ typedef struct { #define RADEON_PLL_NO_ODD_POST_DIV (1 << 1) #define RADEON_PLL_USE_REF_DIV (1 << 2) #define RADEON_PLL_LEGACY (1 << 3) -#define RADEON_PLL_DCE3 (1 << 4) +#define RADEON_PLL_PREFER_LOW_REF_DIV (1 << 4) typedef struct { uint16_t reference_freq; 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; -- cgit v1.2.3