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