Merge branch 'x86-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / drivers / gpu / drm / drm_edid.c
index f87bf10..c1a2621 100644 (file)
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
 #include "drmP.h"
 #include "drm_edid.h"
+#include "drm_edid_modes.h"
+
+#define version_greater(edid, maj, min) \
+       (((edid)->version > (maj)) || \
+        ((edid)->version == (maj) && (edid)->revision > (min)))
 
 #define EDID_EST_TIMINGS 16
 #define EDID_STD_TIMINGS 8
 /* use +hsync +vsync for detailed mode */
 #define EDID_QUIRK_DETAILED_SYNC_PP            (1 << 6)
 
+struct detailed_mode_closure {
+       struct drm_connector *connector;
+       struct edid *edid;
+       bool preferred;
+       u32 quirks;
+       int modes;
+};
 
 #define LEVEL_DMT      0
 #define LEVEL_GTF      1
@@ -282,7 +293,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
        return block;
 
 carp:
-       dev_warn(&connector->dev->pdev->dev, "%s: EDID block %d invalid.\n",
+       dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n",
                 drm_get_connector_name(connector), j);
 
 out:
@@ -375,7 +386,6 @@ static u32 edid_get_quirks(struct edid *edid)
 #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
 #define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh))
 
-
 /**
  * edid_fixup_preferred - set preferred modes based on quirk list
  * @connector: has mode list to fix up
@@ -422,245 +432,6 @@ static void edid_fixup_preferred(struct drm_connector *connector,
        preferred_mode->type |= DRM_MODE_TYPE_PREFERRED;
 }
 
-/*
- * Add the Autogenerated from the DMT spec.
- * This table is copied from xfree86/modes/xf86EdidModes.c.
- * But the mode with Reduced blank feature is deleted.
- */
-static struct drm_display_mode drm_dmt_modes[] = {
-       /* 640x350@85Hz */
-       { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
-                  736, 832, 0, 350, 382, 385, 445, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
-       /* 640x400@85Hz */
-       { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
-                  736, 832, 0, 400, 401, 404, 445, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 720x400@85Hz */
-       { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756,
-                  828, 936, 0, 400, 401, 404, 446, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 640x480@60Hz */
-       { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
-                  752, 800, 0, 480, 489, 492, 525, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
-       /* 640x480@72Hz */
-       { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
-                  704, 832, 0, 480, 489, 492, 520, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
-       /* 640x480@75Hz */
-       { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
-                  720, 840, 0, 480, 481, 484, 500, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
-       /* 640x480@85Hz */
-       { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696,
-                  752, 832, 0, 480, 481, 484, 509, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
-       /* 800x600@56Hz */
-       { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
-                  896, 1024, 0, 600, 601, 603, 625, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 800x600@60Hz */
-       { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
-                  968, 1056, 0, 600, 601, 605, 628, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 800x600@72Hz */
-       { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
-                  976, 1040, 0, 600, 637, 643, 666, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 800x600@75Hz */
-       { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
-                  896, 1056, 0, 600, 601, 604, 625, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 800x600@85Hz */
-       { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832,
-                  896, 1048, 0, 600, 601, 604, 631, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 848x480@60Hz */
-       { DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864,
-                  976, 1088, 0, 480, 486, 494, 517, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1024x768@43Hz, interlace */
-       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032,
-                  1208, 1264, 0, 768, 768, 772, 817, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
-                       DRM_MODE_FLAG_INTERLACE) },
-       /* 1024x768@60Hz */
-       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
-                  1184, 1344, 0, 768, 771, 777, 806, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
-       /* 1024x768@70Hz */
-       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
-                  1184, 1328, 0, 768, 771, 777, 806, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
-       /* 1024x768@75Hz */
-       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040,
-                  1136, 1312, 0, 768, 769, 772, 800, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1024x768@85Hz */
-       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072,
-                  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,
-                  1344, 1600, 0, 864, 865, 868, 900, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1280x768@60Hz */
-       { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
-                  1472, 1664, 0, 768, 771, 778, 798, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1280x768@75Hz */
-       { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360,
-                  1488, 1696, 0, 768, 771, 778, 805, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
-       /* 1280x768@85Hz */
-       { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360,
-                  1496, 1712, 0, 768, 771, 778, 809, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1280x800@60Hz */
-       { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
-                  1480, 1680, 0, 800, 803, 809, 831, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
-       /* 1280x800@75Hz */
-       { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360,
-                  1488, 1696, 0, 800, 803, 809, 838, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1280x800@85Hz */
-       { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360,
-                  1496, 1712, 0, 800, 803, 809, 843, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1280x960@60Hz */
-       { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
-                  1488, 1800, 0, 960, 961, 964, 1000, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1280x960@85Hz */
-       { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344,
-                  1504, 1728, 0, 960, 961, 964, 1011, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1280x1024@60Hz */
-       { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
-                  1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1280x1024@75Hz */
-       { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
-                  1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1280x1024@85Hz */
-       { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344,
-                  1504, 1728, 0, 1024, 1025, 1028, 1072, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1360x768@60Hz */
-       { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
-                  1536, 1792, 0, 768, 771, 777, 795, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1440x1050@60Hz */
-       { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
-                  1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1440x1050@75Hz */
-       { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504,
-                  1648, 1896, 0, 1050, 1053, 1057, 1099, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1440x1050@85Hz */
-       { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504,
-                  1656, 1912, 0, 1050, 1053, 1057, 1105, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1440x900@60Hz */
-       { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
-                  1672, 1904, 0, 900, 903, 909, 934, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1440x900@75Hz */
-       { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536,
-                  1688, 1936, 0, 900, 903, 909, 942, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1440x900@85Hz */
-       { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544,
-                  1696, 1952, 0, 900, 903, 909, 948, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1600x1200@60Hz */
-       { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
-                  1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1600x1200@65Hz */
-       { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664,
-                  1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1600x1200@70Hz */
-       { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664,
-                  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, 202500, 1600, 1664,
-                  1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1600x1200@85Hz */
-       { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664,
-                  1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1680x1050@60Hz */
-       { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
-                  1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1680x1050@75Hz */
-       { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800,
-                  1976, 2272, 0, 1050, 1053, 1059, 1099, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1680x1050@85Hz */
-       { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808,
-                  1984, 2288, 0, 1050, 1053, 1059, 1105, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1792x1344@60Hz */
-       { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
-                  2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1729x1344@75Hz */
-       { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888,
-                  2104, 2456, 0, 1344, 1345, 1348, 1417, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1853x1392@60Hz */
-       { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
-                  2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1856x1392@75Hz */
-       { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984,
-                  2208, 2560, 0, 1392, 1395, 1399, 1500, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1920x1200@60Hz */
-       { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
-                  2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1920x1200@75Hz */
-       { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056,
-                  2264, 2608, 0, 1200, 1203, 1209, 1255, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1920x1200@85Hz */
-       { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064,
-                  2272, 2624, 0, 1200, 1203, 1209, 1262, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1920x1440@60Hz */
-       { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
-                  2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 1920x1440@75Hz */
-       { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064,
-                  2288, 2640, 0, 1440, 1441, 1444, 1500, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 2560x1600@60Hz */
-       { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
-                  3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 2560x1600@75HZ */
-       { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768,
-                  3048, 3536, 0, 1600, 1603, 1609, 1672, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-       /* 2560x1600@85HZ */
-       { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768,
-                  3048, 3536, 0, 1600, 1603, 1609, 1682, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-};
-static const int drm_num_dmt_modes =
-       sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
-
 struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
                                           int hsize, int vsize, int fresh)
 {
@@ -684,6 +455,46 @@ EXPORT_SYMBOL(drm_mode_find_dmt);
 
 typedef void detailed_cb(struct detailed_timing *timing, void *closure);
 
+static void
+cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
+{
+       int i, n = 0;
+       u8 rev = ext[0x01], d = ext[0x02];
+       u8 *det_base = ext + d;
+
+       switch (rev) {
+       case 0:
+               /* can't happen */
+               return;
+       case 1:
+               /* have to infer how many blocks we have, check pixel clock */
+               for (i = 0; i < 6; i++)
+                       if (det_base[18*i] || det_base[18*i+1])
+                               n++;
+               break;
+       default:
+               /* explicit count */
+               n = min(ext[0x03] & 0x0f, 6);
+               break;
+       }
+
+       for (i = 0; i < n; i++)
+               cb((struct detailed_timing *)(det_base + 18 * i), closure);
+}
+
+static void
+vtb_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
+{
+       unsigned int i, n = min((int)ext[0x02], 6);
+       u8 *det_base = ext + 5;
+
+       if (ext[0x01] != 1)
+               return; /* unknown version */
+
+       for (i = 0; i < n; i++)
+               cb((struct detailed_timing *)(det_base + 18 * i), closure);
+}
+
 static void
 drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure)
 {
@@ -696,7 +507,19 @@ drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure)
        for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
                cb(&(edid->detailed_timings[i]), closure);
 
-       /* XXX extension block walk */
+       for (i = 1; i <= raw_edid[0x7e]; i++) {
+               u8 *ext = raw_edid + (i * EDID_LENGTH);
+               switch (*ext) {
+               case CEA_EXT:
+                       cea_for_each_detailed_block(ext, cb, closure);
+                       break;
+               case VTB_EXT:
+                       vtb_for_each_detailed_block(ext, cb, closure);
+                       break;
+               default:
+                       break;
+               }
+       }
 }
 
 static void
@@ -929,13 +752,11 @@ drm_mode_do_interlace_quirk(struct drm_display_mode *mode,
                { 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++) {
+       for (i = 0; i < ARRAY_SIZE(cea_interlaced); i++) {
                if ((mode->hdisplay == cea_interlaced[i].w) &&
                    (mode->vdisplay == cea_interlaced[i].h / 2)) {
                        mode->vdisplay *= 2;
@@ -1049,117 +870,6 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
        return mode;
 }
 
-/*
- * Detailed mode info for the EDID "established modes" data to use.
- */
-static struct drm_display_mode edid_est_modes[] = {
-       { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
-                  968, 1056, 0, 600, 601, 605, 628, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */
-       { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
-                  896, 1024, 0, 600, 601, 603,  625, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */
-       { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
-                  720, 840, 0, 480, 481, 484, 500, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */
-       { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
-                  704,  832, 0, 480, 489, 491, 520, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */
-       { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704,
-                  768,  864, 0, 480, 483, 486, 525, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */
-       { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656,
-                  752, 800, 0, 480, 490, 492, 525, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */
-       { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738,
-                  846, 900, 0, 400, 421, 423,  449, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */
-       { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738,
-                  846,  900, 0, 400, 412, 414, 449, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */
-       { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
-                  1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */
-       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040,
-                  1136, 1312, 0,  768, 769, 772, 800, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */
-       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
-                  1184, 1328, 0,  768, 771, 777, 806, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */
-       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
-                  1184, 1344, 0,  768, 771, 777, 806, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */
-       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
-                  1208, 1264, 0, 768, 768, 776, 817, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */
-       { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
-                  928, 1152, 0, 624, 625, 628, 667, 0,
-                  DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */
-       { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
-                  896, 1056, 0, 600, 601, 604,  625, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */
-       { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
-                  976, 1040, 0, 600, 637, 643, 666, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */
-       { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
-                  1344, 1600, 0,  864, 865, 868, 900, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */
-};
-
-/**
- * add_established_modes - get est. modes from EDID and add them
- * @edid: EDID block to scan
- *
- * Each EDID block contains a bitmap of the supported "established modes" list
- * (defined above).  Tease them out and add them to the global modes list.
- */
-static int add_established_modes(struct drm_connector *connector, struct edid *edid)
-{
-       struct drm_device *dev = connector->dev;
-       unsigned long est_bits = edid->established_timings.t1 |
-               (edid->established_timings.t2 << 8) |
-               ((edid->established_timings.mfg_rsvd & 0x80) << 9);
-       int i, modes = 0;
-
-       for (i = 0; i <= EDID_EST_TIMINGS; i++)
-               if (est_bits & (1<<i)) {
-                       struct drm_display_mode *newmode;
-                       newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
-                       if (newmode) {
-                               drm_mode_probed_add(connector, newmode);
-                               modes++;
-                       }
-               }
-
-       return modes;
-}
-
-/**
- * add_standard_modes - get std. modes from EDID and add them
- * @edid: EDID block to scan
- *
- * Standard modes can be calculated using the CVT standard.  Grab them from
- * @edid, calculate them, and add them to the list.
- */
-static int add_standard_modes(struct drm_connector *connector, struct edid *edid)
-{
-       int i, modes = 0;
-
-       for (i = 0; i < EDID_STD_TIMINGS; i++) {
-               struct drm_display_mode *newmode;
-
-               newmode = drm_mode_std(connector, edid,
-                                      &edid->standard_timings[i],
-                                      edid->revision);
-               if (newmode) {
-                       drm_mode_probed_add(connector, newmode);
-                       modes++;
-               }
-       }
-
-       return modes;
-}
-
 static bool
 mode_is_rb(struct drm_display_mode *mode)
 {
@@ -1269,6 +979,170 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
        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);
+
+       if (gtf && data->type == EDID_DETAIL_MONITOR_RANGE)
+               closure->modes += drm_gtf_modes_for_range(closure->connector,
+                                                         closure->edid,
+                                                         timing);
+}
+
+static int
+add_inferred_modes(struct drm_connector *connector, struct edid *edid)
+{
+       struct detailed_mode_closure closure = {
+               connector, edid, 0, 0, 0
+       };
+
+       if (version_greater(edid, 1, 0))
+               drm_for_each_detailed_block((u8 *)edid, do_inferred_modes,
+                                           &closure);
+
+       return closure.modes;
+}
+
+static int
+drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
+{
+       int i, j, m, modes = 0;
+       struct drm_display_mode *mode;
+       u8 *est = ((u8 *)timing) + 5;
+
+       for (i = 0; i < 6; i++) {
+               for (j = 7; j > 0; j--) {
+                       m = (i * 8) + (7 - j);
+                       if (m >= ARRAY_SIZE(est3_modes))
+                               break;
+                       if (est[i] & (1 << j)) {
+                               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++;
+                               }
+                       }
+               }
+       }
+
+       return modes;
+}
+
+static void
+do_established_modes(struct detailed_timing *timing, void *c)
+{
+       struct detailed_mode_closure *closure = c;
+       struct detailed_non_pixel *data = &timing->data.other_data;
+
+       if (data->type == EDID_DETAIL_EST_TIMINGS)
+               closure->modes += drm_est3_modes(closure->connector, timing);
+}
+
+/**
+ * add_established_modes - get est. modes from EDID and add them
+ * @edid: EDID block to scan
+ *
+ * Each EDID block contains a bitmap of the supported "established modes" list
+ * (defined above).  Tease them out and add them to the global modes list.
+ */
+static int
+add_established_modes(struct drm_connector *connector, struct edid *edid)
+{
+       struct drm_device *dev = connector->dev;
+       unsigned long est_bits = edid->established_timings.t1 |
+               (edid->established_timings.t2 << 8) |
+               ((edid->established_timings.mfg_rsvd & 0x80) << 9);
+       int i, modes = 0;
+       struct detailed_mode_closure closure = {
+               connector, edid, 0, 0, 0
+       };
+
+       for (i = 0; i <= EDID_EST_TIMINGS; i++) {
+               if (est_bits & (1<<i)) {
+                       struct drm_display_mode *newmode;
+                       newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
+                       if (newmode) {
+                               drm_mode_probed_add(connector, newmode);
+                               modes++;
+                       }
+               }
+       }
+
+       if (version_greater(edid, 1, 0))
+                   drm_for_each_detailed_block((u8 *)edid,
+                                               do_established_modes, &closure);
+
+       return modes + closure.modes;
+}
+
+static void
+do_standard_modes(struct detailed_timing *timing, void *c)
+{
+       struct detailed_mode_closure *closure = c;
+       struct detailed_non_pixel *data = &timing->data.other_data;
+       struct drm_connector *connector = closure->connector;
+       struct edid *edid = closure->edid;
+
+       if (data->type == EDID_DETAIL_STD_MODES) {
+               int i;
+               for (i = 0; i < 6; i++) {
+                       struct std_timing *std;
+                       struct drm_display_mode *newmode;
+
+                       std = &data->data.timings[i];
+                       newmode = drm_mode_std(connector, edid, std,
+                                              edid->revision);
+                       if (newmode) {
+                               drm_mode_probed_add(connector, newmode);
+                               closure->modes++;
+                       }
+               }
+       }
+}
+
+/**
+ * add_standard_modes - get std. modes from EDID and add them
+ * @edid: EDID block to scan
+ *
+ * Standard modes can be calculated using the appropriate standard (DMT,
+ * GTF or CVT. Grab them from @edid and add them to the list.
+ */
+static int
+add_standard_modes(struct drm_connector *connector, struct edid *edid)
+{
+       int i, modes = 0;
+       struct detailed_mode_closure closure = {
+               connector, edid, 0, 0, 0
+       };
+
+       for (i = 0; i < EDID_STD_TIMINGS; i++) {
+               struct drm_display_mode *newmode;
+
+               newmode = drm_mode_std(connector, edid,
+                                      &edid->standard_timings[i],
+                                      edid->revision);
+               if (newmode) {
+                       drm_mode_probed_add(connector, newmode);
+                       modes++;
+               }
+       }
+
+       if (version_greater(edid, 1, 0))
+               drm_for_each_detailed_block((u8 *)edid, do_standard_modes,
+                                           &closure);
+
+       /* XXX should also look for standard codes in VTB blocks */
+
+       return modes + closure.modes;
+}
+
 static int drm_cvt_modes(struct drm_connector *connector,
                         struct detailed_timing *timing)
 {
@@ -1318,240 +1192,110 @@ static int drm_cvt_modes(struct drm_connector *connector,
        return modes;
 }
 
-static const struct {
-       short w;
-       short h;
-       short r;
-       short rb;
-} est3_modes[] = {
-       /* byte 6 */
-       { 640, 350, 85, 0 },
-       { 640, 400, 85, 0 },
-       { 720, 400, 85, 0 },
-       { 640, 480, 85, 0 },
-       { 848, 480, 60, 0 },
-       { 800, 600, 85, 0 },
-       { 1024, 768, 85, 0 },
-       { 1152, 864, 75, 0 },
-       /* byte 7 */
-       { 1280, 768, 60, 1 },
-       { 1280, 768, 60, 0 },
-       { 1280, 768, 75, 0 },
-       { 1280, 768, 85, 0 },
-       { 1280, 960, 60, 0 },
-       { 1280, 960, 85, 0 },
-       { 1280, 1024, 60, 0 },
-       { 1280, 1024, 85, 0 },
-       /* byte 8 */
-       { 1360, 768, 60, 0 },
-       { 1440, 900, 60, 1 },
-       { 1440, 900, 60, 0 },
-       { 1440, 900, 75, 0 },
-       { 1440, 900, 85, 0 },
-       { 1400, 1050, 60, 1 },
-       { 1400, 1050, 60, 0 },
-       { 1400, 1050, 75, 0 },
-       /* byte 9 */
-       { 1400, 1050, 85, 0 },
-       { 1680, 1050, 60, 1 },
-       { 1680, 1050, 60, 0 },
-       { 1680, 1050, 75, 0 },
-       { 1680, 1050, 85, 0 },
-       { 1600, 1200, 60, 0 },
-       { 1600, 1200, 65, 0 },
-       { 1600, 1200, 70, 0 },
-       /* byte 10 */
-       { 1600, 1200, 75, 0 },
-       { 1600, 1200, 85, 0 },
-       { 1792, 1344, 60, 0 },
-       { 1792, 1344, 85, 0 },
-       { 1856, 1392, 60, 0 },
-       { 1856, 1392, 75, 0 },
-       { 1920, 1200, 60, 1 },
-       { 1920, 1200, 60, 0 },
-       /* byte 11 */
-       { 1920, 1200, 75, 0 },
-       { 1920, 1200, 85, 0 },
-       { 1920, 1440, 60, 0 },
-       { 1920, 1440, 75, 0 },
-};
-static const int num_est3_modes = sizeof(est3_modes) / sizeof(est3_modes[0]);
+static void
+do_cvt_mode(struct detailed_timing *timing, void *c)
+{
+       struct detailed_mode_closure *closure = c;
+       struct detailed_non_pixel *data = &timing->data.other_data;
+
+       if (data->type == EDID_DETAIL_CVT_3BYTE)
+               closure->modes += drm_cvt_modes(closure->connector, timing);
+}
 
 static int
-drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
-{
-       int i, j, m, modes = 0;
-       struct drm_display_mode *mode;
-       u8 *est = ((u8 *)timing) + 5;
+add_cvt_modes(struct drm_connector *connector, struct edid *edid)
+{      
+       struct detailed_mode_closure closure = {
+               connector, edid, 0, 0, 0
+       };
 
-       for (i = 0; i < 6; i++) {
-               for (j = 7; j > 0; j--) {
-                       m = (i * 8) + (7 - j);
-                       if (m >= num_est3_modes)
-                               break;
-                       if (est[i] & (1 << j)) {
-                               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++;
-                               }
-                       }
-               }
-       }
+       if (version_greater(edid, 1, 2))
+               drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure);
 
-       return modes;
+       /* XXX should also look for CVT codes in VTB blocks */
+
+       return closure.modes;
 }
 
-static int add_detailed_modes(struct drm_connector *connector,
-                             struct detailed_timing *timing,
-                             struct edid *edid, u32 quirks, int preferred)
+static void
+do_detailed_mode(struct detailed_timing *timing, void *c)
 {
-       int i, modes = 0;
-       struct detailed_non_pixel *data = &timing->data.other_data;
-       int gtf = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF);
+       struct detailed_mode_closure *closure = c;
        struct drm_display_mode *newmode;
-       struct drm_device *dev = connector->dev;
 
        if (timing->pixel_clock) {
-               newmode = drm_mode_detailed(dev, edid, timing, quirks);
+               newmode = drm_mode_detailed(closure->connector->dev,
+                                           closure->edid, timing,
+                                           closure->quirks);
                if (!newmode)
-                       return 0;
+                       return;
 
-               if (preferred)
+               if (closure->preferred)
                        newmode->type |= DRM_MODE_TYPE_PREFERRED;
 
-               drm_mode_probed_add(connector, newmode);
-               return 1;
+               drm_mode_probed_add(closure->connector, newmode);
+               closure->modes++;
+               closure->preferred = 0;
        }
-
-       /* other timing types */
-       switch (data->type) {
-       case EDID_DETAIL_MONITOR_RANGE:
-               if (gtf)
-                       modes += drm_gtf_modes_for_range(connector, edid,
-                                                        timing);
-               break;
-       case EDID_DETAIL_STD_MODES:
-               /* Six modes per detailed section */
-               for (i = 0; i < 6; i++) {
-                       struct std_timing *std;
-                       struct drm_display_mode *newmode;
-
-                       std = &data->data.timings[i];
-                       newmode = drm_mode_std(connector, edid, std,
-                                              edid->revision);
-                       if (newmode) {
-                               drm_mode_probed_add(connector, newmode);
-                               modes++;
-                       }
-               }
-               break;
-       case EDID_DETAIL_CVT_3BYTE:
-               modes += drm_cvt_modes(connector, timing);
-               break;
-       case EDID_DETAIL_EST_TIMINGS:
-               modes += drm_est3_modes(connector, timing);
-               break;
-       default:
-               break;
-       }
-
-       return modes;
 }
 
-/**
- * add_detailed_info - get detailed mode info from EDID data
+/*
+ * add_detailed_modes - Add modes from detailed timings
  * @connector: attached connector
  * @edid: EDID block to scan
  * @quirks: quirks to apply
- *
- * Some of the detailed timing sections may contain mode information.  Grab
- * it and add it to the list.
  */
-static int add_detailed_info(struct drm_connector *connector,
-                            struct edid *edid, u32 quirks)
+static int
+add_detailed_modes(struct drm_connector *connector, struct edid *edid,
+                  u32 quirks)
 {
-       int i, modes = 0;
+       struct detailed_mode_closure closure = {
+               connector,
+               edid,
+               1,
+               quirks,
+               0
+       };
 
-       for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
-               struct detailed_timing *timing = &edid->detailed_timings[i];
-               int preferred = (i == 0);
+       if (closure.preferred && !version_greater(edid, 1, 3))
+               closure.preferred =
+                   (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
 
-               if (preferred && edid->version == 1 && edid->revision < 4)
-                       preferred = (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
+       drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure);
 
-               /* In 1.0, only timings are allowed */
-               if (!timing->pixel_clock && edid->version == 1 &&
-                       edid->revision == 0)
-                       continue;
-
-               modes += add_detailed_modes(connector, timing, edid, quirks,
-                                           preferred);
-       }
-
-       return modes;
+       return closure.modes;
 }
 
+#define HDMI_IDENTIFIER 0x000C03
+#define AUDIO_BLOCK    0x01
+#define VENDOR_BLOCK    0x03
+#define EDID_BASIC_AUDIO       (1 << 6)
+
 /**
- * add_detailed_mode_eedid - get detailed mode info from addtional timing
- *                     EDID block
- * @connector: attached connector
- * @edid: EDID block to scan(It is only to get addtional timing EDID block)
- * @quirks: quirks to apply
- *
- * Some of the detailed timing sections may contain mode information.  Grab
- * it and add it to the list.
+ * Search EDID for CEA extension block.
  */
-static int add_detailed_info_eedid(struct drm_connector *connector,
-                            struct edid *edid, u32 quirks)
+static u8 *drm_find_cea_extension(struct edid *edid)
 {
-       int i, modes = 0;
-       char *edid_ext = NULL;
-       struct detailed_timing *timing;
-       int start_offset, end_offset;
+       u8 *edid_ext = NULL;
+       int i;
 
-       if (edid->version == 1 && edid->revision < 3)
-               return 0;
-       if (!edid->extensions)
-               return 0;
+       /* No EDID or EDID extensions */
+       if (edid == NULL || edid->extensions == 0)
+               return NULL;
 
        /* Find CEA extension */
        for (i = 0; i < edid->extensions; i++) {
-               edid_ext = (char *)edid + EDID_LENGTH * (i + 1);
-               if (edid_ext[0] == 0x02)
+               edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
+               if (edid_ext[0] == CEA_EXT)
                        break;
        }
 
        if (i == edid->extensions)
-               return 0;
-
-       /* Get the start offset of detailed timing block */
-       start_offset = edid_ext[2];
-       if (start_offset == 0) {
-               /* If the start_offset is zero, it means that neither detailed
-                * info nor data block exist. In such case it is also
-                * unnecessary to parse the detailed timing info.
-                */
-               return 0;
-       }
-
-       end_offset = EDID_LENGTH;
-       end_offset -= sizeof(struct detailed_timing);
-       for (i = start_offset; i < end_offset;
-                       i += sizeof(struct detailed_timing)) {
-               timing = (struct detailed_timing *)(edid_ext + i);
-               modes += add_detailed_modes(connector, timing, edid, quirks, 0);
-       }
+               return NULL;
 
-       return modes;
+       return edid_ext;
 }
 
-#define HDMI_IDENTIFIER 0x000C03
-#define VENDOR_BLOCK    0x03
 /**
  * drm_detect_hdmi_monitor - detect whether monitor is hdmi.
  * @edid: monitor EDID information
@@ -1561,24 +1305,13 @@ static int add_detailed_info_eedid(struct drm_connector *connector,
  */
 bool drm_detect_hdmi_monitor(struct edid *edid)
 {
-       char *edid_ext = NULL;
+       u8 *edid_ext;
        int i, hdmi_id;
        int start_offset, end_offset;
        bool is_hdmi = false;
 
-       /* No EDID or EDID extensions */
-       if (edid == NULL || edid->extensions == 0)
-               goto end;
-
-       /* Find CEA extension */
-       for (i = 0; i < edid->extensions; i++) {
-               edid_ext = (char *)edid + EDID_LENGTH * (i + 1);
-               /* This block is CEA extension */
-               if (edid_ext[0] == 0x02)
-                       break;
-       }
-
-       if (i == edid->extensions)
+       edid_ext = drm_find_cea_extension(edid);
+       if (!edid_ext)
                goto end;
 
        /* Data block offset in CEA extension block */
@@ -1608,6 +1341,53 @@ end:
 }
 EXPORT_SYMBOL(drm_detect_hdmi_monitor);
 
+/**
+ * drm_detect_monitor_audio - check monitor audio capability
+ *
+ * Monitor should have CEA extension block.
+ * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic
+ * audio' only. If there is any audio extension block and supported
+ * audio format, assume at least 'basic audio' support, even if 'basic
+ * audio' is not defined in EDID.
+ *
+ */
+bool drm_detect_monitor_audio(struct edid *edid)
+{
+       u8 *edid_ext;
+       int i, j;
+       bool has_audio = false;
+       int start_offset, end_offset;
+
+       edid_ext = drm_find_cea_extension(edid);
+       if (!edid_ext)
+               goto end;
+
+       has_audio = ((edid_ext[3] & EDID_BASIC_AUDIO) != 0);
+
+       if (has_audio) {
+               DRM_DEBUG_KMS("Monitor has basic audio support\n");
+               goto end;
+       }
+
+       /* Data block offset in CEA extension block */
+       start_offset = 4;
+       end_offset = edid_ext[2];
+
+       for (i = start_offset; i < end_offset;
+                       i += ((edid_ext[i] & 0x1f) + 1)) {
+               if ((edid_ext[i] >> 5) == AUDIO_BLOCK) {
+                       has_audio = true;
+                       for (j = 1; j < (edid_ext[i] & 0x1f); j += 3)
+                               DRM_DEBUG_KMS("CEA audio format %d\n",
+                                             (edid_ext[i + j] >> 3) & 0xf);
+                       goto end;
+               }
+       }
+end:
+       return has_audio;
+}
+EXPORT_SYMBOL(drm_detect_monitor_audio);
+
 /**
  * drm_add_edid_modes - add modes from EDID data, if available
  * @connector: connector we're probing
@@ -1626,7 +1406,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
                return 0;
        }
        if (!drm_edid_is_valid(edid)) {
-               dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
+               dev_warn(connector->dev->dev, "%s: EDID invalid.\n",
                         drm_get_connector_name(connector));
                return 0;
        }
@@ -1643,35 +1423,21 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
         * - established timing codes
         * - modes inferred from GTF or CVT range information
         *
-        * We don't quite implement this yet, but we're close.
+        * We get this pretty much right.
         *
         * XXX order for additional mode types in extension blocks?
         */
-       num_modes += add_detailed_info(connector, edid, quirks);
-       num_modes += add_detailed_info_eedid(connector, edid, quirks);
+       num_modes += add_detailed_modes(connector, edid, quirks);
+       num_modes += add_cvt_modes(connector, edid);
        num_modes += add_standard_modes(connector, edid);
        num_modes += add_established_modes(connector, edid);
+       num_modes += add_inferred_modes(connector, edid);
 
        if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
                edid_fixup_preferred(connector, quirks);
 
-       connector->display_info.serration_vsync = (edid->input & DRM_EDID_INPUT_SERRATION_VSYNC) ? 1 : 0;
-       connector->display_info.sync_on_green = (edid->input & DRM_EDID_INPUT_SYNC_ON_GREEN) ? 1 : 0;
-       connector->display_info.composite_sync = (edid->input & DRM_EDID_INPUT_COMPOSITE_SYNC) ? 1 : 0;
-       connector->display_info.separate_syncs = (edid->input & DRM_EDID_INPUT_SEPARATE_SYNCS) ? 1 : 0;
-       connector->display_info.blank_to_black = (edid->input & DRM_EDID_INPUT_BLANK_TO_BLACK) ? 1 : 0;
-       connector->display_info.video_level = (edid->input & DRM_EDID_INPUT_VIDEO_LEVEL) >> 5;
-       connector->display_info.digital = (edid->input & DRM_EDID_INPUT_DIGITAL) ? 1 : 0;
        connector->display_info.width_mm = edid->width_cm * 10;
        connector->display_info.height_mm = edid->height_cm * 10;
-       connector->display_info.gamma = edid->gamma;
-       connector->display_info.gtf_supported = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) ? 1 : 0;
-       connector->display_info.standard_color = (edid->features & DRM_EDID_FEATURE_STANDARD_COLOR) ? 1 : 0;
-       connector->display_info.display_type = (edid->features & DRM_EDID_FEATURE_DISPLAY_TYPE) >> 3;
-       connector->display_info.active_off_supported = (edid->features & DRM_EDID_FEATURE_PM_ACTIVE_OFF) ? 1 : 0;
-       connector->display_info.suspend_supported = (edid->features & DRM_EDID_FEATURE_PM_SUSPEND) ? 1 : 0;
-       connector->display_info.standby_supported = (edid->features & DRM_EDID_FEATURE_PM_STANDBY) ? 1 : 0;
-       connector->display_info.gamma = edid->gamma;
 
        return num_modes;
 }