summaryrefslogtreecommitdiff
path: root/src/radeon_crtc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/radeon_crtc.c')
-rw-r--r--src/radeon_crtc.c193
1 files changed, 135 insertions, 58 deletions
diff --git a/src/radeon_crtc.c b/src/radeon_crtc.c
index 1ea6d2b7..fa2aba03 100644
--- a/src/radeon_crtc.c
+++ b/src/radeon_crtc.c
@@ -626,13 +626,96 @@ static int RADEONDiv(int n, int d)
return (n + (d / 2)) / d;
}
+static CARD32 RADEONDiv64(CARD64 n, CARD32 d)
+{
+ return (n + (d / 2)) / d;
+}
+
+static void
+RADEONComputePLL(RADEONPLLPtr pll,
+ unsigned long freq,
+ CARD32 *chosen_dot_clock_freq,
+ CARD32 *chosen_feedback_div,
+ CARD32 *chosen_reference_div,
+ CARD32 *chosen_post_div)
+{
+ int post_divs[] = {1, 2, 4, 8, 3, 6, 12, 0};
+
+ int i;
+
+ CARD32 best_vco = pll->best_vco;
+ CARD32 best_post_div = 1;
+ CARD32 best_ref_div = 1;
+ CARD32 best_feedback_div = 1;
+ CARD32 best_freq = 1;
+ CARD32 best_error = 0xffffffff;
+ CARD32 best_vco_diff = 1;
+
+ ErrorF("freq: %d\n", freq);
+
+ for (i = 0; post_divs[i]; i++) {
+ int post_div = post_divs[i];
+ CARD32 ref_div;
+ CARD32 vco = (freq / 10000) * post_div;
+
+ if (vco < pll->min_pll_freq || vco > pll->max_pll_freq)
+ continue;
+
+ for (ref_div = pll->min_ref_div; ref_div <= pll->max_ref_div; ++ref_div) {
+ CARD32 feedback_div, current_freq, error, vco_diff;
+ CARD32 pll_in = pll->reference_freq / ref_div;
+
+ if (pll_in < pll->pll_in_min || pll_in > pll->pll_in_max)
+ continue;
+
+ feedback_div = RADEONDiv64((CARD64)freq * ref_div * post_div,
+ pll->reference_freq * 10000);
+
+ if (feedback_div < pll->min_feedback_div || feedback_div > pll->max_feedback_div)
+ continue;
+
+ current_freq = RADEONDiv64((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;
+ }
+ }
+ }
+
+ ErrorF("best_freq: %d\n", best_freq);
+ ErrorF("best_feedback_div: %d\n", best_feedback_div);
+ ErrorF("best_ref_div: %d\n", best_ref_div);
+ ErrorF("best_post_div: %d\n", best_post_div);
+
+ *chosen_dot_clock_freq = best_freq;
+ *chosen_feedback_div = best_feedback_div;
+ *chosen_reference_div = best_ref_div;
+ *chosen_post_div = best_post_div;
+
+}
+
/* Define PLL registers for requested video mode */
static void
-RADEONInitPLLRegisters(ScrnInfoPtr pScrn, RADEONInfoPtr info,
- RADEONSavePtr save, RADEONPLLPtr pll,
- double dot_clock)
+RADEONInitPLLRegisters(ScrnInfoPtr pScrn, RADEONSavePtr save,
+ RADEONPLLPtr pll, DisplayModePtr mode)
{
- unsigned long freq = dot_clock * 100;
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ CARD32 feedback_div = 0;
+ CARD32 reference_div = 0;
+ CARD32 post_divider = 0;
+ CARD32 freq = 0;
struct {
int divider;
@@ -654,21 +737,20 @@ RADEONInitPLLRegisters(ScrnInfoPtr pScrn, RADEONInfoPtr info,
{ 0, 0 }
};
+ RADEONComputePLL(pll, mode->Clock * 1000, &freq, &feedback_div, &reference_div, &post_divider);
+
+#if 0
if (info->UseBiosDividers) {
save->ppll_ref_div = info->RefDivider;
save->ppll_div_3 = info->FeedbackDivider | (info->PostDivider << 16);
save->htotal_cntl = 0;
return;
}
-
- if (freq > pll->max_pll_freq) freq = pll->max_pll_freq;
- if (freq * 12 < pll->min_pll_freq) freq = pll->min_pll_freq / 12;
+#endif
for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
- save->pll_output_freq = post_div->divider * freq;
-
- if (save->pll_output_freq >= pll->min_pll_freq
- && save->pll_output_freq <= pll->max_pll_freq) break;
+ if (post_div->divider == post_divider)
+ break;
}
if (!post_div->divider) {
@@ -676,20 +758,20 @@ RADEONInitPLLRegisters(ScrnInfoPtr pScrn, RADEONInfoPtr info,
post_div = &post_divs[0];
}
- save->dot_clock_freq = freq;
- save->feedback_div = RADEONDiv(pll->reference_div
- * save->pll_output_freq,
- pll->reference_freq);
- save->post_div = post_div->divider;
+ save->dot_clock_freq = freq / 10000;
+ save->feedback_div = feedback_div;
+ save->reference_div = reference_div;
+ save->post_div = post_divider;
xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG,
- "dc=%u, of=%u, fd=%d, pd=%d\n",
+ "dc=%u, of=%u, fd=%d, rd=%d, pd=%d\n",
(unsigned)save->dot_clock_freq,
(unsigned)save->pll_output_freq,
save->feedback_div,
+ save->reference_div,
save->post_div);
- save->ppll_ref_div = pll->reference_div;
+ save->ppll_ref_div = save->reference_div;
#if defined(__powerpc__)
/* apparently programming this otherwise causes a hang??? */
@@ -699,21 +781,22 @@ RADEONInitPLLRegisters(ScrnInfoPtr pScrn, RADEONInfoPtr info,
#endif
save->ppll_div_3 = (save->feedback_div | (post_div->bitvalue << 16));
- save->htotal_cntl = 0;
+ save->htotal_cntl = mode->HTotal & 0x7;
save->vclk_ecp_cntl = (info->SavedReg->vclk_ecp_cntl &
~RADEON_VCLK_SRC_SEL_MASK) | RADEON_VCLK_SRC_SEL_PPLLCLK;
-
}
/* Define PLL2 registers for requested video mode */
static void
RADEONInitPLL2Registers(ScrnInfoPtr pScrn, RADEONSavePtr save,
- RADEONPLLPtr pll, double dot_clock,
- int no_odd_postdiv)
+ RADEONPLLPtr pll, DisplayModePtr mode)
{
- RADEONInfoPtr info = RADEONPTR(pScrn);
- unsigned long freq = dot_clock * 100;
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ CARD32 feedback_div = 0;
+ CARD32 reference_div = 0;
+ CARD32 post_divider = 0;
+ CARD32 freq = 0;
struct {
int divider;
@@ -734,18 +817,11 @@ RADEONInitPLL2Registers(ScrnInfoPtr pScrn, RADEONSavePtr save,
{ 0, 0 }
};
- if (freq > pll->max_pll_freq) freq = pll->max_pll_freq;
- if (freq * 12 < pll->min_pll_freq) freq = pll->min_pll_freq / 12;
+ RADEONComputePLL(pll, mode->Clock * 1000, &freq, &feedback_div, &reference_div, &post_divider);
for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
- /* Odd post divider value don't work properly on the second digital
- * output
- */
- if (no_odd_postdiv && (post_div->divider & 1))
- continue;
- save->pll_output_freq_2 = post_div->divider * freq;
- if (save->pll_output_freq_2 >= pll->min_pll_freq
- && save->pll_output_freq_2 <= pll->max_pll_freq) break;
+ if (post_div->divider == post_divider)
+ break;
}
if (!post_div->divider) {
@@ -753,28 +829,29 @@ RADEONInitPLL2Registers(ScrnInfoPtr pScrn, RADEONSavePtr save,
post_div = &post_divs[0];
}
- save->dot_clock_freq_2 = freq;
- save->feedback_div_2 = RADEONDiv(pll->reference_div
- * save->pll_output_freq_2,
- pll->reference_freq);
- save->post_div_2 = post_div->divider;
+ save->dot_clock_freq_2 = freq / 10000;
+ save->feedback_div_2 = feedback_div;
+ save->reference_div_2 = reference_div;
+ save->post_div_2 = post_divider;
xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG,
- "dc=%u, of=%u, fd=%d, pd=%d\n",
+ "dc=%u, of=%u, fd=%d, rd=%d, pd=%d\n",
(unsigned)save->dot_clock_freq_2,
(unsigned)save->pll_output_freq_2,
save->feedback_div_2,
+ save->reference_div_2,
save->post_div_2);
- save->p2pll_ref_div = pll->reference_div;
+ save->p2pll_ref_div = save->reference_div_2;
+
save->p2pll_div_0 = (save->feedback_div_2 |
(post_div->bitvalue << 16));
- save->htotal_cntl2 = 0;
- save->pixclks_cntl = ((info->SavedReg->pixclks_cntl &
- ~(RADEON_PIX2CLK_SRC_SEL_MASK)) |
- RADEON_PIX2CLK_SRC_SEL_P2PLLCLK);
+ save->htotal_cntl2 = mode->HTotal & 0x7;
+ save->pixclks_cntl = ((info->SavedReg->pixclks_cntl &
+ ~(RADEON_PIX2CLK_SRC_SEL_MASK)) |
+ RADEON_PIX2CLK_SRC_SEL_P2PLLCLK);
}
static void
@@ -851,25 +928,25 @@ legacy_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode,
ErrorF("init crtc1\n");
RADEONInitCrtcRegisters(crtc, info->ModeReg, adjusted_mode);
RADEONInitCrtcBase(crtc, info->ModeReg, x, y);
- dot_clock = adjusted_mode->Clock / 1000.0;
- if (dot_clock) {
+ dot_clock = adjusted_mode->Clock / 1000.0;
+ if (dot_clock) {
ErrorF("init pll1\n");
- RADEONInitPLLRegisters(pScrn, info, info->ModeReg, &info->pll, dot_clock);
- } else {
- info->ModeReg->ppll_ref_div = info->SavedReg->ppll_ref_div;
- info->ModeReg->ppll_div_3 = info->SavedReg->ppll_div_3;
- info->ModeReg->htotal_cntl = info->SavedReg->htotal_cntl;
- }
+ RADEONInitPLLRegisters(pScrn, info->ModeReg, &info->pll, adjusted_mode);
+ } else {
+ info->ModeReg->ppll_ref_div = info->SavedReg->ppll_ref_div;
+ info->ModeReg->ppll_div_3 = info->SavedReg->ppll_div_3;
+ info->ModeReg->htotal_cntl = info->SavedReg->htotal_cntl;
+ }
break;
case 1:
ErrorF("init crtc2\n");
- RADEONInitCrtc2Registers(crtc, info->ModeReg, adjusted_mode);
+ RADEONInitCrtc2Registers(crtc, info->ModeReg, adjusted_mode);
RADEONInitCrtc2Base(crtc, info->ModeReg, x, y);
- dot_clock = adjusted_mode->Clock / 1000.0;
- if (dot_clock) {
+ dot_clock = adjusted_mode->Clock / 1000.0;
+ if (dot_clock) {
ErrorF("init pll2\n");
- RADEONInitPLL2Registers(pScrn, info->ModeReg, &info->pll, dot_clock, no_odd_post_div);
- }
+ RADEONInitPLL2Registers(pScrn, info->ModeReg, &info->pll, adjusted_mode);
+ }
break;
}