diff options
Diffstat (limited to 'drivers/gpu/drm/drm_edid.c')
-rw-r--r-- | drivers/gpu/drm/drm_edid.c | 557 |
1 files changed, 537 insertions, 20 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 7f2728bbc16c..b54ba63d506e 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -60,6 +60,12 @@ #define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5) /* use +hsync +vsync for detailed mode */ #define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6) +/* define the number of Extension EDID block */ +#define MAX_EDID_EXT_NUM 4 + +#define LEVEL_DMT 0 +#define LEVEL_GTF 1 +#define LEVEL_CVT 2 static struct edid_quirk { char *vendor; @@ -103,7 +109,9 @@ static struct edid_quirk { /* Valid EDID header has these bytes */ -static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; +static const u8 edid_header[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 +}; /** * edid_is_valid - sanity check EDID data @@ -237,39 +245,345 @@ 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, + 1072, 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, 2025000, 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 struct drm_display_mode *drm_find_dmt(struct drm_device *dev, + int hsize, int vsize, int fresh) +{ + int i, count; + struct drm_display_mode *ptr, *mode; + + count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode); + mode = NULL; + for (i = 0; i < count; i++) { + 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; + } + } + return mode; +} + +/* + * 0 is reserved. The spec says 0x01 fill for unused timings. Some old + * monitors fill with ascii space (0x20) instead. + */ +static int +bad_std_timing(u8 a, u8 b) +{ + return (a == 0x00 && b == 0x00) || + (a == 0x01 && b == 0x01) || + (a == 0x20 && b == 0x20); +} + /** * drm_mode_std - convert standard mode info (width, height, refresh) into mode * @t: standard timing params + * @timing_level: standard timing level * * Take the standard timing params (in this case width, aspect, and refresh) - * and convert them into a real mode using CVT. + * and convert them into a real mode using CVT/GTF/DMT. * * Punts for now, but should eventually use the FB layer's CVT based mode * generation code. */ struct drm_display_mode *drm_mode_std(struct drm_device *dev, - struct std_timing *t) + struct std_timing *t, + int revision, + int timing_level) { struct drm_display_mode *mode; - int hsize = t->hsize * 8 + 248, vsize; + int hsize, vsize; + int vrefresh_rate; unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK) >> EDID_TIMING_ASPECT_SHIFT; + unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK) + >> EDID_TIMING_VFREQ_SHIFT; - mode = drm_mode_create(dev); - if (!mode) + if (bad_std_timing(t->hsize, t->vfreq_aspect)) return NULL; - if (aspect_ratio == 0) - vsize = (hsize * 10) / 16; - else if (aspect_ratio == 1) + /* According to the EDID spec, the hdisplay = hsize * 8 + 248 */ + hsize = t->hsize * 8 + 248; + /* vrefresh_rate = vfreq + 60 */ + vrefresh_rate = vfreq + 60; + /* the vdisplay is calculated based on the aspect ratio */ + if (aspect_ratio == 0) { + if (revision < 3) + vsize = hsize; + else + vsize = (hsize * 10) / 16; + } else if (aspect_ratio == 1) vsize = (hsize * 3) / 4; else if (aspect_ratio == 2) vsize = (hsize * 4) / 5; else vsize = (hsize * 9) / 16; - - drm_mode_set_name(mode); - + /* HDTV hack */ + if (hsize == 1360 && vsize == 765 && vrefresh_rate == 60) { + mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0, + false); + mode->hdisplay = 1366; + mode->vsync_start = mode->vsync_start - 1; + mode->vsync_end = mode->vsync_end - 1; + return mode; + } + mode = NULL; + /* check whether it can be found in default mode table */ + mode = drm_find_dmt(dev, hsize, vsize, vrefresh_rate); + if (mode) + return mode; + + switch (timing_level) { + case LEVEL_DMT: + break; + case LEVEL_GTF: + mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); + break; + case LEVEL_CVT: + mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0, + false); + break; + } return mode; } @@ -312,6 +626,12 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, return NULL; } + /* it is incorrect if hsync/vsync width is zero */ + if (!hsync_pulse_width || !vsync_pulse_width) { + DRM_DEBUG_KMS("Incorrect Detailed timing. " + "Wrong Hsync/Vsync pulse width\n"); + return NULL; + } mode = drm_mode_create(dev); if (!mode) return NULL; @@ -333,6 +653,21 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, mode->vsync_end = mode->vsync_start + vsync_pulse_width; mode->vtotal = mode->vdisplay + vblank; + /* perform the basic check for the detailed timing */ + if (mode->hsync_end > mode->htotal || + mode->vsync_end > mode->vtotal) { + drm_mode_destroy(dev, mode); + DRM_DEBUG_KMS("Incorrect detailed timing. " + "Sync is beyond the blank.\n"); + return NULL; + } + + /* Some EDIDs have bogus h/vtotal values */ + if (mode->hsync_end > mode->htotal) + mode->htotal = mode->hsync_end + 1; + if (mode->vsync_end > mode->vtotal) + mode->vtotal = mode->vsync_end + 1; + drm_mode_set_name(mode); if (pt->misc & DRM_EDID_PT_INTERLACED) @@ -451,6 +786,19 @@ static int add_established_modes(struct drm_connector *connector, struct edid *e return modes; } +/** + * stanard_timing_level - get std. timing level(CVT/GTF/DMT) + * @edid: EDID block to scan + */ +static int standard_timing_level(struct edid *edid) +{ + if (edid->revision >= 2) { + if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)) + return LEVEL_CVT; + return LEVEL_GTF; + } + return LEVEL_DMT; +} /** * add_standard_modes - get std. modes from EDID and add them @@ -463,6 +811,9 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid { struct drm_device *dev = connector->dev; int i, modes = 0; + int timing_level; + + timing_level = standard_timing_level(edid); for (i = 0; i < EDID_STD_TIMINGS; i++) { struct std_timing *t = &edid->standard_timings[i]; @@ -472,7 +823,8 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid if (t->hsize == 1 && t->vfreq_aspect == 1) continue; - newmode = drm_mode_std(dev, &edid->standard_timings[i]); + newmode = drm_mode_std(dev, &edid->standard_timings[i], + edid->revision, timing_level); if (newmode) { drm_mode_probed_add(connector, newmode); modes++; @@ -496,6 +848,9 @@ static int add_detailed_info(struct drm_connector *connector, { struct drm_device *dev = connector->dev; int i, j, modes = 0; + int timing_level; + + timing_level = standard_timing_level(edid); for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { struct detailed_timing *timing = &edid->detailed_timings[i]; @@ -519,13 +874,14 @@ static int add_detailed_info(struct drm_connector *connector, case EDID_DETAIL_MONITOR_CPDATA: break; case EDID_DETAIL_STD_MODES: - /* Five modes per detailed section */ - for (j = 0; j < 5; i++) { + for (j = 0; j < 6; i++) { struct std_timing *std; struct drm_display_mode *newmode; std = &data->data.timings[j]; - newmode = drm_mode_std(dev, std); + newmode = drm_mode_std(dev, std, + edid->revision, + timing_level); if (newmode) { drm_mode_probed_add(connector, newmode); modes++; @@ -551,6 +907,124 @@ static int add_detailed_info(struct drm_connector *connector, return modes; } +/** + * 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. + */ +static int add_detailed_info_eedid(struct drm_connector *connector, + struct edid *edid, u32 quirks) +{ + struct drm_device *dev = connector->dev; + int i, j, modes = 0; + char *edid_ext = NULL; + struct detailed_timing *timing; + struct detailed_non_pixel *data; + struct drm_display_mode *newmode; + int edid_ext_num; + int start_offset, end_offset; + int timing_level; + + if (edid->version == 1 && edid->revision < 3) { + /* If the EDID version is less than 1.3, there is no + * extension EDID. + */ + return 0; + } + if (!edid->extensions) { + /* if there is no extension EDID, it is unnecessary to + * parse the E-EDID to get detailed info + */ + return 0; + } + + /* Chose real EDID extension number */ + edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ? + MAX_EDID_EXT_NUM : edid->extensions; + + /* Find CEA extension */ + for (i = 0; i < edid_ext_num; i++) { + edid_ext = (char *)edid + EDID_LENGTH * (i + 1); + /* This block is CEA extension */ + if (edid_ext[0] == 0x02) + break; + } + + if (i == edid_ext_num) { + /* if there is no additional timing EDID block, return */ + 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; + } + + timing_level = standard_timing_level(edid); + 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); + data = &timing->data.other_data; + /* Detailed mode timing */ + if (timing->pixel_clock) { + newmode = drm_mode_detailed(dev, edid, timing, quirks); + if (!newmode) + continue; + + drm_mode_probed_add(connector, newmode); + + modes++; + continue; + } + + /* Other timing or info */ + switch (data->type) { + case EDID_DETAIL_MONITOR_SERIAL: + break; + case EDID_DETAIL_MONITOR_STRING: + break; + case EDID_DETAIL_MONITOR_RANGE: + /* Get monitor range data */ + break; + case EDID_DETAIL_MONITOR_NAME: + break; + case EDID_DETAIL_MONITOR_CPDATA: + break; + case EDID_DETAIL_STD_MODES: + /* Five modes per detailed section */ + for (j = 0; j < 5; i++) { + struct std_timing *std; + struct drm_display_mode *newmode; + + std = &data->data.timings[j]; + newmode = drm_mode_std(dev, std, + edid->revision, + timing_level); + if (newmode) { + drm_mode_probed_add(connector, newmode); + modes++; + } + } + break; + default: + break; + } + } + + return modes; +} #define DDC_ADDR 0x50 /** @@ -584,7 +1058,6 @@ int drm_do_probe_ddc_edid(struct i2c_adapter *adapter, if (i2c_transfer(adapter, msgs, 2) == 2) return 0; - dev_info(&adapter->dev, "unable to read EDID block.\n"); return -1; } EXPORT_SYMBOL(drm_do_probe_ddc_edid); @@ -597,8 +1070,6 @@ static int drm_ddc_read_edid(struct drm_connector *connector, ret = drm_do_probe_ddc_edid(adapter, buf, len); if (ret != 0) { - dev_info(&connector->dev->pdev->dev, "%s: no EDID data\n", - drm_get_connector_name(connector)); goto end; } if (!edid_is_valid((struct edid *)buf)) { @@ -610,7 +1081,6 @@ end: return ret; } -#define MAX_EDID_EXT_NUM 4 /** * drm_get_edid - get EDID data, if available * @connector: connector we're probing @@ -763,6 +1233,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) num_modes += add_established_modes(connector, edid); num_modes += add_standard_modes(connector, edid); num_modes += add_detailed_info(connector, edid, quirks); + num_modes += add_detailed_info_eedid(connector, edid, quirks); if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) edid_fixup_preferred(connector, quirks); @@ -788,3 +1259,49 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) return num_modes; } EXPORT_SYMBOL(drm_add_edid_modes); + +/** + * drm_add_modes_noedid - add modes for the connectors without EDID + * @connector: connector we're probing + * @hdisplay: the horizontal display limit + * @vdisplay: the vertical display limit + * + * Add the specified modes to the connector's mode list. Only when the + * hdisplay/vdisplay is not beyond the given limit, it will be added. + * + * Return number of modes added or 0 if we couldn't find any. + */ +int drm_add_modes_noedid(struct drm_connector *connector, + int hdisplay, int vdisplay) +{ + int i, count, num_modes = 0; + struct drm_display_mode *mode, *ptr; + struct drm_device *dev = connector->dev; + + count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode); + if (hdisplay < 0) + hdisplay = 0; + if (vdisplay < 0) + vdisplay = 0; + + for (i = 0; i < count; i++) { + ptr = &drm_dmt_modes[i]; + if (hdisplay && vdisplay) { + /* + * Only when two are valid, they will be used to check + * whether the mode should be added to the mode list of + * the connector. + */ + if (ptr->hdisplay > hdisplay || + ptr->vdisplay > vdisplay) + continue; + } + mode = drm_mode_duplicate(dev, ptr); + if (mode) { + drm_mode_probed_add(connector, mode); + num_modes++; + } + } + return num_modes; +} +EXPORT_SYMBOL(drm_add_modes_noedid); |