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