drm/i915: use the default context
[deliverable/linux.git] / drivers / gpu / drm / drm_edid.c
index 043fa5a2412c6fb053323474b60020ecb6fc0183..c3b5139eba7f8eec492ab8d5c818638d897cb272 100644 (file)
@@ -66,6 +66,8 @@
 #define EDID_QUIRK_FIRST_DETAILED_PREFERRED    (1 << 5)
 /* use +hsync +vsync for detailed mode */
 #define EDID_QUIRK_DETAILED_SYNC_PP            (1 << 6)
+/* Force reduced-blanking timings for detailed modes */
+#define EDID_QUIRK_FORCE_REDUCED_BLANKING      (1 << 7)
 
 struct detailed_mode_closure {
        struct drm_connector *connector;
@@ -81,7 +83,7 @@ struct detailed_mode_closure {
 #define LEVEL_CVT      3
 
 static struct edid_quirk {
-       char *vendor;
+       char vendor[4];
        int product_id;
        u32 quirks;
 } edid_quirk_list[] = {
@@ -120,6 +122,9 @@ static struct edid_quirk {
        /* Samsung SyncMaster 22[5-6]BW */
        { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 },
        { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 },
+
+       /* ViewSonic VA2026w */
+       { "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING },
 };
 
 /*** DDC fetch and block validation ***/
@@ -149,13 +154,13 @@ EXPORT_SYMBOL(drm_edid_header_is_valid);
  * Sanity check the EDID block (base or extension).  Return 0 if the block
  * doesn't check out, or 1 if it's valid.
  */
-bool drm_edid_block_valid(u8 *raw_edid)
+bool drm_edid_block_valid(u8 *raw_edid, int block)
 {
        int i;
        u8 csum = 0;
        struct edid *edid = (struct edid *)raw_edid;
 
-       if (raw_edid[0] == 0x00) {
+       if (block == 0) {
                int score = drm_edid_header_is_valid(raw_edid);
                if (score == 8) ;
                else if (score >= 6) {
@@ -219,7 +224,7 @@ bool drm_edid_is_valid(struct edid *edid)
                return false;
 
        for (i = 0; i <= edid->extensions; i++)
-               if (!drm_edid_block_valid(raw + i * EDID_LENGTH))
+               if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i))
                        return false;
 
        return true;
@@ -299,7 +304,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
        for (i = 0; i < 4; i++) {
                if (drm_do_probe_ddc_edid(adapter, block, 0, EDID_LENGTH))
                        goto out;
-               if (drm_edid_block_valid(block))
+               if (drm_edid_block_valid(block, 0))
                        break;
                if (i == 0 && drm_edid_is_zero(block, EDID_LENGTH)) {
                        connector->null_edid_counter++;
@@ -324,7 +329,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
                                  block + (valid_extensions + 1) * EDID_LENGTH,
                                  j, EDID_LENGTH))
                                goto out;
-                       if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH)) {
+                       if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH, j)) {
                                valid_extensions++;
                                break;
                        }
@@ -779,6 +784,8 @@ drm_mode_std(struct drm_connector *connector, struct edid *edid,
                 * secondary GTF curve.  Please don't do that.
                 */
                mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
+               if (!mode)
+                       return NULL;
                if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) {
                        drm_mode_destroy(dev, mode);
                        mode = drm_gtf_mode_complex(dev, hsize, vsize,
@@ -883,12 +890,19 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
                                "Wrong Hsync/Vsync pulse width\n");
                return NULL;
        }
+
+       if (quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) {
+               mode = drm_cvt_mode(dev, hactive, vactive, 60, true, false, false);
+               if (!mode)
+                       return NULL;
+
+               goto set_size;
+       }
+
        mode = drm_mode_create(dev);
        if (!mode)
                return NULL;
 
-       mode->type = DRM_MODE_TYPE_DRIVER;
-
        if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH)
                timing->pixel_clock = cpu_to_le16(1088);
 
@@ -912,8 +926,6 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
 
        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;
        }
@@ -923,6 +935,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
        mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ?
                DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
 
+set_size:
        mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4;
        mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8;
 
@@ -936,6 +949,9 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
                mode->height_mm = edid->height_cm * 10;
        }
 
+       mode->type = DRM_MODE_TYPE_DRIVER;
+       drm_mode_set_name(mode);
+
        return mode;
 }
 
@@ -1017,7 +1033,7 @@ mode_in_range(const struct drm_display_mode *mode, struct edid *edid,
 }
 
 static int
-drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
+drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid,
                        struct detailed_timing *timing)
 {
        int i, modes = 0;
@@ -1037,17 +1053,110 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
        return modes;
 }
 
+/* fix up 1366x768 mode from 1368x768;
+ * GFT/CVT can't express 1366 width which isn't dividable by 8
+ */
+static void fixup_mode_1366x768(struct drm_display_mode *mode)
+{
+       if (mode->hdisplay == 1368 && mode->vdisplay == 768) {
+               mode->hdisplay = 1366;
+               mode->hsync_start--;
+               mode->hsync_end--;
+               drm_mode_set_name(mode);
+       }
+}
+
+static int
+drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
+                       struct detailed_timing *timing)
+{
+       int i, modes = 0;
+       struct drm_display_mode *newmode;
+       struct drm_device *dev = connector->dev;
+
+       for (i = 0; i < num_extra_modes; i++) {
+               const struct minimode *m = &extra_modes[i];
+               newmode = drm_gtf_mode(dev, m->w, m->h, m->r, 0, 0);
+               if (!newmode)
+                       return modes;
+
+               fixup_mode_1366x768(newmode);
+               if (!mode_in_range(newmode, edid, timing)) {
+                       drm_mode_destroy(dev, newmode);
+                       continue;
+               }
+
+               drm_mode_probed_add(connector, newmode);
+               modes++;
+       }
+
+       return modes;
+}
+
+static int
+drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid,
+                       struct detailed_timing *timing)
+{
+       int i, modes = 0;
+       struct drm_display_mode *newmode;
+       struct drm_device *dev = connector->dev;
+       bool rb = drm_monitor_supports_rb(edid);
+
+       for (i = 0; i < num_extra_modes; i++) {
+               const struct minimode *m = &extra_modes[i];
+               newmode = drm_cvt_mode(dev, m->w, m->h, m->r, rb, 0, 0);
+               if (!newmode)
+                       return modes;
+
+               fixup_mode_1366x768(newmode);
+               if (!mode_in_range(newmode, edid, timing)) {
+                       drm_mode_destroy(dev, newmode);
+                       continue;
+               }
+
+               drm_mode_probed_add(connector, newmode);
+               modes++;
+       }
+
+       return modes;
+}
+
 static void
 do_inferred_modes(struct detailed_timing *timing, void *c)
 {
        struct detailed_mode_closure *closure = c;
        struct detailed_non_pixel *data = &timing->data.other_data;
-       int gtf = (closure->edid->features & DRM_EDID_FEATURE_DEFAULT_GTF);
+       struct detailed_data_monitor_range *range = &data->data.range;
 
-       if (gtf && data->type == EDID_DETAIL_MONITOR_RANGE)
+       if (data->type != EDID_DETAIL_MONITOR_RANGE)
+               return;
+
+       closure->modes += drm_dmt_modes_for_range(closure->connector,
+                                                 closure->edid,
+                                                 timing);
+       
+       if (!version_greater(closure->edid, 1, 1))
+               return; /* GTF not defined yet */
+
+       switch (range->flags) {
+       case 0x02: /* secondary gtf, XXX could do more */
+       case 0x00: /* default gtf */
                closure->modes += drm_gtf_modes_for_range(closure->connector,
                                                          closure->edid,
                                                          timing);
+               break;
+       case 0x04: /* cvt, only in 1.4+ */
+               if (!version_greater(closure->edid, 1, 3))
+                       break;
+
+               closure->modes += drm_cvt_modes_for_range(closure->connector,
+                                                         closure->edid,
+                                                         timing);
+               break;
+       case 0x01: /* just the ranges, no formula */
+       default:
+               break;
+       }
 }
 
 static int
This page took 0.028412 seconds and 5 git commands to generate.