diff mbox series

edid: investigate dtd from cea861 extension if necessary

Message ID 20201103125031.248144-1-jbrunet@baylibre.com
State New
Delegated to: Anatolij Gustschin
Headers show
Series edid: investigate dtd from cea861 extension if necessary | expand

Commit Message

Jerome Brunet Nov. 3, 2020, 12:50 p.m. UTC
If no valid detailed timing can be found in the edid base block, check the
detailed timing provided in the cea861 extension block, if any.

Reported-by: Da Xue <da@libre.computer>
Tested-by: Da Xue <da@libre.computer>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 common/edid.c | 72 +++++++++++++++++++++++++++++++++++----------------
 1 file changed, 49 insertions(+), 23 deletions(-)
diff mbox series

Patch

diff --git a/common/edid.c b/common/edid.c
index 553ab8fd01a1..7dd0c924c24a 100644
--- a/common/edid.c
+++ b/common/edid.c
@@ -169,6 +169,33 @@  static bool cea_is_hdmi_vsdb_present(struct edid_cea861_info *info)
 	return false;
 }
 
+bool edid_get_dtd_timing_validate(struct edid_monitor_descriptor *desc,
+				  unsigned int dtd_count,
+				  struct display_timing *timing,
+				  bool (*mode_valid)(void *priv,
+						     const struct display_timing *timing),
+				  void *mode_valid_priv)
+{
+	bool timing_done = false;
+	int i;
+
+	for (i = 0; i < dtd_count; i++, desc++) {
+		if (desc->zero_flag_1 != 0) {
+			decode_timing((u8 *)desc, timing);
+			if (mode_valid)
+				timing_done = mode_valid(mode_valid_priv,
+							 timing);
+			else
+				timing_done = true;
+
+			if (timing_done)
+				break;
+		}
+	}
+
+	return timing_done;
+}
+
 int edid_get_timing_validate(u8 *buf, int buf_size,
 			     struct display_timing *timing,
 			     int *panel_bits_per_colourp,
@@ -177,8 +204,9 @@  int edid_get_timing_validate(u8 *buf, int buf_size,
 			     void *mode_valid_priv)
 {
 	struct edid1_info *edid = (struct edid1_info *)buf;
+	struct edid_cea861_info *info = NULL;
+	struct edid_monitor_descriptor *desc;
 	bool timing_done;
-	int i;
 
 	if (buf_size < sizeof(*edid) || edid_check_info(edid)) {
 		debug("%s: Invalid buffer\n", __func__);
@@ -190,24 +218,27 @@  int edid_get_timing_validate(u8 *buf, int buf_size,
 		return -ENOENT;
 	}
 
-	/* Look for detailed timing */
-	timing_done = false;
-	for (i = 0; i < 4; i++) {
-		struct edid_monitor_descriptor *desc;
+	desc = edid->monitor_details.descriptor;
+	timing_done = edid_get_dtd_timing_validate(desc, 4, timing,
+						   mode_valid, mode_valid_priv);
 
-		desc = &edid->monitor_details.descriptor[i];
-		if (desc->zero_flag_1 != 0) {
-			decode_timing((u8 *)desc, timing);
-			if (mode_valid)
-				timing_done = mode_valid(mode_valid_priv,
-							 timing);
-			else
-				timing_done = true;
+	if (edid->extension_flag && (buf_size >= EDID_EXT_SIZE)) {
+		info = (struct edid_cea861_info *)(buf + sizeof(*edid));
 
-			if (timing_done)
-				break;
-		}
+		if (info->extension_tag != EDID_CEA861_EXTENSION_TAG)
+			info = NULL;
+	}
+
+	/* Check CEA861 info block for timing if don't have one yet */
+	if (info && !timing_done && info->dtd_offset) {
+		unsigned int dtd_count = EDID_CEA861_DTD_COUNT(*info);
+
+		desc = (struct edid_monitor_descriptor *)((u8 *)info +
+							  info->dtd_offset);
+		timing_done = edid_get_dtd_timing_validate(desc, dtd_count, timing,
+							   mode_valid, mode_valid_priv);
 	}
+
 	if (!timing_done)
 		return -EINVAL;
 
@@ -225,13 +256,8 @@  int edid_get_timing_validate(u8 *buf, int buf_size,
 	}
 
 	timing->hdmi_monitor = false;
-	if (edid->extension_flag && (buf_size >= EDID_EXT_SIZE)) {
-		struct edid_cea861_info *info =
-			(struct edid_cea861_info *)(buf + sizeof(*edid));
-
-		if (info->extension_tag == EDID_CEA861_EXTENSION_TAG)
-			timing->hdmi_monitor = cea_is_hdmi_vsdb_present(info);
-	}
+	if (info)
+		timing->hdmi_monitor = cea_is_hdmi_vsdb_present(info);
 
 	return 0;
 }