Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6
[safe/jmp/linux-2.6] / drivers / gpu / drm / drm_edid.c
index d721438..c198186 100644 (file)
@@ -28,6 +28,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 #include <linux/kernel.h>
+#include <linux/slab.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
 #include "drmP.h"
@@ -64,7 +65,8 @@
 
 #define LEVEL_DMT      0
 #define LEVEL_GTF      1
-#define LEVEL_CVT      2
+#define LEVEL_GTF2     2
+#define LEVEL_CVT      3
 
 static struct edid_quirk {
        char *vendor;
@@ -84,6 +86,8 @@ static struct edid_quirk {
 
        /* Envision Peripherals, Inc. EN-7100e */
        { "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH },
+       /* Envision EN2028 */
+       { "EPI", 8232, EDID_QUIRK_PREFER_LARGE_60 },
 
        /* Funai Electronics PM36B */
        { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 |
@@ -143,7 +147,10 @@ drm_edid_block_valid(u8 *raw_edid)
                csum += raw_edid[i];
        if (csum) {
                DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum);
-               goto bad;
+
+               /* allow CEA to slide through, switches mangle this */
+               if (raw_edid[0] != 0x02)
+                       goto bad;
        }
 
        /* per-block-type checks */
@@ -492,7 +499,7 @@ static struct drm_display_mode drm_dmt_modes[] = {
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
        /* 1024x768@85Hz */
        { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072,
-                  1072, 1376, 0, 768, 769, 772, 808, 0,
+                  1168, 1376, 0, 768, 769, 772, 808, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
        /* 1152x864@75Hz */
        { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
@@ -583,7 +590,7 @@ static struct drm_display_mode drm_dmt_modes[] = {
                   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
        /* 1600x1200@75Hz */
-       { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 2025000, 1600, 1664,
+       { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 202500, 1600, 1664,
                   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
        /* 1600x1200@85Hz */
@@ -654,8 +661,8 @@ static struct drm_display_mode drm_dmt_modes[] = {
 static const int drm_num_dmt_modes =
        sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
 
-static struct drm_display_mode *drm_find_dmt(struct drm_device *dev,
-                       int hsize, int vsize, int fresh)
+struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
+                                          int hsize, int vsize, int fresh)
 {
        int i;
        struct drm_display_mode *ptr, *mode;
@@ -673,6 +680,7 @@ static struct drm_display_mode *drm_find_dmt(struct drm_device *dev,
        }
        return mode;
 }
+EXPORT_SYMBOL(drm_mode_find_dmt);
 
 typedef void detailed_cb(struct detailed_timing *timing, void *closure);
 
@@ -713,6 +721,71 @@ drm_monitor_supports_rb(struct edid *edid)
        return ((edid->input & DRM_EDID_INPUT_DIGITAL) != 0);
 }
 
+static void
+find_gtf2(struct detailed_timing *t, void *data)
+{
+       u8 *r = (u8 *)t;
+       if (r[3] == EDID_DETAIL_MONITOR_RANGE && r[10] == 0x02)
+               *(u8 **)data = r;
+}
+
+/* Secondary GTF curve kicks in above some break frequency */
+static int
+drm_gtf2_hbreak(struct edid *edid)
+{
+       u8 *r = NULL;
+       drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
+       return r ? (r[12] * 2) : 0;
+}
+
+static int
+drm_gtf2_2c(struct edid *edid)
+{
+       u8 *r = NULL;
+       drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
+       return r ? r[13] : 0;
+}
+
+static int
+drm_gtf2_m(struct edid *edid)
+{
+       u8 *r = NULL;
+       drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
+       return r ? (r[15] << 8) + r[14] : 0;
+}
+
+static int
+drm_gtf2_k(struct edid *edid)
+{
+       u8 *r = NULL;
+       drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
+       return r ? r[16] : 0;
+}
+
+static int
+drm_gtf2_2j(struct edid *edid)
+{
+       u8 *r = NULL;
+       drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
+       return r ? r[17] : 0;
+}
+
+/**
+ * standard_timing_level - get std. timing level(CVT/GTF/DMT)
+ * @edid: EDID block to scan
+ */
+static int standard_timing_level(struct edid *edid)
+{
+       if (edid->revision >= 2) {
+               if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF))
+                       return LEVEL_CVT;
+               if (drm_gtf2_hbreak(edid))
+                       return LEVEL_GTF2;
+               return LEVEL_GTF;
+       }
+       return LEVEL_DMT;
+}
+
 /*
  * 0 is reserved.  The spec says 0x01 fill for unused timings.  Some old
  * monitors fill with ascii space (0x20) instead.
@@ -733,18 +806,19 @@ bad_std_timing(u8 a, u8 b)
  * Take the standard timing params (in this case width, aspect, and refresh)
  * and convert them into a real mode using CVT/GTF/DMT.
  */
-struct drm_display_mode *drm_mode_std(struct drm_device *dev,
-                                     struct std_timing *t,
-                                     int revision,
-                                     int timing_level)
+static struct drm_display_mode *
+drm_mode_std(struct drm_connector *connector, struct edid *edid,
+            struct std_timing *t, int revision)
 {
-       struct drm_display_mode *mode;
+       struct drm_device *dev = connector->dev;
+       struct drm_display_mode *m, *mode = NULL;
        int hsize, vsize;
        int vrefresh_rate;
        unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK)
                >> EDID_TIMING_ASPECT_SHIFT;
        unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK)
                >> EDID_TIMING_VFREQ_SHIFT;
+       int timing_level = standard_timing_level(edid);
 
        if (bad_std_timing(t->hsize, t->vfreq_aspect))
                return NULL;
@@ -774,6 +848,17 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev,
                vsize = 768;
        }
 
+       /*
+        * If this connector already has a mode for this size and refresh
+        * rate (because it came from detailed or CVT info), use that
+        * instead.  This way we don't have to guess at interlace or
+        * reduced blanking.
+        */
+       list_for_each_entry(m, &connector->probed_modes, head)
+               if (m->hdisplay == hsize && m->vdisplay == vsize &&
+                   drm_mode_vrefresh(m) == vrefresh_rate)
+                       return NULL;
+
        /* HDTV hack, part 2 */
        if (hsize == 1366 && vsize == 768 && vrefresh_rate == 60) {
                mode = drm_cvt_mode(dev, 1366, 768, vrefresh_rate, 0, 0,
@@ -784,9 +869,8 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev,
                return mode;
        }
 
-       mode = NULL;
        /* check whether it can be found in default mode table */
-       mode = drm_find_dmt(dev, hsize, vsize, vrefresh_rate);
+       mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate);
        if (mode)
                return mode;
 
@@ -796,6 +880,23 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev,
        case LEVEL_GTF:
                mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
                break;
+       case LEVEL_GTF2:
+               /*
+                * This is potentially wrong if there's ever a monitor with
+                * more than one ranges section, each claiming a different
+                * secondary GTF curve.  Please don't do that.
+                */
+               mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
+               if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) {
+                       kfree(mode);
+                       mode = drm_gtf_mode_complex(dev, hsize, vsize,
+                                                   vrefresh_rate, 0, 0,
+                                                   drm_gtf2_m(edid),
+                                                   drm_gtf2_2c(edid),
+                                                   drm_gtf2_k(edid),
+                                                   drm_gtf2_2j(edid));
+               }
+               break;
        case LEVEL_CVT:
                mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0,
                                    false);
@@ -919,10 +1020,10 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
        if (mode->vsync_end > mode->vtotal)
                mode->vtotal = mode->vsync_end + 1;
 
-       drm_mode_set_name(mode);
-
        drm_mode_do_interlace_quirk(mode, pt);
 
+       drm_mode_set_name(mode);
+
        if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
                pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE;
        }
@@ -1032,19 +1133,6 @@ static int add_established_modes(struct drm_connector *connector, struct edid *e
 
        return modes;
 }
-/**
- * stanard_timing_level - get std. timing level(CVT/GTF/DMT)
- * @edid: EDID block to scan
- */
-static int standard_timing_level(struct edid *edid)
-{
-       if (edid->revision >= 2) {
-               if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF))
-                       return LEVEL_CVT;
-               return LEVEL_GTF;
-       }
-       return LEVEL_DMT;
-}
 
 /**
  * add_standard_modes - get std. modes from EDID and add them
@@ -1055,18 +1143,14 @@ static int standard_timing_level(struct edid *edid)
  */
 static int add_standard_modes(struct drm_connector *connector, struct edid *edid)
 {
-       struct drm_device *dev = connector->dev;
        int i, modes = 0;
-       int timing_level;
-
-       timing_level = standard_timing_level(edid);
 
        for (i = 0; i < EDID_STD_TIMINGS; i++) {
-               struct std_timing *t = &edid->standard_timings[i];
                struct drm_display_mode *newmode;
 
-               newmode = drm_mode_std(dev, &edid->standard_timings[i],
-                                      edid->revision, timing_level);
+               newmode = drm_mode_std(connector, edid,
+                                      &edid->standard_timings[i],
+                                      edid->revision);
                if (newmode) {
                        drm_mode_probed_add(connector, newmode);
                        modes++;
@@ -1132,9 +1216,6 @@ range_pixel_clock(struct edid *edid, u8 *t)
        return t[9] * 10000 + 5001;
 }
 
-/*
- * XXX fix this for GTF secondary curve formula
- */
 static bool
 mode_in_range(struct drm_display_mode *mode, struct edid *edid,
              struct detailed_timing *timing)
@@ -1306,14 +1387,14 @@ drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
        for (i = 0; i < 6; i++) {
                for (j = 7; j > 0; j--) {
                        m = (i * 8) + (7 - j);
-                       if (m > num_est3_modes)
+                       if (m >= num_est3_modes)
                                break;
                        if (est[i] & (1 << j)) {
-                               mode = drm_find_dmt(connector->dev,
-                                                   est3_modes[m].w,
-                                                   est3_modes[m].h,
-                                                   est3_modes[m].r
-                                                   /*, est3_modes[m].rb */);
+                               mode = drm_mode_find_dmt(connector->dev,
+                                                        est3_modes[m].w,
+                                                        est3_modes[m].h,
+                                                        est3_modes[m].r
+                                                        /*, est3_modes[m].rb */);
                                if (mode) {
                                        drm_mode_probed_add(connector, mode);
                                        modes++;
@@ -1331,7 +1412,6 @@ static int add_detailed_modes(struct drm_connector *connector,
 {
        int i, modes = 0;
        struct detailed_non_pixel *data = &timing->data.other_data;
-       int timing_level = standard_timing_level(edid);
        int gtf = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF);
        struct drm_display_mode *newmode;
        struct drm_device *dev = connector->dev;
@@ -1362,8 +1442,8 @@ static int add_detailed_modes(struct drm_connector *connector,
                        struct drm_display_mode *newmode;
 
                        std = &data->data.timings[i];
-                       newmode = drm_mode_std(dev, std, edid->revision,
-                                              timing_level);
+                       newmode = drm_mode_std(connector, edid, std,
+                                              edid->revision);
                        if (newmode) {
                                drm_mode_probed_add(connector, newmode);
                                modes++;
@@ -1433,7 +1513,6 @@ static int add_detailed_info_eedid(struct drm_connector *connector,
        char *edid_ext = NULL;
        struct detailed_timing *timing;
        int start_offset, end_offset;
-       int timing_level;
 
        if (edid->version == 1 && edid->revision < 3)
                return 0;
@@ -1460,7 +1539,6 @@ static int add_detailed_info_eedid(struct drm_connector *connector,
                return 0;
        }
 
-       timing_level = standard_timing_level(edid);
        end_offset = EDID_LENGTH;
        end_offset -= sizeof(struct detailed_timing);
        for (i = start_offset; i < end_offset;