xf86-video-ati: Branch 'master'

Alex Deucher agd5f at kemper.freedesktop.org
Tue Dec 11 08:58:55 PST 2007


 src/radeon.h        |   10 ++
 src/radeon_crtc.c   |  197 ++++++++++++++++++++++++++++++++++++----------------
 src/radeon_driver.c |    9 ++
 3 files changed, 157 insertions(+), 59 deletions(-)

New commits:
commit f3d2ec3a5ae61215c792018320158750e7aa937c
Author: Alex Deucher <alex at botch2.(none)>
Date:   Tue Dec 11 11:57:27 2007 -0500

    RADEON: rewrite PLL computation
    
    Algorithm adapted from BeOS radeon driver with some tweaks by me.
    Actually calulate and use the reference divider rather than using the bios default.
    Also, always calculate the PLL, rather than falling back to bios dividers.
    This should fix bugs 12913, 13590, 13533, possibly others.

diff --git a/src/radeon.h b/src/radeon.h
index fe491e8..b7f9288 100644
--- a/src/radeon.h
+++ b/src/radeon.h
@@ -288,6 +288,7 @@ typedef struct {
     CARD32            dot_clock_freq;
     CARD32            pll_output_freq;
     int               feedback_div;
+    int               reference_div;
     int               post_div;
 
 				/* PLL registers */
@@ -300,6 +301,7 @@ typedef struct {
     CARD32            dot_clock_freq_2;
     CARD32            pll_output_freq_2;
     int               feedback_div_2;
+    int               reference_div_2;
     int               post_div_2;
 
 				/* PLL2 registers */
@@ -370,6 +372,14 @@ typedef struct {
     CARD32            min_pll_freq;
     CARD32            max_pll_freq;
     CARD16            xclk;
+
+    CARD32            min_ref_div;
+    CARD32            max_ref_div;
+    CARD32            min_feedback_div;
+    CARD32            max_feedback_div;
+    CARD32            pll_in_min;
+    CARD32            pll_in_max;
+    CARD32            best_vco;
 } RADEONPLLRec, *RADEONPLLPtr;
 
 typedef struct {
diff --git a/src/radeon_crtc.c b/src/radeon_crtc.c
index b1d216d..8984428 100644
--- a/src/radeon_crtc.c
+++ b/src/radeon_crtc.c
@@ -612,13 +612,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;
@@ -640,21 +723,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) {
@@ -662,20 +744,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??? */
@@ -685,21 +767,23 @@ 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;
+    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;
@@ -720,18 +804,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) {
@@ -739,27 +816,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);
 
 }
 
@@ -837,25 +916,25 @@ radeon_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;
     }
 
diff --git a/src/radeon_driver.c b/src/radeon_driver.c
index 50f78eb..4b5008e 100644
--- a/src/radeon_driver.c
+++ b/src/radeon_driver.c
@@ -1063,6 +1063,15 @@ static void RADEONGetClockInfo(ScrnInfoPtr pScrn)
                                info->RamWidth / 16);
     }
 
+    /* card limits for computing PLLs */
+    pll->min_ref_div = 2;
+    pll->max_ref_div = 0x3ff;
+    pll->min_feedback_div = 4;
+    pll->max_feedback_div = 0x7ff;
+    pll->pll_in_min = 40;
+    pll->pll_in_max = 100;
+    pll->best_vco = 0;
+
     xf86DrvMsg (pScrn->scrnIndex, X_INFO,
 		"PLL parameters: rf=%u rd=%u min=%u max=%u; xclk=%u\n",
 		pll->reference_freq,


More information about the xorg-commit mailing list