From 5bdc964918318f13db3a0445ad13808177c98733 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Aug 27 2008 15:38:51 +0000 Subject: * Mon Aug 25 2008 Adam Jackson 1.4.99.906-10 - xserver-1.5.0-edid-backport.patch: Backport EDID updates from master. --- diff --git a/xorg-x11-server.spec b/xorg-x11-server.spec index 5b59d96..2919eb7 100644 --- a/xorg-x11-server.spec +++ b/xorg-x11-server.spec @@ -19,7 +19,7 @@ Summary: X.Org X11 X server Name: xorg-x11-server Version: 1.4.99.906 -Release: 9%{?dist} +Release: 10%{?dist} URL: http://www.x.org License: MIT Group: User Interface/X @@ -69,6 +69,7 @@ Patch5013: xserver-1.5.0-xkb-fix-ProcXkbSetXYZ-to-work-on-all.patch Patch5014: xserver-1.5.0-force-SwitchCoreKeyboard-for-evdev.patch Patch5015: xserver-1.5.0-enable-selinux.patch Patch6000: xserver-1.5.0-hide-cursor.patch +Patch6001: xserver-1.5.0-edid-backport.patch %define moduledir %{_libdir}/xorg/modules %define drimoduledir %{_libdir}/dri @@ -494,6 +495,9 @@ rm -rf $RPM_BUILD_ROOT %changelog +* Mon Aug 25 2008 Adam Jackson 1.4.99.906-10 +- xserver-1.5.0-edid-backport.patch: Backport EDID updates from master. + * Wed Aug 20 2008 Adam Jackson 1.4.99.906-9 - xserver-1.5.0-hide-cursor.patch: Suppress displaying the cursor until an app calls XDefineCursor(). diff --git a/xserver-1.5.0-edid-backport.patch b/xserver-1.5.0-edid-backport.patch new file mode 100644 index 0000000..7c4c081 --- /dev/null +++ b/xserver-1.5.0-edid-backport.patch @@ -0,0 +1,976 @@ +From 2a0e3188588bce1759d3cef4d190909df2ca07ff Mon Sep 17 00:00:00 2001 +From: Adam Jackson +Date: Mon, 25 Aug 2008 12:55:56 -0400 +Subject: [PATCH] EDID backport from master. + +--- + hw/xfree86/common/Makefile.am | 3 +- + hw/xfree86/common/xf86Mode.c | 14 +-- + hw/xfree86/ddc/ddcProperty.c | 11 ++- + hw/xfree86/ddc/edid.h | 5 +- + hw/xfree86/ddc/interpret_edid.c | 14 +++ + hw/xfree86/ddc/print_edid.c | 28 +++-- + hw/xfree86/ddc/xf86DDC.c | 241 +++++++++++++++++++++++--------------- + hw/xfree86/ddc/xf86DDC.h | 6 + + hw/xfree86/loader/xf86sym.c | 1 + + hw/xfree86/modes/xf86Crtc.c | 11 +- + hw/xfree86/modes/xf86EdidModes.c | 228 +++++++++++++++++++++++++++++++----- + hw/xfree86/modes/xf86Modes.c | 12 ++ + hw/xfree86/modes/xf86Modes.h | 3 + + 13 files changed, 422 insertions(+), 155 deletions(-) + +diff --git a/hw/xfree86/common/Makefile.am b/hw/xfree86/common/Makefile.am +index 0f44075..8aa3adc 100644 +--- a/hw/xfree86/common/Makefile.am ++++ b/hw/xfree86/common/Makefile.am +@@ -45,7 +45,8 @@ libinit_a_SOURCES = xf86Build.h xf86Init.c + INCLUDES = $(XORG_INCS) -I$(srcdir)/../ddc -I$(srcdir)/../i2c \ + -I$(srcdir)/../loader -I$(srcdir)/../rac -I$(srcdir)/../parser \ + -I$(srcdir)/../vbe -I$(srcdir)/../int10 \ +- -I$(srcdir)/../vgahw -I$(srcdir)/../dixmods/extmod ++ -I$(srcdir)/../vgahw -I$(srcdir)/../dixmods/extmod \ ++ -I$(srcdir)/../modes + + sdk_HEADERS = compiler.h fourcc.h xf86.h xf86Module.h xf86Opt.h \ + xf86PciInfo.h xf86Priv.h xf86Privstr.h xf86Resources.h \ +diff --git a/hw/xfree86/common/xf86Mode.c b/hw/xfree86/common/xf86Mode.c +index c1b0a5f..24a431d 100644 +--- a/hw/xfree86/common/xf86Mode.c ++++ b/hw/xfree86/common/xf86Mode.c +@@ -39,6 +39,7 @@ + #endif + + #include ++#include "xf86Modes.h" + #include "os.h" + #include "servermd.h" + #include "mibank.h" +@@ -705,16 +706,9 @@ xf86CheckModeForMonitor(DisplayModePtr mode, MonPtr monitor) + * -- libv + */ + +- /* Is the horizontal blanking a bit lowish? */ +- if (((mode->HDisplay * 5 / 4) & ~0x07) > mode->HTotal) { +- /* is this a cvt -r mode, and only a cvt -r mode? */ +- if (((mode->HTotal - mode->HDisplay) == 160) && +- ((mode->HSyncEnd - mode->HDisplay) == 80) && +- ((mode->HSyncEnd - mode->HSyncStart) == 32) && +- ((mode->VSyncStart - mode->VDisplay) == 3)) { +- if (!monitor->reducedblanking && !(mode->type & M_T_DRIVER)) +- return MODE_NO_REDUCED; +- } ++ if (xf86ModeIsReduced(mode)) { ++ if (!monitor->reducedblanking && !(mode->type & M_T_DRIVER)) ++ return MODE_NO_REDUCED; + } + + if ((monitor->maxPixClock) && (mode->Clock > monitor->maxPixClock)) +diff --git a/hw/xfree86/ddc/ddcProperty.c b/hw/xfree86/ddc/ddcProperty.c +index 02125df..a4384f1 100644 +--- a/hw/xfree86/ddc/ddcProperty.c ++++ b/hw/xfree86/ddc/ddcProperty.c +@@ -83,13 +83,18 @@ addRootWindowProperties(ScrnInfoPtr pScrn, xf86MonPtr DDC) + } + + if (makeEDID1prop) { +- if ((EDID1rawdata = xalloc(128*sizeof(CARD8)))==NULL) ++ int size = 128; ++ ++ if (DDC->flags & EDID_COMPLETE_RAWDATA) ++ size += DDC->no_sections * 128; ++ ++ if ((EDID1rawdata = xalloc(size*sizeof(CARD8)))==NULL) + return; + + EDID1Atom = MakeAtom(EDID1_ATOM_NAME, sizeof(EDID1_ATOM_NAME) - 1, TRUE); +- memcpy(EDID1rawdata, DDC->rawData, 128); ++ memcpy(EDID1rawdata, DDC->rawData, size); + xf86RegisterRootWindowProperty(scrnIndex, EDID1Atom, XA_INTEGER, 8, +- 128, (unsigned char *)EDID1rawdata); ++ size, (unsigned char *)EDID1rawdata); + } + + if (makeEDID2prop) { +diff --git a/hw/xfree86/ddc/edid.h b/hw/xfree86/ddc/edid.h +index a4e79da..45caf6e 100644 +--- a/hw/xfree86/ddc/edid.h ++++ b/hw/xfree86/ddc/edid.h +@@ -531,6 +531,9 @@ struct detailed_monitor_section { + } section; /* max: 80 */ + }; + ++/* flags */ ++#define EDID_COMPLETE_RAWDATA 0x1 ++ + typedef struct { + int scrnIndex; + struct vendor vendor; +@@ -539,7 +542,7 @@ typedef struct { + struct established_timings timings1; + struct std_timings timings2[8]; + struct detailed_monitor_section det_mon[4]; +- void *vdif; /* unused */ ++ unsigned long flags; + int no_sections; + Uchar *rawData; + } xf86Monitor, *xf86MonPtr; +diff --git a/hw/xfree86/ddc/interpret_edid.c b/hw/xfree86/ddc/interpret_edid.c +index 21391dd..d293861 100644 +--- a/hw/xfree86/ddc/interpret_edid.c ++++ b/hw/xfree86/ddc/interpret_edid.c +@@ -118,6 +118,20 @@ xf86InterpretEDID(int scrnIndex, Uchar *block) + return NULL; + } + ++xf86MonPtr ++xf86InterpretEEDID(int scrnIndex, Uchar *block) ++{ ++ xf86MonPtr m; ++ ++ m = xf86InterpretEDID(scrnIndex, block); ++ if (!m) ++ return NULL; ++ ++ /* extension parse */ ++ ++ return m; ++} ++ + static void + get_vendor_section(Uchar *c, struct vendor *r) + { +diff --git a/hw/xfree86/ddc/print_edid.c b/hw/xfree86/ddc/print_edid.c +index f5442ad..5e89b40 100644 +--- a/hw/xfree86/ddc/print_edid.c ++++ b/hw/xfree86/ddc/print_edid.c +@@ -462,22 +462,28 @@ print_number_sections(int scrnIndex, int num) + xf86MonPtr + xf86PrintEDID(xf86MonPtr m) + { +- CARD16 i, j; ++ CARD16 i, j, n; + char buf[EDID_WIDTH * 2 + 1]; + +- if (!(m)) return NULL; ++ if (!m) return NULL; + +- print_vendor(m->scrnIndex,&m->vendor); +- print_version(m->scrnIndex,&m->ver); +- print_display(m->scrnIndex,&m->features, &m->ver); +- print_established_timings(m->scrnIndex,&m->timings1); +- print_std_timings(m->scrnIndex,m->timings2); +- print_detailed_monitor_section(m->scrnIndex,m->det_mon); +- print_number_sections(m->scrnIndex,m->no_sections); ++ print_vendor(m->scrnIndex, &m->vendor); ++ print_version(m->scrnIndex, &m->ver); ++ print_display(m->scrnIndex, &m->features, &m->ver); ++ print_established_timings(m->scrnIndex, &m->timings1); ++ print_std_timings(m->scrnIndex, m->timings2); ++ print_detailed_monitor_section(m->scrnIndex, m->det_mon); ++ print_number_sections(m->scrnIndex, m->no_sections); ++ ++ /* extension block section stuff */ + + xf86DrvMsg(m->scrnIndex, X_INFO, "EDID (in hex):\n"); +- +- for (i = 0; i < 128; i += j) { ++ ++ n = 128; ++ if (m->flags & EDID_COMPLETE_RAWDATA) ++ n += m->no_sections * 128; ++ ++ for (i = 0; i < n; i += j) { + for (j = 0; j < EDID_WIDTH; ++j) { + sprintf(&buf[j * 2], "%02x", m->rawData[i + j]); + } +diff --git a/hw/xfree86/ddc/xf86DDC.c b/hw/xfree86/ddc/xf86DDC.c +index 28e2ead..0d86776 100644 +--- a/hw/xfree86/ddc/xf86DDC.c ++++ b/hw/xfree86/ddc/xf86DDC.c +@@ -2,6 +2,14 @@ + * + * Copyright 1998,1999 by Egbert Eich + */ ++ ++/* ++ * Note that DDC1 does not define any method for returning blocks beyond ++ * the first. DDC2 does, but the original implementation would only ever ++ * read the first block. If you want to read and parse all blocks, use ++ * xf86DoEEDID(). ++ */ ++ + #ifdef HAVE_XORG_CONFIG_H + #include + #endif +@@ -31,18 +39,6 @@ static unsigned int *FetchEDID_DDC1( + register unsigned int (*)(ScrnInfoPtr) + ); + +-static unsigned char* EDID1Read_DDC2( +- int scrnIndex, +- I2CBusPtr pBus +-); +- +-static unsigned char * DDCRead_DDC2( +- int scrnIndex, +- I2CBusPtr pBus, +- int start, +- int len +-); +- + typedef enum { + DDCOPT_NODDC1, + DDCOPT_NODDC2, +@@ -107,10 +103,102 @@ xf86DoEDID_DDC1( + return tmp; + } + ++static I2CDevPtr ++DDC2MakeDevice(I2CBusPtr pBus, int address, char *name) ++{ ++ I2CDevPtr dev = NULL; ++ ++ if (!(dev = xf86I2CFindDev(pBus, address))) { ++ dev = xf86CreateI2CDevRec(); ++ dev->DevName = name; ++ dev->SlaveAddr = address; ++ dev->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */ ++ dev->StartTimeout = 550; ++ dev->BitTimeout = 40; ++ dev->AcknTimeout = 40; ++ ++ dev->pI2CBus = pBus; ++ if (!xf86I2CDevInit(dev)) { ++ xf86DrvMsg(pBus->scrnIndex, X_PROBED, "No DDC2 device\n"); ++ return NULL; ++ } ++ } ++ ++ return dev; ++} ++ ++static I2CDevPtr ++DDC2Init(int scrnIndex, I2CBusPtr pBus) ++{ ++ I2CDevPtr dev = NULL; ++ ++ /* ++ * Slow down the bus so that older monitors don't ++ * miss things. ++ */ ++ pBus->RiseFallTime = 20; ++ ++ DDC2MakeDevice(pBus, 0x0060, "E-EDID segment register"); ++ dev = DDC2MakeDevice(pBus, 0x00A0, "ddc2"); ++ ++ return dev; ++} ++ ++/* Mmmm, smell the hacks */ ++static void ++EEDIDStop(I2CDevPtr d) ++{ ++} ++ ++/* block is the EDID block number. a segment is two blocks. */ ++static Bool ++DDC2Read(I2CDevPtr dev, int block, unsigned char *R_Buffer) ++{ ++ unsigned char W_Buffer[1]; ++ int i, segment; ++ I2CDevPtr seg; ++ void (*stop)(I2CDevPtr); ++ ++ for (i = 0; i < RETRIES; i++) { ++ /* Stop bits reset the segment pointer to 0, so be careful here. */ ++ segment = block >> 1; ++ if (segment) { ++ Bool b; ++ ++ if (!(seg = xf86I2CFindDev(dev->pI2CBus, 0x0060))) ++ return FALSE; ++ ++ W_Buffer[0] = segment; ++ ++ stop = dev->pI2CBus->I2CStop; ++ dev->pI2CBus->I2CStop = EEDIDStop; ++ ++ b = xf86I2CWriteRead(seg, W_Buffer, 1, NULL, 0); ++ ++ dev->pI2CBus->I2CStop = stop; ++ if (!b) { ++ dev->pI2CBus->I2CStop(dev); ++ continue; ++ } ++ } ++ ++ W_Buffer[0] = (block & 0x01) * EDID1_LEN; ++ ++ if (xf86I2CWriteRead(dev, W_Buffer, 1, R_Buffer, EDID1_LEN)) { ++ if (!DDC_checksum(R_Buffer, EDID1_LEN)) ++ return TRUE; ++ } ++ } ++ ++ return FALSE; ++} ++ + /** + * Attempts to probe the monitor for EDID information, if NoDDC and NoDDC2 are + * unset. EDID information blocks are interpreted and the results returned in +- * an xf86MonPtr. ++ * an xf86MonPtr. Unlike xf86DoEDID_DDC[12](), this function will return ++ * the complete EDID data, including all extension blocks, if the 'complete' ++ * parameter is TRUE; + * + * This function does not affect the list of modes used by drivers -- it is up + * to the driver to decide policy on what to do with EDID information. +@@ -119,46 +207,72 @@ xf86DoEDID_DDC1( + * @return NULL if no monitor attached or failure to interpret the EDID. + */ + xf86MonPtr +-xf86DoEDID_DDC2(int scrnIndex, I2CBusPtr pBus) ++xf86DoEEDID(int scrnIndex, I2CBusPtr pBus, Bool complete) + { + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + unsigned char *EDID_block = NULL; + xf86MonPtr tmp = NULL; ++ I2CDevPtr dev = NULL; + /* Default DDC and DDC2 to enabled. */ + Bool noddc = FALSE, noddc2 = FALSE; + OptionInfoPtr options; + +- options = xnfalloc(sizeof(DDCOptions)); +- (void)memcpy(options, DDCOptions, sizeof(DDCOptions)); ++ options = xalloc(sizeof(DDCOptions)); ++ if (!options) ++ return NULL; ++ memcpy(options, DDCOptions, sizeof(DDCOptions)); + xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, options); + + xf86GetOptValBool(options, DDCOPT_NODDC, &noddc); + xf86GetOptValBool(options, DDCOPT_NODDC2, &noddc2); + xfree(options); +- ++ + if (noddc || noddc2) + return NULL; + +- EDID_block = EDID1Read_DDC2(scrnIndex,pBus); ++ if (!(dev = DDC2Init(scrnIndex, pBus))) ++ return NULL; + +- if (EDID_block){ +- tmp = xf86InterpretEDID(scrnIndex,EDID_block); +- } else { +-#ifdef DEBUG +- ErrorF("No EDID block returned\n"); +-#endif ++ EDID_block = xcalloc(1, EDID1_LEN); ++ if (!EDID_block) + return NULL; ++ ++ if (DDC2Read(dev, 0, EDID_block)) { ++ int i, n = EDID_block[0x7e]; ++ ++ if (complete && n) { ++ EDID_block = xrealloc(EDID_block, EDID1_LEN * (1+n)); ++ ++ for (i = 0; i < n; i++) ++ DDC2Read(dev, i+1, EDID_block + (EDID1_LEN * (1+i))); ++ } ++ ++ tmp = xf86InterpretEEDID(scrnIndex, EDID_block); + } +-#ifdef DEBUG +- if (!tmp) +- ErrorF("Cannot interpret EDID block\n"); +- else +- ErrorF("Sections to follow: %i\n",tmp->no_sections); +-#endif +- ++ ++ if (tmp && complete) ++ tmp->flags |= EDID_COMPLETE_RAWDATA; ++ + return tmp; + } + ++/** ++ * Attempts to probe the monitor for EDID information, if NoDDC and NoDDC2 are ++ * unset. EDID information blocks are interpreted and the results returned in ++ * an xf86MonPtr. ++ * ++ * This function does not affect the list of modes used by drivers -- it is up ++ * to the driver to decide policy on what to do with EDID information. ++ * ++ * @return pointer to a new xf86MonPtr containing the EDID information. ++ * @return NULL if no monitor attached or failure to interpret the EDID. ++ */ ++xf86MonPtr ++xf86DoEDID_DDC2(int scrnIndex, I2CBusPtr pBus) ++{ ++ return xf86DoEEDID(scrnIndex, pBus, FALSE); ++} ++ + /* + * read EDID record , pass it to callback function to interpret. + * callback function will store it for further use by calling +@@ -225,68 +339,3 @@ FetchEDID_DDC1(register ScrnInfoPtr pScrn, + } while(--count); + return (ptr); + } +- +-static unsigned char* +-EDID1Read_DDC2(int scrnIndex, I2CBusPtr pBus) +-{ +- return DDCRead_DDC2(scrnIndex, pBus, 0, EDID1_LEN); +-} +- +-static unsigned char * +-DDCRead_DDC2(int scrnIndex, I2CBusPtr pBus, int start, int len) +-{ +- I2CDevPtr dev; +- unsigned char W_Buffer[2]; +- int w_bytes; +- unsigned char *R_Buffer; +- int i; +- +- /* +- * Slow down the bus so that older monitors don't +- * miss things. +- */ +- pBus->RiseFallTime = 20; +- +- if (!(dev = xf86I2CFindDev(pBus, 0x00A0))) { +- dev = xf86CreateI2CDevRec(); +- dev->DevName = "ddc2"; +- dev->SlaveAddr = 0xA0; +- dev->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */ +- dev->StartTimeout = 550; +- dev->BitTimeout = 40; +- dev->AcknTimeout = 40; +- +- dev->pI2CBus = pBus; +- if (!xf86I2CDevInit(dev)) { +- xf86DrvMsg(scrnIndex, X_PROBED, "No DDC2 device\n"); +- return NULL; +- } +- } +- if (start < 0x100) { +- w_bytes = 1; +- W_Buffer[0] = start; +- } else { +- w_bytes = 2; +- W_Buffer[0] = start & 0xFF; +- W_Buffer[1] = (start & 0xFF00) >> 8; +- } +- R_Buffer = xcalloc(1,sizeof(unsigned char) +- * (len)); +- for (i=0; iver.version == 1) ++ if (edid_mon->ver.version == 1) { + size = 128; +- else if (edid_mon->ver.version == 2) ++ if (edid_mon->flags & EDID_COMPLETE_RAWDATA) ++ size += edid_mon->no_sections * 128; ++ } else if (edid_mon->ver.version == 2) + size = 256; + } + xf86OutputSetEDIDProperty (output, edid_mon ? edid_mon->rawData : NULL, size); +@@ -2622,15 +2624,16 @@ xf86OutputGetEDIDModes (xf86OutputPtr output) + return xf86DDCGetModes(scrn->scrnIndex, edid_mon); + } + ++/* maybe we should care about DDC1? meh. */ + _X_EXPORT xf86MonPtr + xf86OutputGetEDID (xf86OutputPtr output, I2CBusPtr pDDCBus) + { + ScrnInfoPtr scrn = output->scrn; + xf86MonPtr mon; + +- mon = xf86DoEDID_DDC2 (scrn->scrnIndex, pDDCBus); ++ mon = xf86DoEEDID(scrn->scrnIndex, pDDCBus, TRUE); + if (mon) +- xf86DDCApplyQuirks (scrn->scrnIndex, mon); ++ xf86DDCApplyQuirks(scrn->scrnIndex, mon); + + return mon; + } +diff --git a/hw/xfree86/modes/xf86EdidModes.c b/hw/xfree86/modes/xf86EdidModes.c +index bf0ea3f..7e80601 100644 +--- a/hw/xfree86/modes/xf86EdidModes.c ++++ b/hw/xfree86/modes/xf86EdidModes.c +@@ -1,5 +1,6 @@ + /* + * Copyright 2006 Luc Verhaegen. ++ * Copyright 2008 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), +@@ -34,16 +35,39 @@ + #endif + #endif + ++#define _PARSE_EDID_ + #include "xf86.h" + #include "xf86DDC.h" + #include + #include "property.h" + #include "propertyst.h" +-#include "xf86DDC.h" + #include "xf86Crtc.h" + #include + #include + ++static Bool ++xf86MonitorSupportsReducedBlanking(xf86MonPtr DDC) ++{ ++ /* EDID 1.4 explicitly defines RB support */ ++ if (DDC->ver.revision >= 4) { ++ int i; ++ for (i = 0; i < DET_TIMINGS; i++) { ++ struct detailed_monitor_section *det_mon = &DDC->det_mon[i]; ++ if (det_mon->type == DS_RANGES) ++ if (det_mon->section.ranges.supported_blanking & CVT_REDUCED) ++ return TRUE; ++ } ++ ++ return FALSE; ++ } ++ ++ /* For anything older, assume digital means RB support. Boo. */ ++ if (DDC->features.input_type) ++ return TRUE; ++ ++ return FALSE; ++} ++ + /* + * Quirks to work around broken EDID data from various monitors. + */ +@@ -68,6 +92,8 @@ typedef enum { + DDC_QUIRK_FIRST_DETAILED_PREFERRED = 1 << 6, + /* use +hsync +vsync for detailed mode */ + DDC_QUIRK_DETAILED_SYNC_PP = 1 << 7, ++ /* Force single-link DVI bandwidth limit */ ++ DDC_QUIRK_DVI_SINGLE_LINK = 1 << 8, + } ddc_quirk_t; + + static Bool quirk_prefer_large_60 (int scrnIndex, xf86MonPtr DDC) +@@ -181,6 +207,16 @@ static Bool quirk_detailed_sync_pp(int scrnIndex, xf86MonPtr DDC) + return FALSE; + } + ++/* This should probably be made more generic */ ++static Bool quirk_dvi_single_link(int scrnIndex, xf86MonPtr DDC) ++{ ++ /* Red Hat bug #453106: Apple 23" Cinema Display */ ++ if (memcmp (DDC->vendor.name, "APL", 4) == 0 && ++ DDC->vendor.prod_id == 0x921c) ++ return TRUE; ++ return FALSE; ++} ++ + typedef struct { + Bool (*detect) (int scrnIndex, xf86MonPtr DDC); + ddc_quirk_t quirk; +@@ -220,6 +256,10 @@ static const ddc_quirk_map_t ddc_quirks[] = { + quirk_detailed_sync_pp, DDC_QUIRK_DETAILED_SYNC_PP, + "Use +hsync +vsync for detailed timing." + }, ++ { ++ quirk_dvi_single_link, DDC_QUIRK_DVI_SINGLE_LINK, ++ "Forcing maximum pixel clock to single DVI link." ++ }, + { + NULL, DDC_QUIRK_NONE, + "No known quirks" +@@ -227,8 +267,13 @@ static const ddc_quirk_map_t ddc_quirks[] = { + }; + + /* +- * TODO: +- * - for those with access to the VESA DMT standard; review please. ++ * These more or less come from the DMT spec. The 720x400 modes are ++ * inferred from historical 80x25 practice. The 640x480@67 and 832x624@75 ++ * modes are old-school Mac modes. The EDID spec says the 1152x864@75 mode ++ * should be 1152x870, again for the Mac, but instead we use the x864 DMT ++ * mode. ++ * ++ * The DMT modes have been fact-checked; the rest are mild guesses. + */ + #define MODEPREFIX NULL, NULL, NULL, 0, M_T_DRIVER + #define MODESUFFIX 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,FALSE,FALSE,0,NULL,0,0.0,0.0 +@@ -237,16 +282,16 @@ static const DisplayModeRec DDCEstablishedModes[17] = { + { MODEPREFIX, 40000, 800, 840, 968, 1056, 0, 600, 601, 605, 628, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 800x600@60Hz */ + { MODEPREFIX, 36000, 800, 824, 896, 1024, 0, 600, 601, 603, 625, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 800x600@56Hz */ + { MODEPREFIX, 31500, 640, 656, 720, 840, 0, 480, 481, 484, 500, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x480@75Hz */ +- { MODEPREFIX, 31500, 640, 664, 704, 832, 0, 480, 489, 491, 520, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x480@72Hz */ ++ { MODEPREFIX, 31500, 640, 664, 704, 832, 0, 480, 489, 492, 520, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x480@72Hz */ + { MODEPREFIX, 30240, 640, 704, 768, 864, 0, 480, 483, 486, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x480@67Hz */ +- { MODEPREFIX, 25200, 640, 656, 752, 800, 0, 480, 490, 492, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x480@60Hz */ ++ { MODEPREFIX, 25175, 640, 656, 752, 800, 0, 480, 490, 492, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x480@60Hz */ + { MODEPREFIX, 35500, 720, 738, 846, 900, 0, 400, 421, 423, 449, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 720x400@88Hz */ + { MODEPREFIX, 28320, 720, 738, 846, 900, 0, 400, 412, 414, 449, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 720x400@70Hz */ + { MODEPREFIX, 135000, 1280, 1296, 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1280x1024@75Hz */ +- { MODEPREFIX, 78800, 1024, 1040, 1136, 1312, 0, 768, 769, 772, 800, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1024x768@75Hz */ ++ { MODEPREFIX, 78750, 1024, 1040, 1136, 1312, 0, 768, 769, 772, 800, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1024x768@75Hz */ + { MODEPREFIX, 75000, 1024, 1048, 1184, 1328, 0, 768, 771, 777, 806, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 1024x768@70Hz */ + { MODEPREFIX, 65000, 1024, 1048, 1184, 1344, 0, 768, 771, 777, 806, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 1024x768@60Hz */ +- { MODEPREFIX, 44900, 1024, 1032, 1208, 1264, 0, 768, 768, 776, 817, 0, V_PHSYNC | V_PVSYNC | V_INTERLACE, MODESUFFIX }, /* 1024x768@43Hz */ ++ { MODEPREFIX, 44900, 1024, 1032, 1208, 1264, 0, 768, 768, 772, 817, 0, V_PHSYNC | V_PVSYNC | V_INTERLACE, MODESUFFIX }, /* 1024x768@43Hz */ + { MODEPREFIX, 57284, 832, 864, 928, 1152, 0, 624, 625, 628, 667, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 832x624@75Hz */ + { MODEPREFIX, 49500, 800, 816, 896, 1056, 0, 600, 601, 604, 625, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 800x600@75Hz */ + { MODEPREFIX, 50000, 800, 856, 976, 1040, 0, 600, 637, 643, 666, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 800x600@72Hz */ +@@ -272,6 +317,90 @@ DDCModesFromEstablished(int scrnIndex, struct established_timings *timing, + return Modes; + } + ++/* Autogenerated from the DMT spec */ ++static const DisplayModeRec DMTModes[] = { ++ { MODEPREFIX, 31500, 640, 672, 736, 832, 0, 350, 382, 385, 445, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x350@85Hz */ ++ { MODEPREFIX, 31500, 640, 672, 736, 832, 0, 400, 401, 404, 445, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 640x400@85Hz */ ++ { MODEPREFIX, 35500, 720, 756, 828, 936, 0, 400, 401, 404, 446, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 720x400@85Hz */ ++ { MODEPREFIX, 25175, 640, 656, 752, 800, 0, 480, 490, 492, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x480@60Hz */ ++ { MODEPREFIX, 31500, 640, 664, 704, 832, 0, 480, 489, 492, 520, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x480@72Hz */ ++ { MODEPREFIX, 31500, 640, 656, 720, 840, 0, 480, 481, 484, 500, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x480@75Hz */ ++ { MODEPREFIX, 36000, 640, 696, 752, 832, 0, 480, 481, 484, 509, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 640x480@85Hz */ ++ { MODEPREFIX, 36000, 800, 824, 896, 1024, 0, 600, 601, 603, 625, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 800x600@56Hz */ ++ { MODEPREFIX, 40000, 800, 840, 968, 1056, 0, 600, 601, 605, 628, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 800x600@60Hz */ ++ { MODEPREFIX, 50000, 800, 856, 976, 1040, 0, 600, 637, 643, 666, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 800x600@72Hz */ ++ { MODEPREFIX, 49500, 800, 816, 896, 1056, 0, 600, 601, 604, 625, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 800x600@75Hz */ ++ { MODEPREFIX, 56250, 800, 832, 896, 1048, 0, 600, 601, 604, 631, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 800x600@85Hz */ ++ { MODEPREFIX, 73250, 800, 848, 880, 960, 0, 600, 603, 607, 636, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 800x600@120Hz RB */ ++ { MODEPREFIX, 33750, 848, 864, 976, 1088, 0, 480, 486, 494, 517, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 848x480@60Hz */ ++ { MODEPREFIX, 44900, 1024, 1032, 1208, 1264, 0, 768, 768, 772, 817, 0, V_PHSYNC | V_PVSYNC | V_INTERLACE, MODESUFFIX }, /* 1024x768@43Hz (interlaced) */ ++ { MODEPREFIX, 65000, 1024, 1048, 1184, 1344, 0, 768, 771, 777, 806, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 1024x768@60Hz */ ++ { MODEPREFIX, 75000, 1024, 1048, 1184, 1328, 0, 768, 771, 777, 806, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* 1024x768@70Hz */ ++ { MODEPREFIX, 78750, 1024, 1040, 1136, 1312, 0, 768, 769, 772, 800, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1024x768@75Hz */ ++ { MODEPREFIX, 94500, 1024, 1072, 1168, 1376, 0, 768, 769, 772, 808, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1024x768@85Hz */ ++ { MODEPREFIX, 115500, 1024, 1072, 1104, 1184, 0, 768, 771, 775, 813, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1024x768@120Hz RB */ ++ { MODEPREFIX, 108000, 1152, 1216, 1344, 1600, 0, 864, 865, 868, 900, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1152x864@75Hz */ ++ { MODEPREFIX, 68250, 1280, 1328, 1360, 1440, 0, 768, 771, 778, 790, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1280x768@60Hz RB */ ++ { MODEPREFIX, 79500, 1280, 1344, 1472, 1664, 0, 768, 771, 778, 798, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1280x768@60Hz */ ++ { MODEPREFIX, 102250, 1280, 1360, 1488, 1696, 0, 768, 771, 778, 805, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1280x768@75Hz */ ++ { MODEPREFIX, 117500, 1280, 1360, 1496, 1712, 0, 768, 771, 778, 809, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1280x768@85Hz */ ++ { MODEPREFIX, 140250, 1280, 1328, 1360, 1440, 0, 768, 771, 778, 813, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1280x768@120Hz RB */ ++ { MODEPREFIX, 71000, 1280, 1328, 1360, 1440, 0, 800, 803, 809, 823, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1280x800@60Hz RB */ ++ { MODEPREFIX, 83500, 1280, 1352, 1480, 1680, 0, 800, 803, 809, 831, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1280x800@60Hz */ ++ { MODEPREFIX, 106500, 1280, 1360, 1488, 1696, 0, 800, 803, 809, 838, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1280x800@75Hz */ ++ { MODEPREFIX, 122500, 1280, 1360, 1496, 1712, 0, 800, 803, 809, 843, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1280x800@85Hz */ ++ { MODEPREFIX, 146250, 1280, 1328, 1360, 1440, 0, 800, 803, 809, 847, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1280x800@120Hz RB */ ++ { MODEPREFIX, 108000, 1280, 1376, 1488, 1800, 0, 960, 961, 964, 1000, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1280x960@60Hz */ ++ { MODEPREFIX, 148500, 1280, 1344, 1504, 1728, 0, 960, 961, 964, 1011, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1280x960@85Hz */ ++ { MODEPREFIX, 175500, 1280, 1328, 1360, 1440, 0, 960, 963, 967, 1017, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1280x960@120Hz RB */ ++ { MODEPREFIX, 108000, 1280, 1328, 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1280x1024@60Hz */ ++ { MODEPREFIX, 135000, 1280, 1296, 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1280x1024@75Hz */ ++ { MODEPREFIX, 157500, 1280, 1344, 1504, 1728, 0, 1024, 1025, 1028, 1072, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1280x1024@85Hz */ ++ { MODEPREFIX, 187250, 1280, 1328, 1360, 1440, 0, 1024, 1027, 1034, 1084, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1280x1024@120Hz RB */ ++ { MODEPREFIX, 85500, 1360, 1424, 1536, 1792, 0, 768, 771, 777, 795, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1360x768@60Hz */ ++ { MODEPREFIX, 148250, 1360, 1408, 1440, 1520, 0, 768, 771, 776, 813, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1360x768@120Hz RB */ ++ { MODEPREFIX, 101000, 1400, 1448, 1480, 1560, 0, 1050, 1053, 1057, 1080, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1400x1050@60Hz RB */ ++ { MODEPREFIX, 121750, 1400, 1488, 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1400x1050@60Hz */ ++ { MODEPREFIX, 156000, 1400, 1504, 1648, 1896, 0, 1050, 1053, 1057, 1099, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1400x1050@75Hz */ ++ { MODEPREFIX, 179500, 1400, 1504, 1656, 1912, 0, 1050, 1053, 1057, 1105, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1400x1050@85Hz */ ++ { MODEPREFIX, 208000, 1400, 1448, 1480, 1560, 0, 1050, 1053, 1057, 1112, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1400x1050@120Hz RB */ ++ { MODEPREFIX, 88750, 1440, 1488, 1520, 1600, 0, 900, 903, 909, 926, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1440x900@60Hz RB */ ++ { MODEPREFIX, 106500, 1440, 1520, 1672, 1904, 0, 900, 903, 909, 934, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1440x900@60Hz */ ++ { MODEPREFIX, 136750, 1440, 1536, 1688, 1936, 0, 900, 903, 909, 942, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1440x900@75Hz */ ++ { MODEPREFIX, 157000, 1440, 1544, 1696, 1952, 0, 900, 903, 909, 948, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1440x900@85Hz */ ++ { MODEPREFIX, 182750, 1440, 1488, 1520, 1600, 0, 900, 903, 909, 953, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1440x900@120Hz RB */ ++ { MODEPREFIX, 162000, 1600, 1664, 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1600x1200@60Hz */ ++ { MODEPREFIX, 175500, 1600, 1664, 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1600x1200@65Hz */ ++ { MODEPREFIX, 189000, 1600, 1664, 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1600x1200@70Hz */ ++ { MODEPREFIX, 202500, 1600, 1664, 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1600x1200@75Hz */ ++ { MODEPREFIX, 229500, 1600, 1664, 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* 1600x1200@85Hz */ ++ { MODEPREFIX, 268250, 1600, 1648, 1680, 1760, 0, 1200, 1203, 1207, 1271, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1600x1200@120Hz RB */ ++ { MODEPREFIX, 119000, 1680, 1728, 1760, 1840, 0, 1050, 1053, 1059, 1080, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1680x1050@60Hz RB */ ++ { MODEPREFIX, 146250, 1680, 1784, 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1680x1050@60Hz */ ++ { MODEPREFIX, 187000, 1680, 1800, 1976, 2272, 0, 1050, 1053, 1059, 1099, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1680x1050@75Hz */ ++ { MODEPREFIX, 214750, 1680, 1808, 1984, 2288, 0, 1050, 1053, 1059, 1105, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1680x1050@85Hz */ ++ { MODEPREFIX, 245500, 1680, 1728, 1760, 1840, 0, 1050, 1053, 1059, 1112, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1680x1050@120Hz RB */ ++ { MODEPREFIX, 204750, 1792, 1920, 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1792x1344@60Hz */ ++ { MODEPREFIX, 261000, 1792, 1888, 2104, 2456, 0, 1344, 1345, 1348, 1417, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1792x1344@75Hz */ ++ { MODEPREFIX, 333250, 1792, 1840, 1872, 1952, 0, 1344, 1347, 1351, 1423, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1792x1344@120Hz RB */ ++ { MODEPREFIX, 218250, 1856, 1952, 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1856x1392@60Hz */ ++ { MODEPREFIX, 288000, 1856, 1984, 2208, 2560, 0, 1392, 1393, 1396, 1500, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1856x1392@75Hz */ ++ { MODEPREFIX, 356500, 1856, 1904, 1936, 2016, 0, 1392, 1395, 1399, 1474, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1856x1392@120Hz RB */ ++ { MODEPREFIX, 154000, 1920, 1968, 2000, 2080, 0, 1200, 1203, 1209, 1235, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1920x1200@60Hz RB */ ++ { MODEPREFIX, 193250, 1920, 2056, 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1920x1200@60Hz */ ++ { MODEPREFIX, 245250, 1920, 2056, 2264, 2608, 0, 1200, 1203, 1209, 1255, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1920x1200@75Hz */ ++ { MODEPREFIX, 281250, 1920, 2064, 2272, 2624, 0, 1200, 1203, 1209, 1262, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1920x1200@85Hz */ ++ { MODEPREFIX, 317000, 1920, 1968, 2000, 2080, 0, 1200, 1203, 1209, 1271, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1920x1200@120Hz RB */ ++ { MODEPREFIX, 234000, 1920, 2048, 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1920x1440@60Hz */ ++ { MODEPREFIX, 297000, 1920, 2064, 2288, 2640, 0, 1440, 1441, 1444, 1500, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 1920x1440@75Hz */ ++ { MODEPREFIX, 380500, 1920, 1968, 2000, 2080, 0, 1440, 1443, 1447, 1525, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 1920x1440@120Hz RB */ ++ { MODEPREFIX, 268500, 2560, 2608, 2640, 2720, 0, 1600, 1603, 1609, 1646, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 2560x1600@60Hz RB */ ++ { MODEPREFIX, 348500, 2560, 2752, 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 2560x1600@60Hz */ ++ { MODEPREFIX, 443250, 2560, 2768, 3048, 3536, 0, 1600, 1603, 1609, 1672, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 2560x1600@75Hz */ ++ { MODEPREFIX, 505250, 2560, 2768, 3048, 3536, 0, 1600, 1603, 1609, 1682, 0, V_NHSYNC | V_PVSYNC, MODESUFFIX }, /* 2560x1600@85Hz */ ++ { MODEPREFIX, 552750, 2560, 2608, 2640, 2720, 0, 1600, 1603, 1609, 1694, 0, V_PHSYNC | V_NVSYNC, MODESUFFIX }, /* 2560x1600@120Hz RB */ ++}; ++ + #define LEVEL_DMT 0 + #define LEVEL_GTF 1 + #define LEVEL_CVT 2 +@@ -288,12 +417,43 @@ MonitorStandardTimingLevel(xf86MonPtr DDC) + return LEVEL_DMT; + } + ++static int ++ModeRefresh(DisplayModePtr mode) ++{ ++ return (int)(xf86ModeVRefresh(mode) + 0.5); ++} ++ + /* +- * This is not really correct. Appendix B of the EDID 1.4 spec defines +- * the right thing to do here. If the timing given here matches a mode +- * defined in the VESA DMT standard, we _must_ use that. If the device +- * supports CVT modes, then we should generate a CVT timing. If both +- * of the above fail, use GTF. ++ * If rb is not set, then we'll not consider reduced-blanking modes as ++ * part of the DMT pool. For the 'standard' EDID mode descriptor there's ++ * no way to specify whether the mode should be RB or not. ++ */ ++static DisplayModePtr ++FindDMTMode(int hsize, int vsize, int refresh, Bool rb) ++{ ++ int i; ++ DisplayModePtr ret; ++ ++ for (i = 0; i < sizeof(DMTModes) / sizeof(DisplayModeRec); i++) { ++ ret = &DMTModes[i]; ++ ++ if (!rb && xf86ModeIsReduced(ret)) ++ continue; ++ ++ if (ret->HDisplay == hsize && ++ ret->VDisplay == vsize && ++ refresh == ModeRefresh(ret)) ++ return xf86DuplicateMode(ret); ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Appendix B of the EDID 1.4 spec defines the right thing to do here. ++ * If the timing given here matches a mode defined in the VESA DMT standard, ++ * we _must_ use that. If the device supports CVT modes, then we should ++ * generate a CVT timing. If both of the above fail, use GTF. + * + * There are some wrinkles here. EDID 1.1 and 1.0 sinks can't really + * "support" GTF, since it wasn't a standard yet; so if they ask for a +@@ -308,20 +468,28 @@ MonitorStandardTimingLevel(xf86MonPtr DDC) + */ + static DisplayModePtr + DDCModesFromStandardTiming(struct std_timings *timing, ddc_quirk_t quirks, +- int timing_level) ++ int timing_level, Bool rb) + { + DisplayModePtr Modes = NULL, Mode = NULL; + int i; + + for (i = 0; i < STD_TIMINGS; i++) { + if (timing[i].hsize && timing[i].vsize && timing[i].refresh) { +- /* XXX check for DMT first, else... */ +- if (timing_level == LEVEL_CVT) +- Mode = xf86CVTMode(timing[i].hsize, timing[i].vsize, +- timing[i].refresh, FALSE, FALSE); +- else +- Mode = xf86GTFMode(timing[i].hsize, timing[i].vsize, +- timing[i].refresh, FALSE, FALSE); ++ Mode = FindDMTMode(timing[i].hsize, timing[i].vsize, ++ timing[i].refresh, rb); ++ ++ if (!Mode) { ++ if (timing_level == LEVEL_CVT) ++ /* pass rb here too? */ ++ Mode = xf86CVTMode(timing[i].hsize, timing[i].vsize, ++ timing[i].refresh, FALSE, FALSE); ++ else if (timing_level == LEVEL_GTF) ++ Mode = xf86GTFMode(timing[i].hsize, timing[i].vsize, ++ timing[i].refresh, FALSE, FALSE); ++ } ++ ++ if (!Mode) ++ continue; + + Mode->type = M_T_DRIVER; + Modes = xf86ModesAdd(Modes, Mode); +@@ -595,7 +763,7 @@ xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC) + int i; + DisplayModePtr Modes = NULL, Mode; + ddc_quirk_t quirks; +- Bool preferred; ++ Bool preferred, rb; + int timing_level; + + xf86DrvMsg (scrnIndex, X_INFO, "EDID vendor \"%s\", prod id %d\n", +@@ -611,6 +779,8 @@ xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC) + if (quirks & (DDC_QUIRK_PREFER_LARGE_60 | DDC_QUIRK_PREFER_LARGE_75)) + preferred = FALSE; + ++ rb = xf86MonitorSupportsReducedBlanking(DDC); ++ + timing_level = MonitorStandardTimingLevel(DDC); + + for (i = 0; i < DET_TIMINGS; i++) { +@@ -627,7 +797,7 @@ xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC) + break; + case DS_STD_TIMINGS: + Mode = DDCModesFromStandardTiming(det_mon->section.std_t, +- quirks, timing_level); ++ quirks, timing_level, rb); + Modes = xf86ModesAdd(Modes, Mode); + break; + case DS_CVT: +@@ -644,7 +814,7 @@ xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC) + Modes = xf86ModesAdd(Modes, Mode); + + /* Add standard timings */ +- Mode = DDCModesFromStandardTiming(DDC->timings2, quirks, timing_level); ++ Mode = DDCModesFromStandardTiming(DDC->timings2, quirks, timing_level, rb); + Modes = xf86ModesAdd(Modes, Mode); + + if (quirks & DDC_QUIRK_PREFER_LARGE_60) +@@ -665,23 +835,21 @@ xf86DDCMonitorSet(int scrnIndex, MonPtr Monitor, xf86MonPtr DDC) + DisplayModePtr Modes = NULL, Mode; + int i, clock; + Bool have_hsync = FALSE, have_vrefresh = FALSE, have_maxpixclock = FALSE; ++ ddc_quirk_t quirks; + + if (!Monitor || !DDC) + return; + + Monitor->DDC = DDC; + ++ quirks = xf86DDCDetectQuirks(scrnIndex, DDC, FALSE); ++ + if (Monitor->widthmm <= 0 && Monitor->heightmm <= 0) { + Monitor->widthmm = 10 * DDC->features.hsize; + Monitor->heightmm = 10 * DDC->features.vsize; + } + +- /* +- * If this is a digital display, then we can use reduced blanking. +- * XXX This is a 1.3 heuristic. 1.4 explicitly defines rb support. +- */ +- if (DDC->features.input_type) +- Monitor->reducedblanking = TRUE; ++ Monitor->reducedblanking = xf86MonitorSupportsReducedBlanking(DDC); + + Modes = xf86DDCGetModes(scrnIndex, DDC); + +@@ -723,6 +891,8 @@ xf86DDCMonitorSet(int scrnIndex, MonPtr Monitor, xf86MonPtr DDC) + } + + clock = DDC->det_mon[i].section.ranges.max_clock * 1000; ++ if (quirks & DDC_QUIRK_DVI_SINGLE_LINK) ++ clock = min(clock, 165000); + if (!have_maxpixclock && clock > Monitor->maxPixClock) + Monitor->maxPixClock = clock; + +diff --git a/hw/xfree86/modes/xf86Modes.c b/hw/xfree86/modes/xf86Modes.c +index ea398ad..da25273 100644 +--- a/hw/xfree86/modes/xf86Modes.c ++++ b/hw/xfree86/modes/xf86Modes.c +@@ -513,6 +513,18 @@ xf86ValidateModesBandwidth(ScrnInfoPtr pScrn, DisplayModePtr modeList, + } + } + ++Bool ++xf86ModeIsReduced(DisplayModePtr mode) ++{ ++ if ((((mode->HDisplay * 5 / 4) & ~0x07) > mode->HTotal) && ++ ((mode->HTotal - mode->HDisplay) == 160) && ++ ((mode->HSyncEnd - mode->HDisplay) == 80) && ++ ((mode->HSyncEnd - mode->HSyncStart) == 32) && ++ ((mode->VSyncStart - mode->VDisplay) == 3)) ++ return TRUE; ++ return FALSE; ++} ++ + /** + * Marks as bad any reduced-blanking modes. + * +diff --git a/hw/xfree86/modes/xf86Modes.h b/hw/xfree86/modes/xf86Modes.h +index acdea65..af5987b 100644 +--- a/hw/xfree86/modes/xf86Modes.h ++++ b/hw/xfree86/modes/xf86Modes.h +@@ -64,6 +64,9 @@ DisplayModePtr xf86CVTMode(int HDisplay, int VDisplay, float VRefresh, + Bool Reduced, Bool Interlaced); + DisplayModePtr xf86GTFMode(int h_pixels, int v_lines, float freq, int interlaced, int margins); + ++Bool ++xf86ModeIsReduced(DisplayModePtr mode); ++ + void + xf86ValidateModesFlags(ScrnInfoPtr pScrn, DisplayModePtr modeList, + int flags); +-- +1.5.6.4 +