diff mbox

[v2,08/18] DRM/KMS/EDID: Fix up EEDID Map Blogs if Extension Block Count has changed (v2)

Message ID 1353579788-30637-9-git-send-email-eich@suse.com (mailing list archive)
State New, archived
Headers show

Commit Message

Egbert Eich Nov. 22, 2012, 10:22 a.m. UTC
EEDID v1.3 mandates map blogs if more than one EDID extension block
is used while in v1.4 they are optional.
If the extension count has been changed (because some extension
blocks were not readable) those map blocks need fixing.
In case of v1.4 or higher we simply eliminate all map blogs as
this is less time consuming. For v1.3 we scrap any exsisting map
blocks and recreate them from scratch.

v2: Fixed conflits due to reordering of commits.

Signed-off-by: Egbert Eich <eich@suse.com>
---
 drivers/gpu/drm/drm_edid.c |   89 ++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 89 insertions(+), 0 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 0fe61fb..9b298fc 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -47,6 +47,7 @@ 
 #define EDID_CHECKSUM_OFFSET	    offsetof(struct edid, checksum)
 #define EDID_VERSION_MAJOR_OFFSET   offsetof(struct edid, version)
 #define EDID_VERSION_MINOR_OFFSET   offsetof(struct edid, revision)
+#define EEDID_BLOCK_MAP_FLAG      0xF0
 /*
  * EDID blocks out in the wild have a variety of bugs, try to collect
  * them here (note that userspace may work around broken monitors first,
@@ -320,6 +321,92 @@  static bool drm_edid_is_zero(u8 *in_edid, int length)
 	return true;
 }
 
+static void
+fix_map(u8 *block, int cnt)
+{
+	int i;
+	u8 csum;
+
+	if (--cnt > 127)
+		cnt = 127;
+	memset(block, 0, 128);
+	block[0] = EEDID_BLOCK_MAP_FLAG;
+	csum = block[0];
+
+	for (i = 1; i < cnt; i++) {
+		block[i] = block[i * EDID_LENGTH];
+		csum += block[i];
+	}
+	block[127] = (u8)(0x100 - csum);
+}
+
+static int
+fixup_blockmaps(u8 **blockp, int eblock_cnt)
+{
+	int i;
+	u8 *block = *blockp;
+
+	if (block[EDID_VERSION_MAJOR_OFFSET] > 1)
+		return 0;
+	if (block[EDID_VERSION_MINOR_OFFSET] < 3)
+		return 0;
+	if (eblock_cnt == 1) {
+		if (block[EDID_LENGTH] == EEDID_BLOCK_MAP_FLAG)
+			return 0;
+		else
+			return 1;
+	}
+	if (block[EDID_VERSION_MINOR_OFFSET] >= 4) {
+		/* blockmaps are optional: simply toss them */
+		for (i = 1; i <= eblock_cnt; i++) {
+			if (block[i * EDID_LENGTH] == EEDID_BLOCK_MAP_FLAG) {
+				if (i < eblock_cnt)
+					memmove(&block[i * EDID_LENGTH],
+						&block[(i + 1) * EDID_LENGTH],
+						(eblock_cnt-i) * EDID_LENGTH);
+				i--;
+				eblock_cnt--;
+			}
+		}
+	} else {
+		int total_cnt = block[EDID_EXTENSION_FLAG_OFFSET];
+		for (i = 1; i <= eblock_cnt; i++) {
+			if (block[i * EDID_LENGTH] == EEDID_BLOCK_MAP_FLAG) {
+				if (i == 1 || i == 128) /* correct map block locations */
+					continue;
+				if (i < eblock_cnt)
+					memmove(&block[i * EDID_LENGTH],
+						&block[(i + 1) * EDID_LENGTH],
+						(eblock_cnt-i) * EDID_LENGTH);
+				i--;
+				eblock_cnt--;
+				continue;
+			} else if (i == 1 || i == 128) {
+				if (eblock_cnt >= total_cnt) {
+					u8 *tmp_p;
+					tmp_p = krealloc(block, (eblock_cnt + 2) * EDID_LENGTH, GFP_KERNEL);
+					if (!tmp_p)
+						return eblock_cnt;
+					*blockp = block = tmp_p;
+					total_cnt = eblock_cnt + 1;
+				}
+				eblock_cnt++;
+				memmove(&block[(i + 1) * EDID_LENGTH],
+					&block[i * EDID_LENGTH],
+					(eblock_cnt-i) * EDID_LENGTH);
+			}
+		}
+		fix_map(&block[EDID_LENGTH], eblock_cnt);
+
+		if (eblock_cnt == 129)
+			return 128;
+
+		if (eblock_cnt > 129)
+			fix_map(&block[EDID_LENGTH * 128], eblock_cnt - 127);
+	}
+	return eblock_cnt;
+}
+
 static u8 *
 drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
 {
@@ -394,6 +481,8 @@  drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
 
 done_fix_extension_count:
 	if (valid_extensions != block[EDID_EXTENSION_FLAG_OFFSET]) {
+		if (valid_extensions)
+			valid_extensions = fixup_blockmaps(&block, valid_extensions);
 		block[EDID_CHECKSUM_OFFSET] += block[EDID_EXTENSION_FLAG_OFFSET] - valid_extensions;
 		block[EDID_EXTENSION_FLAG_OFFSET] = valid_extensions;
 	}