summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Deucher <alexdeucher@gmail.com>2009-04-22 11:42:18 -0400
committerAlex Deucher <alexdeucher@gmail.com>2009-04-22 11:42:18 -0400
commit24e4b73b4fbbb2c790e6120ede3caaa4e7e58359 (patch)
tree65d7192bce5b1a3f19464a693db48f2d11e58ba0
parentefa0825a86a8dc0f03ebb42c576ed26189e9d4bb (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.
-rw-r--r--src/atombios_crtc.c11
-rw-r--r--src/legacy_crtc.c6
-rw-r--r--src/radeon.h3
-rw-r--r--src/radeon_crtc.c84
-rw-r--r--src/radeon_driver.c4
5 files changed, 69 insertions, 39 deletions
diff --git a/src/atombios_crtc.c b/src/atombios_crtc.c
index 31c032ba..01266b65 100644
--- a/src/atombios_crtc.c
+++ b/src/atombios_crtc.c
@@ -259,7 +259,7 @@ atombios_crtc_set_pll(xf86CrtcPtr crtc, DisplayModePtr mode)
unsigned char *RADEONMMIO = info->MMIO;
int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock);
uint32_t sclock = mode->Clock;
- uint32_t ref_div = 0, fb_div = 0, post_div = 0;
+ uint32_t ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0;
int major, minor, i;
SET_PIXEL_CLOCK_PS_ALLOCATION spc_param;
PIXEL_CLOCK_PARAMETERS_V2 *spc2_ptr;
@@ -311,15 +311,16 @@ atombios_crtc_set_pll(xf86CrtcPtr crtc, DisplayModePtr mode)
pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
}
- RADEONComputePLL(&info->pll, mode->Clock, &temp, &fb_div, &ref_div, &post_div, pll_flags);
+ RADEONComputePLL(&info->pll, mode->Clock, &temp, &fb_div, &frac_fb_div, &ref_div, &post_div, pll_flags);
sclock = temp;
xf86DrvMsg(crtc->scrn->scrnIndex, X_INFO,
"crtc(%d) Clock: mode %d, PLL %lu\n",
radeon_crtc->crtc_id, mode->Clock, (long unsigned int)sclock * 10);
xf86DrvMsg(crtc->scrn->scrnIndex, X_INFO,
- "crtc(%d) PLL : refdiv %u, fbdiv 0x%X(%u), pdiv %u\n",
- radeon_crtc->crtc_id, (unsigned int)ref_div, (unsigned int)fb_div, (unsigned int)fb_div, (unsigned int)post_div);
+ "crtc(%d) PLL : refdiv %u, fbdiv 0x%X(%u), fracfbdiv %u, pdiv %u\n",
+ radeon_crtc->crtc_id, (unsigned int)ref_div, (unsigned int)fb_div,
+ (unsigned int)fb_div, (unsigned int)frac_fb_div, (unsigned int)post_div);
/* Can't really do cloning easily on DCE3 cards */
for (i = 0; i < xf86_config->num_output; i++) {
@@ -353,6 +354,7 @@ atombios_crtc_set_pll(xf86CrtcPtr crtc, DisplayModePtr mode)
spc2_ptr->usPixelClock = cpu_to_le16(sclock);
spc2_ptr->usRefDiv = cpu_to_le16(ref_div);
spc2_ptr->usFbDiv = cpu_to_le16(fb_div);
+ spc2_ptr->ucFracFbDiv = frac_fb_div;
spc2_ptr->ucPostDiv = post_div;
spc2_ptr->ucPpll = radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
spc2_ptr->ucCRTC = radeon_crtc->crtc_id;
@@ -364,6 +366,7 @@ atombios_crtc_set_pll(xf86CrtcPtr crtc, DisplayModePtr mode)
spc3_ptr->usPixelClock = cpu_to_le16(sclock);
spc3_ptr->usRefDiv = cpu_to_le16(ref_div);
spc3_ptr->usFbDiv = cpu_to_le16(fb_div);
+ spc3_ptr->ucFracFbDiv = frac_fb_div;
spc3_ptr->ucPostDiv = post_div;
spc3_ptr->ucPpll = radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
spc3_ptr->ucMiscInfo = (radeon_crtc->crtc_id << 2);
diff --git a/src/legacy_crtc.c b/src/legacy_crtc.c
index 3759f05b..7a3a920a 100644
--- a/src/legacy_crtc.c
+++ b/src/legacy_crtc.c
@@ -1193,6 +1193,7 @@ RADEONInitPLLRegisters(ScrnInfoPtr pScrn, RADEONSavePtr save,
{
RADEONInfoPtr info = RADEONPTR(pScrn);
uint32_t feedback_div = 0;
+ uint32_t frac_fb_div = 0;
uint32_t reference_div = 0;
uint32_t post_divider = 0;
uint32_t freq = 0;
@@ -1225,7 +1226,7 @@ RADEONInitPLLRegisters(ScrnInfoPtr pScrn, RADEONSavePtr save,
return;
}
- RADEONComputePLL(pll, mode->Clock, &freq, &feedback_div, &reference_div, &post_divider, flags);
+ RADEONComputePLL(pll, mode->Clock, &freq, &feedback_div, &frac_fb_div, &reference_div, &post_divider, flags);
for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
if (post_div->divider == post_divider)
@@ -1274,6 +1275,7 @@ RADEONInitPLL2Registers(ScrnInfoPtr pScrn, RADEONSavePtr save,
{
RADEONInfoPtr info = RADEONPTR(pScrn);
uint32_t feedback_div = 0;
+ uint32_t frac_fb_div = 0;
uint32_t reference_div = 0;
uint32_t post_divider = 0;
uint32_t freq = 0;
@@ -1304,7 +1306,7 @@ RADEONInitPLL2Registers(ScrnInfoPtr pScrn, RADEONSavePtr save,
return;
}
- RADEONComputePLL(pll, mode->Clock, &freq, &feedback_div, &reference_div, &post_divider, flags);
+ RADEONComputePLL(pll, mode->Clock, &freq, &feedback_div, &frac_fb_div, &reference_div, &post_divider, flags);
for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
if (post_div->divider == post_divider)
diff --git a/src/radeon.h b/src/radeon.h
index 174352d3..eaaff250 100644
--- a/src/radeon.h
+++ b/src/radeon.h
@@ -277,6 +277,8 @@ typedef struct {
uint32_t max_post_div;
uint32_t min_feedback_div;
uint32_t max_feedback_div;
+ uint32_t min_frac_feedback_div;
+ uint32_t max_frac_feedback_div;
uint32_t best_vco;
} RADEONPLLRec, *RADEONPLLPtr;
@@ -1049,6 +1051,7 @@ extern void RADEONBlank(ScrnInfoPtr pScrn);
extern 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);
extern DisplayModePtr RADEONCrtcFindClosestMode(xf86CrtcPtr crtc,
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;
diff --git a/src/radeon_driver.c b/src/radeon_driver.c
index db5a0e38..9cbfd0d1 100644
--- a/src/radeon_driver.c
+++ b/src/radeon_driver.c
@@ -1206,9 +1206,13 @@ static void RADEONGetClockInfo(ScrnInfoPtr pScrn)
if (IS_AVIVO_VARIANT) {
pll->min_post_div = 2;
pll->max_post_div = 0x7f;
+ pll->min_frac_feedback_div = 0;
+ pll->max_frac_feedback_div = 9;
} else {
pll->min_post_div = 1;
pll->max_post_div = 12; //16 on crtc0
+ pll->min_frac_feedback_div = 0;
+ pll->max_frac_feedback_div = 0;
}
pll->min_ref_div = 2;
pll->max_ref_div = 0x3ff;