drm/edid: Fix interlaced detailed timings to be frame size, not field.
authorAdam Jackson <ajax@redhat.com>
Mon, 15 Feb 2010 22:15:39 +0000 (22:15 +0000)
committerDave Airlie <airlied@redhat.com>
Thu, 18 Feb 2010 05:03:16 +0000 (15:03 +1000)
cf. https://bugzilla.redhat.com/show_bug.cgi?id=533561

Signed-off-by: Adam Jackson <ajax@redhat.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/drm_edid.c

index f665b05..ab6c973 100644 (file)
@@ -598,6 +598,50 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev,
        return mode;
 }
 
+/*
+ * EDID is delightfully ambiguous about how interlaced modes are to be
+ * encoded.  Our internal representation is of frame height, but some
+ * HDTV detailed timings are encoded as field height.
+ *
+ * The format list here is from CEA, in frame size.  Technically we
+ * should be checking refresh rate too.  Whatever.
+ */
+static void
+drm_mode_do_interlace_quirk(struct drm_display_mode *mode,
+                           struct detailed_pixel_timing *pt)
+{
+       int i;
+       static const struct {
+               int w, h;
+       } cea_interlaced[] = {
+               { 1920, 1080 },
+               {  720,  480 },
+               { 1440,  480 },
+               { 2880,  480 },
+               {  720,  576 },
+               { 1440,  576 },
+               { 2880,  576 },
+       };
+       static const int n_sizes =
+               sizeof(cea_interlaced)/sizeof(cea_interlaced[0]);
+
+       if (!(pt->misc & DRM_EDID_PT_INTERLACED))
+               return;
+
+       for (i = 0; i < n_sizes; i++) {
+               if ((mode->hdisplay == cea_interlaced[i].w) &&
+                   (mode->vdisplay == cea_interlaced[i].h / 2)) {
+                       mode->vdisplay *= 2;
+                       mode->vsync_start *= 2;
+                       mode->vsync_end *= 2;
+                       mode->vtotal *= 2;
+                       mode->vtotal |= 1;
+               }
+       }
+
+       mode->flags |= DRM_MODE_FLAG_INTERLACE;
+}
+
 /**
  * drm_mode_detailed - create a new mode from an EDID detailed timing section
  * @dev: DRM device (needed to create new mode)
@@ -680,8 +724,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
 
        drm_mode_set_name(mode);
 
-       if (pt->misc & DRM_EDID_PT_INTERLACED)
-               mode->flags |= DRM_MODE_FLAG_INTERLACE;
+       drm_mode_do_interlace_quirk(mode, pt);
 
        if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
                pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE;