diff options
Diffstat (limited to 'drivers/gpu/drm/drm_edid.c')
-rw-r--r-- | drivers/gpu/drm/drm_edid.c | 115 |
1 files changed, 104 insertions, 11 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index adc9358c9be..7425e5c9bd7 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -127,6 +127,23 @@ static const u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; + /* + * Sanity check the header of the base EDID block. Return 8 if the header + * is perfect, down to 0 if it's totally wrong. + */ +int drm_edid_header_is_valid(const u8 *raw_edid) +{ + int i, score = 0; + + for (i = 0; i < sizeof(edid_header); i++) + if (raw_edid[i] == edid_header[i]) + score++; + + return score; +} +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. @@ -139,12 +156,7 @@ drm_edid_block_valid(u8 *raw_edid) struct edid *edid = (struct edid *)raw_edid; if (raw_edid[0] == 0x00) { - int score = 0; - - for (i = 0; i < sizeof(edid_header); i++) - if (raw_edid[i] == edid_header[i]) - score++; - + int score = drm_edid_header_is_valid(raw_edid); if (score == 8) ; else if (score >= 6) { DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); @@ -184,9 +196,9 @@ drm_edid_block_valid(u8 *raw_edid) bad: if (raw_edid) { - DRM_ERROR("Raw EDID:\n"); - print_hex_dump_bytes(KERN_ERR, DUMP_PREFIX_NONE, raw_edid, EDID_LENGTH); - printk("\n"); + printk(KERN_ERR "Raw EDID:\n"); + print_hex_dump(KERN_ERR, " \t", DUMP_PREFIX_NONE, 16, 1, + raw_edid, EDID_LENGTH, false); } return 0; } @@ -258,6 +270,17 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, return ret == 2 ? 0 : -1; } +static bool drm_edid_is_zero(u8 *in_edid, int length) +{ + int i; + u32 *raw_edid = (u32 *)in_edid; + + for (i = 0; i < length / 4; i++) + if (*(raw_edid + i) != 0) + return false; + return true; +} + static u8 * drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) { @@ -273,6 +296,10 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) goto out; if (drm_edid_block_valid(block)) break; + if (i == 0 && drm_edid_is_zero(block, EDID_LENGTH)) { + connector->null_edid_counter++; + goto carp; + } } if (i == 4) goto carp; @@ -1413,6 +1440,73 @@ end: EXPORT_SYMBOL(drm_detect_monitor_audio); /** + * drm_add_display_info - pull display info out if present + * @edid: EDID data + * @info: display info (attached to connector) + * + * Grab any available display info and stuff it into the drm_display_info + * structure that's part of the connector. Useful for tracking bpp and + * color spaces. + */ +static void drm_add_display_info(struct edid *edid, + struct drm_display_info *info) +{ + u8 *edid_ext; + + info->width_mm = edid->width_cm * 10; + info->height_mm = edid->height_cm * 10; + + /* driver figures it out in this case */ + info->bpc = 0; + info->color_formats = 0; + + /* Only defined for 1.4 with digital displays */ + if (edid->revision < 4) + return; + + if (!(edid->input & DRM_EDID_INPUT_DIGITAL)) + return; + + switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) { + case DRM_EDID_DIGITAL_DEPTH_6: + info->bpc = 6; + break; + case DRM_EDID_DIGITAL_DEPTH_8: + info->bpc = 8; + break; + case DRM_EDID_DIGITAL_DEPTH_10: + info->bpc = 10; + break; + case DRM_EDID_DIGITAL_DEPTH_12: + info->bpc = 12; + break; + case DRM_EDID_DIGITAL_DEPTH_14: + info->bpc = 14; + break; + case DRM_EDID_DIGITAL_DEPTH_16: + info->bpc = 16; + break; + case DRM_EDID_DIGITAL_DEPTH_UNDEF: + default: + info->bpc = 0; + 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]; +} + +/** * drm_add_edid_modes - add modes from EDID data, if available * @connector: connector we're probing * @edid: edid data @@ -1460,8 +1554,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) edid_fixup_preferred(connector, quirks); - connector->display_info.width_mm = edid->width_cm * 10; - connector->display_info.height_mm = edid->height_cm * 10; + drm_add_display_info(edid, &connector->display_info); return num_modes; } |