summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSandor Yu <R01008@freescale.com>2014-11-07 11:14:05 +0800
committerNitin Garg <nitin.garg@freescale.com>2015-09-17 09:20:06 -0500
commitea5e2442c871eb5c01979aad4b84a36cfc6ed6b5 (patch)
tree5dce6209656d9f49ce76f0c133efa9b6680982da
parent5dd1803bebdb393d5e7c68d9ec1f611364b8ba00 (diff)
MLK-9808 HDMI: EDID bug fixes / improvements
Changes in order of appearence: - If an EDID extension block other than the known (CEA extension) is found don't fail monitor detection completely, just proceed to the next block. - If 4 or more extensions are present two problems arise: - only 2 extensions will actually be read and - parsing will read beyond the buffer. Throw a BUG() and add a comment, don't have time for a rewrite right now. - The EDID I2C read code has a 1 second timeout - per byte. With 128 bytes per block this could take over 2 minutes. And we have indeed seen a very long pause on Linux shutdown on rare occasions. At 100 kHz reading a byte takes 0.6 ms, reduce the timeout to 30 ms. - Checking extblknum < 0 is pointless when its value was assigned from an unsigned char. - Some old monitors didn't set the 'number of EDID ext. blocks' field. 0xFF means no extensions. - Calling mxc_edid_parse_ext_blk() only makes sense if an ext. block was actually read. Otherwise it's sure to fail, and monitor detection with it. - As the 1st extension was parsed beforehand the following for loop must start at 2, otherwise the 1st extension is parsed twice. - Inside the read loop all bytes were written to tmpedid[1], It should tmpedid[i]. - And then when parsing the read data they have to start at tmpedid[0], not at tmpedid[EDID_LENGTH], which is beyond the buffer. - Improved debugging a bit by inserting a message if reading fails and also removing one that may be confused with another with the same text. - If getting the EDID data fails we will retry once. But if the failure was due to not being able to parse the data rather than a read error re-reading will yield HDMI_EDID_SAME. The code will misinterpret that as 'no change, video modes already set up'. Instead continue with the status code of the initial attempt. - Before retrying wait 0.2 s, most likely reading initially failed because the cable had not been fully inserted. Signed-off-by: Sandor Yu <R01008@freescale.com> (cherry picked from commit 1c580e028ebea180481b8539d3ee4264244a4ec6)
-rw-r--r--drivers/video/mxc/mxc_edid.c8
-rw-r--r--drivers/video/mxc/mxc_hdmi.c35
2 files changed, 28 insertions, 15 deletions
diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c
index 88b52682d99b..d791bfea5609 100644
--- a/drivers/video/mxc/mxc_edid.c
+++ b/drivers/video/mxc/mxc_edid.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2009-2014 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -277,7 +277,7 @@ int mxc_edid_parse_ext_blk(unsigned char *edid,
int i, num = 0, revision;
if (edid[index++] != 0x2) /* only support cea ext block now */
- return -1;
+ return 0;
revision = edid[index++];
DPRINTK("cea extent revision %d\n", revision);
mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
@@ -742,6 +742,10 @@ int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr,
if (extblknum) {
int i;
+ /* FIXME: mxc_edid_readsegblk() won't read more than 2 blocks
+ * and the for-loop will read past the end of the buffer! :-( */
+ BUG_ON(extblknum > 3);
+
/* need read segment block? */
if (extblknum > 1) {
ret = mxc_edid_readsegblk(adp, addr,
diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c
index 51869988ff3c..2a816bc82658 100644
--- a/drivers/video/mxc/mxc_hdmi.c
+++ b/drivers/video/mxc/mxc_hdmi.c
@@ -937,7 +937,7 @@ static u8 hdmi_edid_i2c_read(struct mxc_hdmi *hdmi,
hdmi_writeb(HDMI_I2CM_OPERATION_READ_EXT,
HDMI_I2CM_OPERATION);
- hdmi_edid_wait_i2c_done(hdmi, 1000);
+ hdmi_edid_wait_i2c_done(hdmi, 30);
data = hdmi_readb(HDMI_I2CM_DATAI);
hdmi_writeb(0xFF, HDMI_IH_I2CM_STAT0);
return data;
@@ -1563,8 +1563,8 @@ static int mxc_edid_read_internal(struct mxc_hdmi *hdmi, unsigned char *edid,
}
extblknum = edid[0x7E];
- if (extblknum < 0)
- return extblknum;
+ if (extblknum == 255)
+ extblknum = 0;
if (extblknum) {
ediddata = edid + EDID_LENGTH;
@@ -1578,19 +1578,21 @@ static int mxc_edid_read_internal(struct mxc_hdmi *hdmi, unsigned char *edid,
memset(&fbi->monspecs, 0, sizeof(fbi->monspecs));
fb_edid_to_monspecs(edid, &fbi->monspecs);
- ret = mxc_edid_parse_ext_blk(edid + EDID_LENGTH,
- cfg, &fbi->monspecs);
- if (ret < 0)
- return -ENOENT;
+ if (extblknum) {
+ ret = mxc_edid_parse_ext_blk(edid + EDID_LENGTH,
+ cfg, &fbi->monspecs);
+ if (ret < 0)
+ return -ENOENT;
+ }
/* need read segment block? */
if (extblknum > 1) {
- for (j = 1; j <= extblknum; j++) {
+ for (j = 2; j <= extblknum; j++) {
for (i = 0; i < 128; i++)
- *(tmpedid + 1) = hdmi_edid_i2c_read(hdmi, i, j);
+ tmpedid[i] = hdmi_edid_i2c_read(hdmi, i, j);
/* edid ext block parsing */
- ret = mxc_edid_parse_ext_blk(tmpedid + EDID_LENGTH,
+ ret = mxc_edid_parse_ext_blk(tmpedid,
cfg, &fbi->monspecs);
if (ret < 0)
return -ENOENT;
@@ -1635,8 +1637,10 @@ static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi)
}
}
- if (ret < 0)
+ if (ret < 0) {
+ dev_dbg(&hdmi->pdev->dev, "read failed\n");
return HDMI_EDID_FAIL;
+ }
/* Save edid cfg for audio driver */
hdmi_set_edid_cfg(&hdmi->edid_cfg);
@@ -1833,7 +1837,6 @@ static void mxc_hdmi_default_modelist(struct mxc_hdmi *hdmi)
dev_dbg(&hdmi->pdev->dev, "%s\n", __func__);
/* If not EDID data read, set up default modelist */
- dev_info(&hdmi->pdev->dev, "No modes read from edid\n");
dev_info(&hdmi->pdev->dev, "create default modelist\n");
console_lock();
@@ -1919,8 +1922,14 @@ static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi)
/* Read EDID again if first EDID read failed */
if (edid_status == HDMI_EDID_NO_MODES ||
edid_status == HDMI_EDID_FAIL) {
+ int retry_status;
dev_info(&hdmi->pdev->dev, "Read EDID again\n");
- edid_status = mxc_hdmi_read_edid(hdmi);
+ msleep(200);
+ retry_status = mxc_hdmi_read_edid(hdmi);
+ /* If we get NO_MODES on the 1st and SAME on the 2nd attempt we
+ * want NO_MODES as final result. */
+ if (retry_status != HDMI_EDID_SAME)
+ edid_status = retry_status;
}
/* HDMI Initialization Steps D, E, F */