summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiří Paleček <jpalecek@web.de>2008-06-23 15:53:58 -0400
committerAlex Deucher <alex@botch2.com>2008-06-23 15:53:58 -0400
commit72feaa37ea07620f5f2ead438dbc72a1c8883cd3 (patch)
tree239935051ab53d1e6a3d2e871626ae9c86399d71
parent9c2f909ea437a63a408d2398ecabe0b378dbb982 (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.
-rw-r--r--src/atombios_crtc.c3
-rw-r--r--src/legacy_crtc.c2
-rw-r--r--src/radeon.h2
-rw-r--r--src/radeon_crtc.c99
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;