[PATCH] Radeon 3/11 : Fix PLL probing

Benjamin Herrenschmidt benh at kernel.crashing.org
Sun Dec 5 01:59:37 PST 2004


This patch fix the routine that probes for PLL values in absence of a
BIOS ROM. The measure is more precise, done several times to avoid
"gliches" caused by scheduling latencies, plus the patch fixes actual
bugs in the previous iteration of the code. It also add calculation of
the mclk and sclk values for proper display bandwidth calculation and
adds proper min/max PLL values for r420 type cards.

Index: xc/programs/Xserver/hw/xfree86/drivers/ati/radeon_driver.c
===================================================================
--- xc.orig/programs/Xserver/hw/xfree86/drivers/ati/radeon_driver.c	2004-12-05 17:13:38.156123888 +1100
+++ xc/programs/Xserver/hw/xfree86/drivers/ati/radeon_driver.c	2004-12-05 19:41:48.182971616 +1100
@@ -1199,41 +1199,59 @@
     return(bConnected ? MT_CRT : MT_NONE);
 }
 
-#if defined(__powerpc__)
 static Bool RADEONProbePLLParameters(ScrnInfoPtr pScrn)
 {
     RADEONInfoPtr info = RADEONPTR(pScrn);
     RADEONPLLPtr  pll  = &info->pll;
     unsigned char *RADEONMMIO = info->MMIO;
     unsigned char ppll_div_sel;
-    unsigned Nx, M;
+    unsigned mpll_fb_div, spll_fb_div, M;
     unsigned xclk, tmp, ref_div;
     int hTotal, vTotal, num, denom, m, n;
-    float hz, vclk, xtal;
+    float hz, prev_xtal, vclk, xtal, mpll, spll;
     long start_secs, start_usecs, stop_secs, stop_usecs, total_usecs;
-    int i;
-
-    for(i=0; i<1000000; i++)
-	if (((INREG(RADEON_CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) == 0)
+    long to1_secs, to1_usecs, to2_secs, to2_usecs;
+    unsigned int f1, f2, f3;
+    int i, tries = 0;
+
+    prev_xtal = 0;
+ again:
+    xtal = 0;
+    if (++tries > 10)
+	    goto failed;
+
+    xf86getsecs(&to1_secs, &to1_usecs);
+    f1 = INREG(RADEON_CRTC_CRNT_FRAME);
+    for (;;) {
+	f2 = INREG(RADEON_CRTC_CRNT_FRAME);
+	if (f1 != f2)
 	    break;
-
+	xf86getsecs(&to2_secs, &to2_usecs);
+	if ((to2_secs - to1_secs) > 1) {
+	    xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Clock not counting...\n");
+	    goto failed;
+	}
+    }
     xf86getsecs(&start_secs, &start_usecs);
-
-    for(i=0; i<1000000; i++)
-	if (((INREG(RADEON_CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) != 0)
+    for(;;) {
+	f3 = INREG(RADEON_CRTC_CRNT_FRAME);
+	if (f3 != f2)
 	    break;
-
-    for(i=0; i<1000000; i++)
-	if (((INREG(RADEON_CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) == 0)
-	    break;
-
+	xf86getsecs(&to2_secs, &to2_usecs);
+	if ((to2_secs - start_secs) > 1)
+	    goto failed;
+    }
     xf86getsecs(&stop_secs, &stop_usecs);
 
+    if ((stop_secs - start_secs) != 0)
+	    goto again;
     total_usecs = abs(stop_usecs - start_usecs);
-    hz = 1000000/total_usecs;
+    if (total_usecs == 0)
+	    goto again;
+    hz = 1000000.0/(float)total_usecs;
 
-    hTotal = ((INREG(RADEON_CRTC_H_TOTAL_DISP) & 0x1ff) + 1) * 8;
-    vTotal = ((INREG(RADEON_CRTC_V_TOTAL_DISP) & 0x3ff) + 1);
+    hTotal = ((INREG(RADEON_CRTC_H_TOTAL_DISP) & 0x3ff) + 1) * 8;
+    vTotal = ((INREG(RADEON_CRTC_V_TOTAL_DISP) & 0xfff) + 1);
     vclk = (float)(hTotal * (float)(vTotal * hz));
 
     switch((INPLL(pScrn, RADEON_PPLL_REF_DIV) & 0x30000) >> 16) {
@@ -1295,23 +1313,94 @@
     else if ((xtal > 29400000) && (xtal < 29600000))
         xtal = 2950;
     else
-	return FALSE;
+	goto again;
+ failed:
+    if (xtal == 0) {
+	xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Failed to probe xtal value ! "
+		   "Using default 27Mhz\n");
+	xtal = 2700;
+    } else {
+	if (prev_xtal == 0) {
+	    prev_xtal = xtal;
+	    tries = 0;
+	    goto again;
+	} else if (prev_xtal != xtal) {
+	    prev_xtal = 0;
+	    goto again;
+	}
+    }
 
     tmp = INPLL(pScrn, RADEON_X_MPLL_REF_FB_DIV);
     ref_div = INPLL(pScrn, RADEON_PPLL_REF_DIV) & 0x3ff;
 
-    Nx = (tmp & 0xff00) >> 8;
+    /* Some sanity check based on the BIOS code .... */
+    if (ref_div < 2) {
+	CARD32 tmp;
+	tmp = INPLL(pScrn, RADEON_PPLL_REF_DIV);
+	if (IS_R300_VARIANT || (info->ChipFamily == CHIP_FAMILY_RS300))
+	    ref_div = (tmp & R300_PPLL_REF_DIV_ACC_MASK) >>
+		    R300_PPLL_REF_DIV_ACC_SHIFT;
+	else
+	    ref_div = tmp & RADEON_PPLL_REF_DIV_MASK;
+	if (ref_div < 2)
+	    ref_div = 12;
+    }
+
+    /* Calculate "base" xclk straight from MPLL, though that isn't
+     * really useful (hopefully)
+     */
+    mpll_fb_div = (tmp & 0xff00) >> 8;
+    spll_fb_div = (tmp & 0xff0000) >> 16;
     M = (tmp & 0xff);
-    xclk = RADEONDiv((2 * Nx * xtal), (2 * M));
+    xclk = RADEONDiv((2 * mpll_fb_div * xtal), (2 * M));
+
+    /*
+     * Calculate MCLK based on MCLK-A and SCLK
+     *
+     * NOTE: It is not clear at this point wether we should put in sclk and
+     * mclk the raw SPLL and MPLL output values, or the divided values according
+     * to the source selection iN MCLK_CNTL and SCLK_CNTL. I'm putting the divided
+     * values for now, waiting for a definitive answer from ATI
+     */
+    mpll = ((float)mpll_fb_div * (float)(xtal / 100.0)) / (float)M;
+    spll = ((float)spll_fb_div * (float)(xtal / 100.0)) / (float)M;
+
+    tmp = INPLL(pScrn, RADEON_MCLK_CNTL) & 0x7;
+    switch(tmp) {
+    case 1: info->mclk = mpll; break;
+    case 2: info->mclk = mpll / 2.0; break;
+    case 3: info->mclk = mpll / 4.0; break;
+    case 4: info->mclk = mpll / 8.0; break;
+    case 7: info->mclk = spll; break;
+    default:
+	    info->mclk = 200.00;
+	    xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Unsupported MCLKA source"
+		       " setting %d, can't probe MCLK value !\n", tmp);
+    }
+
+    tmp = INPLL(pScrn, RADEON_SCLK_CNTL) & 0x7;
+    switch(tmp) {
+    case 1: info->sclk = spll; break;
+    case 2: info->sclk = spll / 2.0; break;
+    case 3: info->sclk = spll / 4.0; break;
+    case 4: info->sclk = spll / 8.0; break;
+    case 7: info->sclk = mpll;
+    default:
+	    info->sclk = 200.00;
+	    xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Unsupported SCLK source"
+		       " setting %d, can't probe SCLK value !\n", tmp);
+    }
 
     /* we're done, hopefully these are sane values */
     pll->reference_div = ref_div;
     pll->xclk = xclk;
     pll->reference_freq = xtal;
 
+    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Probed PLL values: xtal: %f Mhz, "
+	       "sclk: %f Mhz, mclk: %f Mhz\n", xtal/100.0, info->sclk, info->mclk);
+
     return TRUE;
 }
-#endif
 
 static void RADEONGetPanelInfoFromReg (ScrnInfoPtr pScrn)
 {
@@ -1463,17 +1552,24 @@
 	xf86DrvMsg (pScrn->scrnIndex, X_WARNING,
 		    "Video BIOS not detected, using default clock settings!\n");
 
-#if defined(__powerpc__)
-	if (RADEONProbePLLParameters(pScrn)) return;
-#endif
+	/* Default min/max PLL values */
+	if (info->ChipFamily == CHIP_FAMILY_R420) {
+	    pll->min_pll_freq = 20000;
+	    pll->max_pll_freq = 50000;
+	} else {
+	    pll->min_pll_freq = 12500;
+	    pll->max_pll_freq = 35000;
+	}
+
+	if (RADEONProbePLLParameters(pScrn))
+            return;
+
 	if (info->IsIGP)
 	    pll->reference_freq = 1432;
 	else
 	    pll->reference_freq = 2700;
 
 	pll->reference_div = 12;
-	pll->min_pll_freq = 12500;
-	pll->max_pll_freq = 35000;
 	pll->xclk = 10300;
 
         info->sclk = 200.00;





More information about the xorg mailing list