[PATCH] Radeon 10/11 : Add LVDSProbePLL option

Benjamin Herrenschmidt benh at kernel.crashing.org
Sun Dec 5 02:00:09 PST 2004


This patch adds an option for probing the PLL value at server init time
for LVDS panels and re-using it later (by setting UseBiosDividers). It's
useful on machines without an X86 BIOS image providing the proper set of
divider values for the LVDS, as the value calculated by RADEONInitPLLRegisters()
tend not to be suitable for some LVDS panels.

It also changes a bit the way the panel infos are extracted, the previous
code didn't quite work for me, and after discussing with Hui, I decided
to move the detection earlier in the discovery process and to do it slightly
differently.

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 20:49:57.183349072 +1100
+++ xc/programs/Xserver/hw/xfree86/drivers/ati/radeon_driver.c	2004-12-05 20:49:58.439158160 +1100
@@ -118,6 +118,7 @@
 static void RADEONGetMergedFBOptions(ScrnInfoPtr pScrn);
 static int RADEONValidateMergeModes(ScrnInfoPtr pScrn);
 static void RADEONSetDynamicClock(ScrnInfoPtr pScrn, int mode);
+static void RADEONUpdatePanelSize(ScrnInfoPtr pScrn);
 
 /* psuedo xinerama support */
 
@@ -173,6 +174,7 @@
     OPTION_SHOWCACHE,
     OPTION_DYNAMIC_CLOCKS,
     OPTION_VGA_ACCESS,
+    OPTION_LVDS_PROBE_PLL,
     OPTION_REVERSE_DDC,
 } RADEONOpts;
 
@@ -226,6 +228,7 @@
     { OPTION_SHOWCACHE,      "ShowCache",        OPTV_BOOLEAN, {0}, FALSE },
     { OPTION_DYNAMIC_CLOCKS, "DynamicClocks",    OPTV_BOOLEAN, {0}, FALSE },
     { OPTION_VGA_ACCESS,     "VGAAccess",        OPTV_BOOLEAN, {0}, TRUE  },
+    { OPTION_LVDS_PROBE_PLL, "LVDSProbePLL",     OPTV_BOOLEAN, {0}, FALSE },
     { OPTION_REVERSE_DDC,    "ReverseDDC",       OPTV_BOOLEAN, {0}, FALSE },
     { -1,                    NULL,               OPTV_NONE,    {0}, FALSE }
 };
@@ -1427,6 +1430,24 @@
 	info->PanelXRes = 640;
 	info->PanelYRes = 480;
     }
+
+    if (xf86ReturnOptValBool(info->Options, OPTION_LVDS_PROBE_PLL, TRUE)) {
+	    CARD32 ppll_div_sel, ppll_val;
+
+	    OUTREG(RADEON_CLOCK_CNTL_INDEX, 1);
+	    ppll_div_sel = INREG8(RADEON_CLOCK_CNTL_DATA + 1) & 0x3;
+            ppll_val = INPLL(pScrn, RADEON_PPLL_DIV_0 + ppll_div_sel);
+	    if ((ppll_val & 0x000707ff) == 0x1bb)
+		goto noprobe;
+            info->FeedbackDivider = ppll_val & 0x7ff;
+            info->PostDivider = (ppll_val >> 16) & 0x7;
+	    info->RefDivider = info->pll.reference_div;
+            info->UseBiosDividers = TRUE;
+
+	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		       "Existing panel PLL dividers will be used.\n");
+    }
+ noprobe:
     
     xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 
 	       "Panel size %dx%d is derived, this may not be correct.\n"
@@ -1441,17 +1462,24 @@
     if (!RADEONGetLVDSInfoFromBIOS(pScrn))
         RADEONGetPanelInfoFromReg(pScrn);
 
+    /* The panel size we collected from BIOS may not be the
+     * maximum size supported by the panel.  If not, we update
+     * it now.  These will be used if no matching mode can be
+     * found from EDID data.
+     */
+    RADEONUpdatePanelSize(pScrn);
+
+    /* No timing information for the native mode,
+     * use whatever specified in the Modeline.
+     * If no Modeline specified, we'll just pick
+     * the VESA mode at 60Hz refresh rate which
+     * is likely to be the best for a flat panel.
+     */
     if (info->DotClock == 0) {
         RADEONEntPtr pRADEONEnt   = RADEONEntPriv(pScrn);
         DisplayModePtr  tmp_mode = NULL;
         xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                    "No valid timing info from BIOS.\n");
-        /* No timing information for the native mode,
-           use whatever specified in the Modeline.
-           If no Modeline specified, we'll just pick
-           the VESA mode at 60Hz refresh rate which
-           is likely to be the best for a flat panel.
-	*/
         tmp_mode = pScrn->monitor->Modes;
         while(tmp_mode) {
             if ((tmp_mode->HDisplay == info->PanelXRes) &&
@@ -1522,6 +1550,8 @@
             RADEONGetTMDSInfo(pScrn);
             if (!pScrn->monitor->DDC)
                 RADEONGetHardCodedEDIDFromBIOS(pScrn);
+	    else if (!info->IsSecondary)
+		RADEONUpdatePanelSize(pScrn);
         }
     }
 }
@@ -2704,14 +2734,35 @@
     xf86MonPtr      ddc  = pScrn->monitor->DDC;
     DisplayModePtr  p;
 
+    if (info->UseBiosDividers && info->DotClock != 0)
+	return;
+
     /* Go thru detailed timing table first */
     for (j = 0; j < 4; j++) {
 	if (ddc->det_mon[j].type == 0) {
 	    struct detailed_timings *d_timings =
 		&ddc->det_mon[j].section.d_timings;
+	    int match = 0;
+
+	    /* If we didn't get a panel clock or guessed one, try to match the
+	     * mode with the panel size. We do that because we _need_ a panel
+	     * clock, or ValidateFPModes will fail, even when UseBiosDividers
+	     * is set.
+	     */
+	    if (info->DotClock == 0 &&
+		info->PanelXRes == d_timings->h_active &&
+		info->PanelYRes == d_timings->v_active)
+	        match = 1;
+	    
+	    /* If we don't have a BIOS provided panel data with fixed dividers,
+	     * check for a larger panel size
+	     */
 	    if (info->PanelXRes < d_timings->h_active &&
-		info->PanelYRes < d_timings->v_active) {
+		info->PanelYRes < d_timings->v_active &&
+		!info->UseBiosDividers)
+	        match = 1;
 
+	    if (match) {
 		info->PanelXRes  = d_timings->h_active;
 		info->PanelYRes  = d_timings->v_active;
 		info->DotClock   = d_timings->clock / 1000;
@@ -2721,10 +2772,24 @@
 		info->VOverPlus  = d_timings->v_sync_off;
 		info->VSyncWidth = d_timings->v_sync_width;
 		info->VBlank     = d_timings->v_blanking;
+		info->Flags      = (d_timings->interlaced ? V_INTERLACE : 0);
+		if (d_timings->sync == 3) {
+		    switch (d_timings->misc) {
+		    case 0: info->Flags |= V_NHSYNC | V_NVSYNC; break;
+		    case 1: info->Flags |= V_PHSYNC | V_NVSYNC; break;
+		    case 2: info->Flags |= V_NHSYNC | V_PVSYNC; break;
+		    case 3: info->Flags |= V_PHSYNC | V_PVSYNC; break;
+		    }
+	        }
+		xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel infos found from DDC detailed: %dx%d\n",
+			   info->PanelXRes, info->PanelYRes);
 	    }
 	}
     }
 
+    if (info->UseBiosDividers && info->DotClock != 0)
+	return;
+
     /* Search thru standard VESA modes from EDID */
     for (j = 0; j < 8; j++) {
 	if ((info->PanelXRes < ddc->timings2[j].hsize) &&
@@ -2746,28 +2811,16 @@
 			info->VOverPlus  = p->VSyncStart - p->VDisplay;
 			info->VSyncWidth = p->VSyncEnd - p->VSyncStart;
 			info->DotClock   = p->Clock;
-			info->Flags      =
-			    (ddc->det_mon[j].section.d_timings.interlaced
-			     ? V_INTERLACE
-			     : 0);
-			if (ddc->det_mon[j].section.d_timings.sync == 3) {
-			    switch (ddc->det_mon[j].section.d_timings.misc) {
-			    case 0: info->Flags |= V_NHSYNC | V_NVSYNC; break;
-			    case 1: info->Flags |= V_PHSYNC | V_NVSYNC; break;
-			    case 2: info->Flags |= V_NHSYNC | V_PVSYNC; break;
-			    case 3: info->Flags |= V_PHSYNC | V_PVSYNC; break;
-			    }
-			}
+			info->Flags	 = p->Flags;
+			xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel infos found from DDC VESA/EDID: %dx%d\n",
+				   info->PanelXRes, info->PanelYRes);
 		    }
 		}
 	    }
 	}
     }
-
-    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel size found from DDC: %dx%d\n",
-	       info->PanelXRes, info->PanelYRes);
-}
-
+ }
+ 
 /* This function will sort all modes according to their resolution.
  * Highest resolution first.
  */
@@ -2886,6 +2939,8 @@
 
     /* Search thru standard VESA modes from EDID */
     for (j = 0; j < 8; j++) {
+	if (ddc->timings2[j].hsize == 0 || ddc->timings2[j].vsize == 0)
+		continue;
 	for (p = pScrn->monitor->Modes; p && p->next; p = p->next->next) {
 	    /* Ignore all double scan modes */
 	    if ((ddc->timings2[j].hsize == p->HDisplay) &&
@@ -2975,19 +3030,10 @@
     pScrn->virtualX = pScrn1->display->virtualX;
     pScrn->virtualY = pScrn1->display->virtualY;
 
-    if (pScrn->monitor->DDC && !info->UseBiosDividers) {
+    if (pScrn->monitor->DDC) {
 	int  maxVirtX = pScrn->virtualX;
 	int  maxVirtY = pScrn->virtualY;
 
-	if ((DisplayType != MT_CRT) && (!info->IsSecondary) && (!crtc2)) {
-	    /* The panel size we collected from BIOS may not be the
-	     * maximum size supported by the panel.  If not, we update
-	     * it now.  These will be used if no matching mode can be
-	     * found from EDID data.
-	     */
-	    RADEONUpdatePanelSize(pScrn);
-	}
-
 	/* Collect all of the DDC modes */
 	first = last = ddcModes = RADEONDDCModes(pScrn);
 
@@ -3679,7 +3725,8 @@
 		xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
 			   "Invalid PanelSize value: %s\n", s);
 	    }
-	}
+	} else
+		RADEONGetPanelInfo(pScrn);
     }
 
     if (pScrn->monitor->DDC) {
@@ -4478,7 +4525,6 @@
     RADEONGetBIOSInfo(pScrn, pInt10);
     if (!RADEONQueryConnectedMonitors(pScrn))    goto fail;
     RADEONGetClockInfo(pScrn);
-    RADEONGetPanelInfo(pScrn);
 
     /* collect MergedFB options */
     /* only parse mergedfb options on the primary head. 
Index: xc/programs/Xserver/hw/xfree86/drivers/ati/radeon.man
===================================================================
--- xc.orig/programs/Xserver/hw/xfree86/drivers/ati/radeon.man	2004-12-05 20:22:08.094089208 +1100
+++ xc/programs/Xserver/hw/xfree86/drivers/ati/radeon.man	2004-12-05 20:49:58.442157704 +1100
@@ -507,6 +507,15 @@
 .B on 
 on other architectures.
 .TP
+.BI "Option \*qLVDSProbePLL\*q \*q" boolean \*q
+When BIOS panel informations aren't available (like on PowerBooks), it
+may still be necessary to use the firmware provided PLL values for the
+panel or flickering will happen. This option will force probing of
+the current value programmed in the chip when X is launched in that
+case.  This is only useful for LVDS panels (laptop internal panels).
+The default is
+.B on.
+.TP
 .BI "Option \*qReverseDDC\*q \*q" boolean \*q
 When BIOS connector informations aren't available, use this option to
 reverse the mapping of the 2 main DDC ports. Use this if the X serve





More information about the xorg mailing list