|
@@ -1349,6 +1349,7 @@ MODULE_PARM_DESC(edid_fixup,
|
|
|
|
|
|
|
|
static void drm_get_displayid(struct drm_connector *connector,
|
|
static void drm_get_displayid(struct drm_connector *connector,
|
|
|
struct edid *edid);
|
|
struct edid *edid);
|
|
|
|
|
+static int validate_displayid(u8 *displayid, int length, int idx);
|
|
|
|
|
|
|
|
static int drm_edid_block_checksum(const u8 *raw_edid)
|
|
static int drm_edid_block_checksum(const u8 *raw_edid)
|
|
|
{
|
|
{
|
|
@@ -2932,16 +2933,46 @@ static u8 *drm_find_edid_extension(const struct edid *edid, int ext_id)
|
|
|
return edid_ext;
|
|
return edid_ext;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static u8 *drm_find_cea_extension(const struct edid *edid)
|
|
|
|
|
-{
|
|
|
|
|
- return drm_find_edid_extension(edid, CEA_EXT);
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
static u8 *drm_find_displayid_extension(const struct edid *edid)
|
|
static u8 *drm_find_displayid_extension(const struct edid *edid)
|
|
|
{
|
|
{
|
|
|
return drm_find_edid_extension(edid, DISPLAYID_EXT);
|
|
return drm_find_edid_extension(edid, DISPLAYID_EXT);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+static u8 *drm_find_cea_extension(const struct edid *edid)
|
|
|
|
|
+{
|
|
|
|
|
+ int ret;
|
|
|
|
|
+ int idx = 1;
|
|
|
|
|
+ int length = EDID_LENGTH;
|
|
|
|
|
+ struct displayid_block *block;
|
|
|
|
|
+ u8 *cea;
|
|
|
|
|
+ u8 *displayid;
|
|
|
|
|
+
|
|
|
|
|
+ /* Look for a top level CEA extension block */
|
|
|
|
|
+ cea = drm_find_edid_extension(edid, CEA_EXT);
|
|
|
|
|
+ if (cea)
|
|
|
|
|
+ return cea;
|
|
|
|
|
+
|
|
|
|
|
+ /* CEA blocks can also be found embedded in a DisplayID block */
|
|
|
|
|
+ displayid = drm_find_displayid_extension(edid);
|
|
|
|
|
+ if (!displayid)
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+
|
|
|
|
|
+ ret = validate_displayid(displayid, length, idx);
|
|
|
|
|
+ if (ret)
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+
|
|
|
|
|
+ idx += sizeof(struct displayid_hdr);
|
|
|
|
|
+ for_each_displayid_db(displayid, block, idx, length) {
|
|
|
|
|
+ if (block->tag == DATA_BLOCK_CTA) {
|
|
|
|
|
+ cea = (u8 *)block;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return cea;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/*
|
|
/*
|
|
|
* Calculate the alternate clock for the CEA mode
|
|
* Calculate the alternate clock for the CEA mode
|
|
|
* (60Hz vs. 59.94Hz etc.)
|
|
* (60Hz vs. 59.94Hz etc.)
|
|
@@ -3665,13 +3696,38 @@ cea_revision(const u8 *cea)
|
|
|
static int
|
|
static int
|
|
|
cea_db_offsets(const u8 *cea, int *start, int *end)
|
|
cea_db_offsets(const u8 *cea, int *start, int *end)
|
|
|
{
|
|
{
|
|
|
- /* Data block offset in CEA extension block */
|
|
|
|
|
- *start = 4;
|
|
|
|
|
- *end = cea[2];
|
|
|
|
|
- if (*end == 0)
|
|
|
|
|
- *end = 127;
|
|
|
|
|
- if (*end < 4 || *end > 127)
|
|
|
|
|
- return -ERANGE;
|
|
|
|
|
|
|
+ /* DisplayID CTA extension blocks and top-level CEA EDID
|
|
|
|
|
+ * block header definitions differ in the following bytes:
|
|
|
|
|
+ * 1) Byte 2 of the header specifies length differently,
|
|
|
|
|
+ * 2) Byte 3 is only present in the CEA top level block.
|
|
|
|
|
+ *
|
|
|
|
|
+ * The different definitions for byte 2 follow.
|
|
|
|
|
+ *
|
|
|
|
|
+ * DisplayID CTA extension block defines byte 2 as:
|
|
|
|
|
+ * Number of payload bytes
|
|
|
|
|
+ *
|
|
|
|
|
+ * CEA EDID block defines byte 2 as:
|
|
|
|
|
+ * Byte number (decimal) within this block where the 18-byte
|
|
|
|
|
+ * DTDs begin. If no non-DTD data is present in this extension
|
|
|
|
|
+ * block, the value should be set to 04h (the byte after next).
|
|
|
|
|
+ * If set to 00h, there are no DTDs present in this block and
|
|
|
|
|
+ * no non-DTD data.
|
|
|
|
|
+ */
|
|
|
|
|
+ if (cea[0] == DATA_BLOCK_CTA) {
|
|
|
|
|
+ *start = 3;
|
|
|
|
|
+ *end = *start + cea[2];
|
|
|
|
|
+ } else if (cea[0] == CEA_EXT) {
|
|
|
|
|
+ /* Data block offset in CEA extension block */
|
|
|
|
|
+ *start = 4;
|
|
|
|
|
+ *end = cea[2];
|
|
|
|
|
+ if (*end == 0)
|
|
|
|
|
+ *end = 127;
|
|
|
|
|
+ if (*end < 4 || *end > 127)
|
|
|
|
|
+ return -ERANGE;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return -ENOTSUPP;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -5218,6 +5274,9 @@ static int drm_parse_display_id(struct drm_connector *connector,
|
|
|
case DATA_BLOCK_TYPE_1_DETAILED_TIMING:
|
|
case DATA_BLOCK_TYPE_1_DETAILED_TIMING:
|
|
|
/* handled in mode gathering code. */
|
|
/* handled in mode gathering code. */
|
|
|
break;
|
|
break;
|
|
|
|
|
+ case DATA_BLOCK_CTA:
|
|
|
|
|
+ /* handled in the cea parser code. */
|
|
|
|
|
+ break;
|
|
|
default:
|
|
default:
|
|
|
DRM_DEBUG_KMS("found DisplayID tag 0x%x, unhandled\n", block->tag);
|
|
DRM_DEBUG_KMS("found DisplayID tag 0x%x, unhandled\n", block->tag);
|
|
|
break;
|
|
break;
|