[U-Boot] [PATCH 18/25] edid: Add a function to read detailed monitor timings

Simon Glass sjg at chromium.org
Tue Dec 2 01:48:46 CET 2014


For digital displays (such as EDP LCDs) we would like to read the EDID
information and use that to set display timings. Provide a function to do
this.

Signed-off-by: Simon Glass <sjg at chromium.org>
---

 common/edid.c  | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/edid.h |  19 +++++++++++
 2 files changed, 125 insertions(+)

diff --git a/common/edid.c b/common/edid.c
index e66108f..c031eee 100644
--- a/common/edid.c
+++ b/common/edid.c
@@ -12,6 +12,8 @@
 
 #include <common.h>
 #include <edid.h>
+#include <errno.h>
+#include <fdtdec.h>
 #include <linux/ctype.h>
 #include <linux/string.h>
 
@@ -53,6 +55,110 @@ int edid_get_ranges(struct edid1_info *edid, unsigned int *hmin,
 	return -1;
 }
 
+/* Set all parts of a timing entry to the same value */
+static void set_entry(struct timing_entry *entry, u32 value)
+{
+	entry->min = value;
+	entry->typ = value;
+	entry->max = value;
+}
+
+/**
+ * decode_timing() - Decoding an 18-byte detailed timing record
+ *
+ * @buf:	Pointer to EDID detailed timing record
+ * @timing:	Place to put timing
+ */
+static void decode_timing(u8 *buf, struct display_timing *timing)
+{
+	uint x_mm, y_mm;
+	unsigned int ha, hbl, hso, hspw, hborder;
+	unsigned int va, vbl, vso, vspw, vborder;
+
+	/* Edid contains pixel clock in terms of 10KHz */
+	set_entry(&timing->pixelclock, (buf[0] + (buf[1] << 8)) * 10000);
+	x_mm = (buf[12] + ((buf[14] & 0xf0) << 4));
+	y_mm = (buf[13] + ((buf[14] & 0x0f) << 8));
+	ha = (buf[2] + ((buf[4] & 0xf0) << 4));
+	hbl = (buf[3] + ((buf[4] & 0x0f) << 8));
+	hso = (buf[8] + ((buf[11] & 0xc0) << 2));
+	hspw = (buf[9] + ((buf[11] & 0x30) << 4));
+	hborder = buf[15];
+	va = (buf[5] + ((buf[7] & 0xf0) << 4));
+	vbl = (buf[6] + ((buf[7] & 0x0f) << 8));
+	vso = ((buf[10] >> 4) + ((buf[11] & 0x0c) << 2));
+	vspw = ((buf[10] & 0x0f) + ((buf[11] & 0x03) << 4));
+	vborder = buf[16];
+
+	set_entry(&timing->hactive, ha);
+	set_entry(&timing->hfront_porch, hso);
+	set_entry(&timing->hback_porch, hbl - hso - hspw);
+	set_entry(&timing->hsync_len, hspw);
+
+	set_entry(&timing->vactive, va);
+	set_entry(&timing->vfront_porch, vso);
+	set_entry(&timing->vback_porch, vbl - vso - vspw);
+	set_entry(&timing->vsync_len, vspw);
+
+	debug("Detailed mode clock %u Hz, %d mm x %d mm\n"
+	      "               %04x %04x %04x %04x hborder %x\n"
+	      "               %04x %04x %04x %04x vborder %x\n",
+	      timing->pixelclock.typ,
+	      x_mm, y_mm,
+	      ha, ha + hso, ha + hso + hspw,
+	      ha + hbl, hborder,
+	      va, va + vso, va + vso + vspw,
+	      va + vbl, vborder);
+}
+
+int edid_get_timing(u8 *buf, int buf_size, struct display_timing *timing,
+		    int *panel_bits_per_colourp)
+{
+	struct edid1_info *edid = (struct edid1_info *)buf;
+	bool timing_done;
+	int i;
+
+	if (buf_size < sizeof(*edid) || edid_check_info(edid)) {
+		debug("%s: Invalid buffer\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(*edid)) {
+		debug("%s: No preferred timing\n", __func__);
+		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[i];
+		if (desc->zero_flag_1 != 0) {
+			decode_timing((u8 *)desc, timing);
+			timing_done = true;
+			break;
+		}
+	}
+	if (!timing_done)
+		return -EINVAL;
+
+	if (!EDID1_INFO_VIDEO_INPUT_DIGITAL(*edid)) {
+		debug("%s: Not a digital display\n", __func__);
+		return -ENOSYS;
+	}
+	if (edid->version != 1 || edid->revision < 4) {
+		debug("%s: EDID version %d.%d does not have required info\n",
+		      __func__, edid->version, edid->revision);
+		*panel_bits_per_colourp = -1;
+	} else  {
+		*panel_bits_per_colourp =
+			((edid->video_input_definition & 0x70) >> 3) + 4;
+	}
+
+	return 0;
+}
+
 /**
  * Snip the tailing whitespace/return of a string.
  *
diff --git a/include/edid.h b/include/edid.h
index 480a773..d331738 100644
--- a/include/edid.h
+++ b/include/edid.h
@@ -15,6 +15,9 @@
 
 #include <linux/types.h>
 
+/* Size of the EDID data */
+#define EDID_SIZE	128
+
 #define GET_BIT(_x, _pos) \
 	(((_x) >> (_pos)) & 1)
 #define GET_BITS(_x, _pos_msb, _pos_lsb) \
@@ -255,4 +258,20 @@ int edid_get_ranges(struct edid1_info *edid, unsigned int *hmin,
 		    unsigned int *hmax, unsigned int *vmin,
 		    unsigned int *vmax);
 
+struct display_timing;
+
+/**
+ * edid_get_timing() - Get basic digital display parameters
+ *
+ * @param buf		Buffer containing EDID data
+ * @param buf_size	Size of buffer in bytes
+ * @param timing	Place to put preferring timing information
+ * @param panel_bits_per_colourp	Place to put the number of bits per
+ *			colour supported by the panel. This will be set to
+ *			-1 if not available
+ * @return 0 if timings are OK, -ve on error
+ */
+int edid_get_timing(u8 *buf, int buf_size, struct display_timing *timing,
+		    int *panel_bits_per_colourp);
+
 #endif /* __EDID_H_ */
-- 
2.2.0.rc0.207.ga3a616c



More information about the U-Boot mailing list