diff options
author | Sandor Yu <R01008@freescale.com> | 2014-11-07 11:14:05 +0800 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2015-09-17 09:20:06 -0500 |
commit | ea5e2442c871eb5c01979aad4b84a36cfc6ed6b5 (patch) | |
tree | 5dce6209656d9f49ce76f0c133efa9b6680982da | |
parent | 5dd1803bebdb393d5e7c68d9ec1f611364b8ba00 (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.c | 8 | ||||
-rw-r--r-- | drivers/video/mxc/mxc_hdmi.c | 35 |
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 */ |