[PATCH] drm/edid: Parse multiple CEA ext block

Lee Shawn C shawn.c.lee at intel.com
Mon Nov 16 13:37:22 UTC 2020


Based on commit '8873cfa38405 ("drm/edid: Allow looking for
ext blocks starting from a specified index")'. Driver have
capability to retrieve specific ext block from EDID.

So far, driver just recognized first cea ext block. Now we
have an EDID with two cea ext block. And video info was
stored in second cea ext block. Those video info will not be
able to retrieve properly.

This chagne allow driver to read multiple cea ext block
in add_cea_modes(). Then driver can receive video info from
second cea ext block correctly.

Cc: Ville Syrjälä <ville.syrjala at linux.intel.com>
Cc: Jani Nikula <jani.nikula at linux.intel.com>
Cc: Cooper Chiou <cooper.chiou at intel.com>
Cc: Khaled Almahallawy <khaled.almahallawy at intel.com>
Signed-off-by: Lee Shawn C <shawn.c.lee at intel.com>
---
 drivers/gpu/drm/drm_edid.c | 130 +++++++++++++++++++++----------------
 1 file changed, 74 insertions(+), 56 deletions(-)

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 74f5a3197214..1cbccf6a9d3e 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -3287,26 +3287,22 @@ static u8 *drm_find_displayid_extension(const struct edid *edid,
 	return displayid;
 }
 
-static u8 *drm_find_cea_extension(const struct edid *edid)
+static u8 *drm_find_cea_extension(const struct edid *edid, int *ext_index)
 {
 	int length, idx;
 	struct displayid_block *block;
 	u8 *cea;
 	u8 *displayid;
-	int ext_index;
 
 	/* Look for a top level CEA extension block */
-	/* FIXME: make callers iterate through multiple CEA ext blocks? */
-	ext_index = 0;
-	cea = drm_find_edid_extension(edid, CEA_EXT, &ext_index);
+	cea = drm_find_edid_extension(edid, CEA_EXT, ext_index);
 	if (cea)
 		return cea;
 
 	/* CEA blocks can also be found embedded in a DisplayID block */
-	ext_index = 0;
 	for (;;) {
 		displayid = drm_find_displayid_extension(edid, &length, &idx,
-							 &ext_index);
+							 ext_index);
 		if (!displayid)
 			return NULL;
 
@@ -3584,10 +3580,10 @@ add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid)
 	struct drm_device *dev = connector->dev;
 	struct drm_display_mode *mode, *tmp;
 	LIST_HEAD(list);
-	int modes = 0;
+	int modes = 0, ext_index = 0;
 
 	/* Don't add CEA modes if the CEA extension block is missing */
-	if (!drm_find_cea_extension(edid))
+	if (!drm_find_cea_extension(edid, &ext_index))
 		return 0;
 
 	/*
@@ -3779,6 +3775,9 @@ do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len)
 
 		mode = drm_display_mode_from_vic_index(connector, db, len, i);
 		if (mode) {
+			const struct drm_display_mode *m;
+			bool edid_duplicated;
+
 			/*
 			 * YCBCR420 capability block contains a bitmap which
 			 * gives the index of CEA modes from CEA VDB, which
@@ -3791,8 +3790,21 @@ do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len)
 			if (i < 64 && hdmi->y420_cmdb_map & (1ULL << i))
 				drm_add_cmdb_modes(connector, db[i]);
 
-			drm_mode_probed_add(connector, mode);
-			modes++;
+			edid_duplicated = false;
+			list_for_each_entry(m, &connector->probed_modes, head) {
+				if (mode->clock == m->clock &&
+				    mode->hdisplay == m->hdisplay &&
+				    mode->vdisplay == m->vdisplay &&
+				    drm_mode_vrefresh(mode) == drm_mode_vrefresh(m))
+					edid_duplicated = true;
+			}
+
+			if (edid_duplicated) {
+				drm_mode_destroy(connector->dev, mode);
+			} else {
+				drm_mode_probed_add(connector, mode);
+				modes++;
+			}
 		}
 	}
 
@@ -4259,46 +4271,52 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector,
 static int
 add_cea_modes(struct drm_connector *connector, struct edid *edid)
 {
-	const u8 *cea = drm_find_cea_extension(edid);
+	const u8 *cea;
 	const u8 *db, *hdmi = NULL, *video = NULL;
 	u8 dbl, hdmi_len, video_len = 0;
-	int modes = 0;
-
-	if (cea && cea_revision(cea) >= 3) {
-		int i, start, end;
+	int modes = 0, ext_index = 0, i;
 
-		if (cea_db_offsets(cea, &start, &end))
-			return 0;
-
-		for_each_cea_db(cea, i, start, end) {
-			db = &cea[i];
-			dbl = cea_db_payload_len(db);
+	for (i = ext_index; i <= edid->extensions; i++) {
+		cea = drm_find_cea_extension(edid, &ext_index);
+		if (!cea)
+			break;
 
-			if (cea_db_tag(db) == VIDEO_BLOCK) {
-				video = db + 1;
-				video_len = dbl;
-				modes += do_cea_modes(connector, video, dbl);
-			} else if (cea_db_is_hdmi_vsdb(db)) {
-				hdmi = db;
-				hdmi_len = dbl;
-			} else if (cea_db_is_y420vdb(db)) {
-				const u8 *vdb420 = &db[2];
-
-				/* Add 4:2:0(only) modes present in EDID */
-				modes += do_y420vdb_modes(connector,
-							  vdb420,
-							  dbl - 1);
+		if (cea && cea_revision(cea) >= 3) {
+			int i, start, end;
+
+			if (cea_db_offsets(cea, &start, &end))
+				return 0;
+
+			for_each_cea_db(cea, i, start, end) {
+				db = &cea[i];
+				dbl = cea_db_payload_len(db);
+
+				if (cea_db_tag(db) == VIDEO_BLOCK) {
+					video = db + 1;
+					video_len = dbl;
+					modes += do_cea_modes(connector, video, dbl);
+				} else if (cea_db_is_hdmi_vsdb(db)) {
+					hdmi = db;
+					hdmi_len = dbl;
+				} else if (cea_db_is_y420vdb(db)) {
+					const u8 *vdb420 = &db[2];
+
+					/* Add 4:2:0(only) modes present in EDID */
+					modes += do_y420vdb_modes(connector,
+								  vdb420,
+								  dbl - 1);
+				}
 			}
 		}
-	}
 
-	/*
-	 * We parse the HDMI VSDB after having added the cea modes as we will
-	 * be patching their flags when the sink supports stereo 3D.
-	 */
-	if (hdmi)
-		modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, video,
-					    video_len);
+		/*
+		 * We parse the HDMI VSDB after having added the cea modes as we will
+		 * be patching their flags when the sink supports stereo 3D.
+		 */
+		if (hdmi)
+			modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, video,
+						    video_len);
+	}
 
 	return modes;
 }
@@ -4500,7 +4518,7 @@ static void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
 	uint8_t *eld = connector->eld;
 	u8 *cea;
 	u8 *db;
-	int total_sad_count = 0;
+	int total_sad_count = 0, ext_index = 0;
 	int mnl;
 	int dbl;
 
@@ -4509,7 +4527,7 @@ static void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
 	if (!edid)
 		return;
 
-	cea = drm_find_cea_extension(edid);
+	cea = drm_find_cea_extension(edid, &ext_index);
 	if (!cea) {
 		DRM_DEBUG_KMS("ELD: no CEA Extension found\n");
 		return;
@@ -4593,11 +4611,11 @@ static void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
  */
 int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads)
 {
-	int count = 0;
+	int count = 0, ext_index = 0;
 	int i, start, end, dbl;
 	u8 *cea;
 
-	cea = drm_find_cea_extension(edid);
+	cea = drm_find_cea_extension(edid, &ext_index);
 	if (!cea) {
 		DRM_DEBUG_KMS("SAD: no CEA Extension found\n");
 		return 0;
@@ -4655,11 +4673,11 @@ EXPORT_SYMBOL(drm_edid_to_sad);
  */
 int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
 {
-	int count = 0;
+	int count = 0, ext_index = 0;
 	int i, start, end, dbl;
 	const u8 *cea;
 
-	cea = drm_find_cea_extension(edid);
+	cea = drm_find_cea_extension(edid, &ext_index);
 	if (!cea) {
 		DRM_DEBUG_KMS("SAD: no CEA Extension found\n");
 		return 0;
@@ -4752,9 +4770,9 @@ bool drm_detect_hdmi_monitor(struct edid *edid)
 {
 	u8 *edid_ext;
 	int i;
-	int start_offset, end_offset;
+	int start_offset, end_offset, ext_index = 0;
 
-	edid_ext = drm_find_cea_extension(edid);
+	edid_ext = drm_find_cea_extension(edid, &ext_index);
 	if (!edid_ext)
 		return false;
 
@@ -4791,9 +4809,9 @@ bool drm_detect_monitor_audio(struct edid *edid)
 	u8 *edid_ext;
 	int i, j;
 	bool has_audio = false;
-	int start_offset, end_offset;
+	int start_offset, end_offset, ext_index = 0;
 
-	edid_ext = drm_find_cea_extension(edid);
+	edid_ext = drm_find_cea_extension(edid, &ext_index);
 	if (!edid_ext)
 		goto end;
 
@@ -5000,9 +5018,9 @@ static void drm_parse_cea_ext(struct drm_connector *connector,
 {
 	struct drm_display_info *info = &connector->display_info;
 	const u8 *edid_ext;
-	int i, start, end;
+	int i, start, end, ext_index = 0;
 
-	edid_ext = drm_find_cea_extension(edid);
+	edid_ext = drm_find_cea_extension(edid, &ext_index);
 	if (!edid_ext)
 		return;
 
-- 
2.28.0



More information about the dri-devel mailing list