drm/edid: Decode 3-byte CVT codes from EDID 1.4
authorAdam Jackson <ajax@redhat.com>
Thu, 3 Dec 2009 22:44:40 +0000 (17:44 -0500)
committerDave Airlie <airlied@redhat.com>
Thu, 3 Dec 2009 22:53:47 +0000 (08:53 +1000)
Signed-off-by: Adam Jackson <ajax@redhat.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/drm_edid.c
include/drm/drm_edid.h

index cc8e696827141fffa524d9506f5ea354b3a6d3c7..30af8f3e6427d9f135e042976f1848c574b7f3b3 100644 (file)
@@ -897,6 +897,51 @@ static int drm_gtf_modes_for_range(struct drm_connector *connector,
        return modes;
 }
 
+static int drm_cvt_modes(struct drm_connector *connector,
+                        struct detailed_timing *timing)
+{
+       int i, j, modes = 0;
+       struct drm_display_mode *newmode;
+       struct drm_device *dev = connector->dev;
+       struct cvt_timing *cvt;
+       const int rates[] = { 60, 85, 75, 60, 50 };
+
+       for (i = 0; i < 4; i++) {
+               int width, height;
+               cvt = &(timing->data.other_data.data.cvt[i]);
+
+               height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 8) + 1) * 2;
+               switch (cvt->code[1] & 0xc0) {
+               case 0x00:
+                       width = height * 4 / 3;
+                       break;
+               case 0x40:
+                       width = height * 16 / 9;
+                       break;
+               case 0x80:
+                       width = height * 16 / 10;
+                       break;
+               case 0xc0:
+                       width = height * 15 / 9;
+                       break;
+               }
+
+               for (j = 1; j < 5; j++) {
+                       if (cvt->code[2] & (1 << j)) {
+                               newmode = drm_cvt_mode(dev, width, height,
+                                                      rates[j], j == 0,
+                                                      false, false);
+                               if (newmode) {
+                                       drm_mode_probed_add(connector, newmode);
+                                       modes++;
+                               }
+                       }
+               }
+       }
+
+       return modes;
+}
+
 static int add_detailed_modes(struct drm_connector *connector,
                              struct detailed_timing *timing,
                              struct edid *edid, u32 quirks, int preferred)
@@ -941,6 +986,9 @@ static int add_detailed_modes(struct drm_connector *connector,
                        }
                }
                break;
+       case EDID_DETAIL_CVT_3BYTE:
+               modes += drm_cvt_modes(connector, timing);
+               break;
        default:
                break;
        }
index 9087557fd83df20b55a9f9699c8517fc1c476adb..d33c3e038606c1354d671d90a9d3d776d4e225a7 100644 (file)
@@ -106,6 +106,10 @@ struct detailed_data_color_point {
        u8 wpindex2[3];
 } __attribute__((packed));
 
+struct cvt_timing {
+       u8 code[3];
+} __attribute__((packed));
+
 struct detailed_non_pixel {
        u8 pad1;
        u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name
@@ -117,6 +121,7 @@ struct detailed_non_pixel {
                struct detailed_data_monitor_range range;
                struct detailed_data_wpindex color;
                struct std_timing timings[5];
+               struct cvt_timing cvt[4];
        } data;
 } __attribute__((packed));