drm/i915: use the default context
[deliverable/linux.git] / drivers / gpu / drm / drm_edid.c
index 5a18b0df82850a046608c57c7fedb4cf75d03e4b..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;
                        }
@@ -486,23 +491,47 @@ static void edid_fixup_preferred(struct drm_connector *connector,
        preferred_mode->type |= DRM_MODE_TYPE_PREFERRED;
 }
 
+static bool
+mode_is_rb(const struct drm_display_mode *mode)
+{
+       return (mode->htotal - mode->hdisplay == 160) &&
+              (mode->hsync_end - mode->hdisplay == 80) &&
+              (mode->hsync_end - mode->hsync_start == 32) &&
+              (mode->vsync_start - mode->vdisplay == 3);
+}
+
+/*
+ * drm_mode_find_dmt - Create a copy of a mode if present in DMT
+ * @dev: Device to duplicate against
+ * @hsize: Mode width
+ * @vsize: Mode height
+ * @fresh: Mode refresh rate
+ * @rb: Mode reduced-blanking-ness
+ *
+ * Walk the DMT mode list looking for a match for the given parameters.
+ * Return a newly allocated copy of the mode, or NULL if not found.
+ */
 struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
-                                          int hsize, int vsize, int fresh)
+                                          int hsize, int vsize, int fresh,
+                                          bool rb)
 {
-       struct drm_display_mode *mode = NULL;
        int i;
 
        for (i = 0; i < drm_num_dmt_modes; i++) {
                const struct drm_display_mode *ptr = &drm_dmt_modes[i];
-               if (hsize == ptr->hdisplay &&
-                       vsize == ptr->vdisplay &&
-                       fresh == drm_mode_vrefresh(ptr)) {
-                       /* get the expected default mode */
-                       mode = drm_mode_duplicate(dev, ptr);
-                       break;
-               }
+               if (hsize != ptr->hdisplay)
+                       continue;
+               if (vsize != ptr->vdisplay)
+                       continue;
+               if (fresh != drm_mode_vrefresh(ptr))
+                       continue;
+               if (rb != mode_is_rb(ptr))
+                       continue;
+
+               return drm_mode_duplicate(dev, ptr);
        }
-       return mode;
+
+       return NULL;
 }
 EXPORT_SYMBOL(drm_mode_find_dmt);
 
@@ -731,10 +760,17 @@ drm_mode_std(struct drm_connector *connector, struct edid *edid,
        }
 
        /* check whether it can be found in default mode table */
-       mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate);
+       if (drm_monitor_supports_rb(edid)) {
+               mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate,
+                                        true);
+               if (mode)
+                       return mode;
+       }
+       mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate, false);
        if (mode)
                return mode;
 
+       /* okay, generate it */
        switch (timing_level) {
        case LEVEL_DMT:
                break;
@@ -748,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,
@@ -852,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);
 
@@ -881,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;
        }
@@ -892,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;
 
@@ -905,16 +949,10 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
                mode->height_mm = edid->height_cm * 10;
        }
 
-       return mode;
-}
+       mode->type = DRM_MODE_TYPE_DRIVER;
+       drm_mode_set_name(mode);
 
-static bool
-mode_is_rb(const struct drm_display_mode *mode)
-{
-       return (mode->htotal - mode->hdisplay == 160) &&
-              (mode->hsync_end - mode->hdisplay == 80) &&
-              (mode->hsync_end - mode->hsync_start == 32) &&
-              (mode->vsync_start - mode->vdisplay == 3);
+       return mode;
 }
 
 static bool
@@ -994,12 +1032,8 @@ mode_in_range(const struct drm_display_mode *mode, struct edid *edid,
        return true;
 }
 
-/*
- * XXX If drm_dmt_modes ever regrows the CVT-R modes (and it will) this will
- * need to account for them.
- */
 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;
@@ -1019,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 (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 */
 
-       if (gtf && data->type == EDID_DETAIL_MONITOR_RANGE)
+       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
@@ -1062,8 +1189,8 @@ drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
                                mode = drm_mode_find_dmt(connector->dev,
                                                         est3_modes[m].w,
                                                         est3_modes[m].h,
-                                                        est3_modes[m].r
-                                                        /*, est3_modes[m].rb */);
+                                                        est3_modes[m].r,
+                                                        est3_modes[m].rb);
                                if (mode) {
                                        drm_mode_probed_add(connector, mode);
                                        modes++;
@@ -1312,6 +1439,8 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
 #define VENDOR_BLOCK    0x03
 #define SPEAKER_BLOCK  0x04
 #define EDID_BASIC_AUDIO       (1 << 6)
+#define EDID_CEA_YCRCB444      (1 << 5)
+#define EDID_CEA_YCRCB422      (1 << 4)
 
 /**
  * Search EDID for CEA extension block.
@@ -1666,13 +1795,29 @@ static void drm_add_display_info(struct edid *edid,
        info->bpc = 0;
        info->color_formats = 0;
 
-       /* Only defined for 1.4 with digital displays */
-       if (edid->revision < 4)
+       if (edid->revision < 3)
                return;
 
        if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
                return;
 
+       /* Get data from CEA blocks if present */
+       edid_ext = drm_find_cea_extension(edid);
+       if (edid_ext) {
+               info->cea_rev = edid_ext[1];
+
+               /* The existence of a CEA block should imply RGB support */
+               info->color_formats = DRM_COLOR_FORMAT_RGB444;
+               if (edid_ext[3] & EDID_CEA_YCRCB444)
+                       info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
+               if (edid_ext[3] & EDID_CEA_YCRCB422)
+                       info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
+       }
+
+       /* Only defined for 1.4 with digital displays */
+       if (edid->revision < 4)
+               return;
+
        switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) {
        case DRM_EDID_DIGITAL_DEPTH_6:
                info->bpc = 6;
@@ -1698,18 +1843,11 @@ static void drm_add_display_info(struct edid *edid,
                break;
        }
 
-       info->color_formats = DRM_COLOR_FORMAT_RGB444;
-       if (info->color_formats & DRM_EDID_FEATURE_RGB_YCRCB444)
-               info->color_formats = DRM_COLOR_FORMAT_YCRCB444;
-       if (info->color_formats & DRM_EDID_FEATURE_RGB_YCRCB422)
-               info->color_formats = DRM_COLOR_FORMAT_YCRCB422;
-
-       /* Get data from CEA blocks if present */
-       edid_ext = drm_find_cea_extension(edid);
-       if (!edid_ext)
-               return;
-
-       info->cea_rev = edid_ext[1];
+       info->color_formats |= DRM_COLOR_FORMAT_RGB444;
+       if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444)
+               info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
+       if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
+               info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
 }
 
 /**
This page took 0.031634 seconds and 5 git commands to generate.