78ce686
From: Eric Anholt <eric@anholt.net>
78ce686
Date: Thu, 11 Oct 2007 23:48:56 +0000 (-0700)
78ce686
Subject: Bug #10304,12784,11603: Add quirks for several physical size issues.
78ce686
X-Git-Url: http://gitweb.freedesktop.org/?p=xorg/xserver.git;a=commitdiff;h=fc092334ac0a323b80a9602cb8bf60ca9dee3bfa
78ce686
78ce686
Bug #10304,12784,11603: Add quirks for several physical size issues.
78ce686
78ce686
A lot of EDID writers apparently end up stuffing centimeters (like the
78ce686
maximum image size field) into the detailed timings, instead of millimeters.
78ce686
Some of them only get it wrong in one direction.  Also, add a quirk to let
78ce686
us mark the largest 75hz mode as preferred, which will often be used for
78ce686
EDID 1.0 CRTs.
78ce686
---
78ce686
78ce686
--- a/hw/xfree86/modes/xf86Crtc.c
78ce686
+++ b/hw/xfree86/modes/xf86Crtc.c
78ce686
@@ -2134,8 +2134,12 @@ _X_EXPORT xf86MonPtr
78ce686
 xf86OutputGetEDID (xf86OutputPtr output, I2CBusPtr pDDCBus)
78ce686
 {
78ce686
     ScrnInfoPtr	scrn = output->scrn;
78ce686
+    xf86MonPtr mon;
78ce686
 
78ce686
-    return xf86DoEDID_DDC2 (scrn->scrnIndex, pDDCBus);
78ce686
+    mon = xf86DoEDID_DDC2 (scrn->scrnIndex, pDDCBus);
78ce686
+    xf86DDCApplyQuirks (scrn->scrnIndex, pDDCBus);
78ce686
+
78ce686
+    return mon;
78ce686
 }
78ce686
 
78ce686
 static char *_xf86ConnectorNames[] = { "None", "VGA", "DVI-I", "DVI-D",
78ce686
--- a/hw/xfree86/modes/xf86EdidModes.c
78ce686
+++ b/hw/xfree86/modes/xf86EdidModes.c
78ce686
@@ -54,6 +54,16 @@ typedef enum {
78ce686
     DDC_QUIRK_PREFER_LARGE_60 = 1 << 0,
78ce686
     /* 135MHz clock is too high, drop a bit */
78ce686
     DDC_QUIRK_135_CLOCK_TOO_HIGH = 1 << 1,
78ce686
+    /* Prefer the largest mode at 75 Hz */
78ce686
+    DDC_QUIRK_PREFER_LARGE_75 = 1 << 2,
78ce686
+    /* Convert detailed timing's horizontal from units of cm to mm */
78ce686
+    DDC_QUIRK_DETAILED_H_IN_CM = 1 << 3,
78ce686
+    /* Convert detailed timing's vertical from units of cm to mm */
78ce686
+    DDC_QUIRK_DETAILED_V_IN_CM = 1 << 4,
78ce686
+    /* Detailed timing descriptors have bogus size values, so just take the
78ce686
+     * maximum size and use that.
78ce686
+     */
78ce686
+    DDC_QUIRK_DETAILED_USE_MAXIMUM_SIZE = 1 << 5,
78ce686
 } ddc_quirk_t;
78ce686
 
78ce686
 static Bool quirk_prefer_large_60 (int scrnIndex, xf86MonPtr DDC)
78ce686
@@ -81,6 +91,52 @@ static Bool quirk_prefer_large_60 (int s
78ce686
     return FALSE;
78ce686
 }
78ce686
 
78ce686
+static Bool quirk_prefer_large_75 (int scrnIndex, xf86MonPtr DDC)
78ce686
+{
78ce686
+    /* Bug #11603: Funai Electronics PM36B */
78ce686
+    if (memcmp (DDC->vendor.name, "FCM", 4) == 0 &&
78ce686
+	DDC->vendor.prod_id == 13600)
78ce686
+	return TRUE;
78ce686
+
78ce686
+    return FALSE;
78ce686
+}
78ce686
+
78ce686
+static Bool quirk_detailed_h_in_cm (int scrnIndex, xf86MonPtr DDC)
78ce686
+{
78ce686
+    /* Bug #10304: "LGPhilipsLCD LP154W01-A5" */
78ce686
+    /* Bug #12784: "LGPhilipsLCD LP154W01-TLA2" */
78ce686
+    if (memcmp (DDC->vendor.name, "LPL", 4) == 0 &&
78ce686
+	DDC->vendor.prod_id == 0)
78ce686
+	return TRUE;
78ce686
+
78ce686
+    /* Bug #11603: Funai Electronics PM36B */
78ce686
+    if (memcmp (DDC->vendor.name, "FCM", 4) == 0 &&
78ce686
+	DDC->vendor.prod_id == 13600)
78ce686
+	return TRUE;
78ce686
+
78ce686
+    return FALSE;
78ce686
+}
78ce686
+
78ce686
+static Bool quirk_detailed_v_in_cm (int scrnIndex, xf86MonPtr DDC)
78ce686
+{
78ce686
+    /* Bug #11603: Funai Electronics PM36B */
78ce686
+    if (memcmp (DDC->vendor.name, "FCM", 4) == 0 &&
78ce686
+	DDC->vendor.prod_id == 13600)
78ce686
+	return TRUE;
78ce686
+
78ce686
+    return FALSE;
78ce686
+}
78ce686
+
78ce686
+static Bool quirk_detailed_use_maximum_size (int scrnIndex, xf86MonPtr DDC)
78ce686
+{
78ce686
+    /* Bug #10304: LGPhilipsLCD LP154W01-A5 */
78ce686
+    if (memcmp (DDC->vendor.name, "LPL", 4) == 0 &&
78ce686
+	DDC->vendor.prod_id == 0)
78ce686
+	return TRUE;
78ce686
+
78ce686
+    return FALSE;
78ce686
+}
78ce686
+
78ce686
 static Bool quirk_135_clock_too_high (int scrnIndex, xf86MonPtr DDC)
78ce686
 {
78ce686
     /* Envision Peripherals, Inc. EN-7100e.  See bug #9550. */
78ce686
@@ -106,6 +162,22 @@ static const ddc_quirk_map_t ddc_quirks[
78ce686
 	quirk_135_clock_too_high,   DDC_QUIRK_135_CLOCK_TOO_HIGH,
78ce686
 	"Recommended 135MHz pixel clock is too high"
78ce686
     },
78ce686
+    {
78ce686
+	quirk_prefer_large_75,   DDC_QUIRK_PREFER_LARGE_75,
78ce686
+	"Detailed timing is not preferred, use largest mode at 75Hz"
78ce686
+    },
78ce686
+    {
78ce686
+	quirk_detailed_h_in_cm,   DDC_QUIRK_DETAILED_H_IN_CM,
78ce686
+	"Detailed timings give horizontal size in cm."
78ce686
+    },
78ce686
+    {
78ce686
+	quirk_detailed_v_in_cm,   DDC_QUIRK_DETAILED_V_IN_CM,
78ce686
+	"Detailed timings give vertical size in cm."
78ce686
+    },
78ce686
+    {
78ce686
+	quirk_detailed_use_maximum_size,   DDC_QUIRK_DETAILED_USE_MAXIMUM_SIZE,
78ce686
+	"Detailed timings give sizes in cm."
78ce686
+    },
78ce686
     { 
78ce686
 	NULL,		DDC_QUIRK_NONE,
78ce686
 	"No known quirks"
78ce686
@@ -303,6 +375,98 @@ DDCGuessRangesFromModes(int scrnIndex, M
78ce686
     }
78ce686
 }
78ce686
 
78ce686
+static ddc_quirk_t
78ce686
+xf86DDCDetectQuirks(int scrnIndex, xf86MonPtr DDC, Bool verbose)
78ce686
+{
78ce686
+    ddc_quirk_t	quirks;
78ce686
+    int i;
78ce686
+
78ce686
+    quirks = DDC_QUIRK_NONE;
78ce686
+    for (i = 0; ddc_quirks[i].detect; i++) {
78ce686
+	if (ddc_quirks[i].detect (scrnIndex, DDC)) {
78ce686
+	    if (verbose) {
78ce686
+		xf86DrvMsg (scrnIndex, X_INFO, "    EDID quirk: %s\n",
78ce686
+			    ddc_quirks[i].description);
78ce686
+	    }
78ce686
+	    quirks |= ddc_quirks[i].quirk;
78ce686
+	}
78ce686
+    }
78ce686
+
78ce686
+    return quirks;
78ce686
+}
78ce686
+
78ce686
+/**
78ce686
+ * Applies monitor-specific quirks to the decoded EDID information.
78ce686
+ *
78ce686
+ * Note that some quirks applying to the mode list are still implemented in
78ce686
+ * xf86DDCGetModes.
78ce686
+ */
78ce686
+void
78ce686
+xf86DDCApplyQuirks(int scrnIndex, xf86MonPtr DDC)
78ce686
+{
78ce686
+    ddc_quirk_t quirks = xf86DDCDetectQuirks (scrnIndex, DDC, FALSE);
78ce686
+    int i;
78ce686
+
78ce686
+    for (i = 0; i < DET_TIMINGS; i++) {
78ce686
+	struct detailed_monitor_section *det_mon = &DDC->det_mon[i];
78ce686
+
78ce686
+	if (det_mon->type != DT)
78ce686
+	    continue;
78ce686
+
78ce686
+	if (quirks & DDC_QUIRK_DETAILED_H_IN_CM)
78ce686
+	    det_mon->section.d_timings.h_size *= 10;
78ce686
+
78ce686
+	if (quirks & DDC_QUIRK_DETAILED_V_IN_CM)
78ce686
+	    det_mon->section.d_timings.v_size *= 10;
78ce686
+
78ce686
+	if (quirks & DDC_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
78ce686
+	    det_mon->section.d_timings.h_size = 10 * DDC->features.hsize;
78ce686
+	    det_mon->section.d_timings.v_size = 10 * DDC->features.vsize;
78ce686
+	}
78ce686
+    }
78ce686
+}
78ce686
+
78ce686
+/**
78ce686
+ * Walks the modes list, finding the mode with the largest area which is
78ce686
+ * closest to the target refresh rate, and marks it as the only preferred mode.
78ce686
+*/
78ce686
+static void
78ce686
+xf86DDCSetPreferredRefresh(int scrnIndex, DisplayModePtr modes,
78ce686
+			   float target_refresh)
78ce686
+{
78ce686
+	DisplayModePtr	mode, best = modes;
78ce686
+
78ce686
+	for (mode = modes; mode; mode = mode->next)
78ce686
+	{
78ce686
+	    mode->type &= ~M_T_PREFERRED;
78ce686
+
78ce686
+	    if (mode == best) continue;
78ce686
+
78ce686
+	    if (mode->HDisplay * mode->VDisplay >
78ce686
+		best->HDisplay * best->VDisplay)
78ce686
+	    {
78ce686
+		best = mode;
78ce686
+		continue;
78ce686
+	    }
78ce686
+	    if (mode->HDisplay * mode->VDisplay ==
78ce686
+		best->HDisplay * best->VDisplay)
78ce686
+	    {
78ce686
+		double	mode_refresh = xf86ModeVRefresh (mode);
78ce686
+		double	best_refresh = xf86ModeVRefresh (best);
78ce686
+		double	mode_dist = fabs(mode_refresh - target_refresh);
78ce686
+		double	best_dist = fabs(best_refresh - target_refresh);
78ce686
+
78ce686
+		if (mode_dist < best_dist)
78ce686
+		{
78ce686
+		    best = mode;
78ce686
+		    continue;
78ce686
+		}
78ce686
+	    }
78ce686
+	}
78ce686
+	if (best)
78ce686
+	    best->type |= M_T_PREFERRED;
78ce686
+}
78ce686
+
78ce686
 _X_EXPORT DisplayModePtr
78ce686
 xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC)
78ce686
 {
78ce686
@@ -312,15 +476,9 @@ xf86DDCGetModes(int scrnIndex, xf86MonPt
78ce686
 
78ce686
     xf86DrvMsg (scrnIndex, X_INFO, "EDID vendor \"%s\", prod id %d\n",
78ce686
 		DDC->vendor.name, DDC->vendor.prod_id);
78ce686
-    quirks = DDC_QUIRK_NONE;
78ce686
-    for (i = 0; ddc_quirks[i].detect; i++)
78ce686
-	if (ddc_quirks[i].detect (scrnIndex, DDC))
78ce686
-	{
78ce686
-	    xf86DrvMsg (scrnIndex, X_INFO, "    EDID quirk: %s\n",
78ce686
-			ddc_quirks[i].description);
78ce686
-	    quirks |= ddc_quirks[i].quirk;
78ce686
-	}
78ce686
-    
78ce686
+
78ce686
+    quirks = xf86DDCDetectQuirks(scrnIndex, DDC, TRUE);
78ce686
+
78ce686
     preferred = PREFERRED_TIMING_MODE(DDC->features.msc);
78ce686
     if (quirks & DDC_QUIRK_PREFER_LARGE_60)
78ce686
 	preferred = 0;
78ce686
@@ -357,32 +515,11 @@ xf86DDCGetModes(int scrnIndex, xf86MonPt
78ce686
     Modes = xf86ModesAdd(Modes, Mode);
78ce686
 
78ce686
     if (quirks & DDC_QUIRK_PREFER_LARGE_60)
78ce686
-    {
78ce686
-	DisplayModePtr	best = Modes;
78ce686
-	for (Mode = Modes; Mode; Mode = Mode->next)
78ce686
-	{
78ce686
-	    if (Mode == best) continue;
78ce686
-	    if (Mode->HDisplay * Mode->VDisplay > best->HDisplay * best->VDisplay)
78ce686
-	    {
78ce686
-		best = Mode;
78ce686
-		continue;
78ce686
-	    }
78ce686
-	    if (Mode->HDisplay * Mode->VDisplay == best->HDisplay * best->VDisplay)
78ce686
-	    {
78ce686
-		double	mode_refresh = xf86ModeVRefresh (Mode);
78ce686
-		double	best_refresh = xf86ModeVRefresh (best);
78ce686
-		double	mode_dist = fabs(mode_refresh - 60.0);
78ce686
-		double	best_dist = fabs(best_refresh - 60.0);
78ce686
-		if (mode_dist < best_dist)
78ce686
-		{
78ce686
-		    best = Mode;
78ce686
-		    continue;
78ce686
-		}
78ce686
-	    }
78ce686
-	}
78ce686
-	if (best)
78ce686
-	    best->type |= M_T_PREFERRED;
78ce686
-    }
78ce686
+	xf86DDCSetPreferredRefresh(scrnIndex, Modes, 60);
78ce686
+
78ce686
+    if (quirks & DDC_QUIRK_PREFER_LARGE_75)
78ce686
+	xf86DDCSetPreferredRefresh(scrnIndex, Modes, 75);
78ce686
+
78ce686
     return Modes;
78ce686
 }
78ce686
 
78ce686
--- a/hw/xfree86/modes/xf86Modes.h
78ce686
+++ b/hw/xfree86/modes/xf86Modes.h
78ce686
@@ -95,4 +95,7 @@ xf86GetMonitorModes (ScrnInfoPtr pScrn, 
78ce686
 DisplayModePtr
78ce686
 xf86GetDefaultModes (Bool interlaceAllowed, Bool doubleScanAllowed);
78ce686
 
78ce686
+void
78ce686
+xf86DDCApplyQuirks(int scrnIndex, xf86MonPtr DDC);
78ce686
+
78ce686
 #endif /* _XF86MODES_H_ */