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 | |
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')
-rw-r--r-- | src/atombios_crtc.c | 11 | ||||
-rw-r--r-- | src/legacy_crtc.c | 6 | ||||
-rw-r--r-- | src/radeon.h | 3 | ||||
-rw-r--r-- | src/radeon_crtc.c | 84 | ||||
-rw-r--r-- | src/radeon_driver.c | 4 |
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; |