| From be9059ee366d592ad512e717c6126e63f355b4cc Mon Sep 17 00:00:00 2001 |
| From: Nickey Yang <nickey.yang@rock-chips.com> |
| Date: Mon, 20 Mar 2017 10:57:31 +0800 |
| Subject: [PATCH 235/286] drm/bridge: dw_hdmi: support i2c extended read mode |
| |
| "I2C Master Interface Extended Read Mode" implements a segment |
| pointer-based read operation using the Special Register configuration. |
| |
| This patch fix https://patchwork.kernel.org/patch/7098101/ mentioned |
| "The current implementation does not support "I2C Master Interface |
| Extended Read Mode" to read data addressed by non-zero segment |
| pointer, this means that if EDID has more than 1 extension blocks, |
| EDID reading operation won't succeed" |
| |
| With this patch, dw-hdmi can read EDID data with 1/2/4 blocks. |
| |
| Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com> |
| Reviewed-by: Douglas Anderson <dianders@chromium.org> |
| Acked-by: Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com> |
| Signed-off-by: Archit Taneja <architt@codeaurora.org> |
| Link: http://patchwork.freedesktop.org/patch/msgid/1489978651-16647-1-git-send-email-nickey.yang@rock-chips.com |
| (cherry picked from commit 94bb4dc132ed2e3a4d16649b0096c49d13670fe8) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 38 ++++++++++++++++++------------ |
| 1 file changed, 24 insertions(+), 14 deletions(-) |
| |
| --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c |
| +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c |
| @@ -33,6 +33,7 @@ |
| #include "dw-hdmi.h" |
| #include "dw-hdmi-audio.h" |
| |
| +#define DDC_SEGMENT_ADDR 0x30 |
| #define HDMI_EDID_LEN 512 |
| |
| #define RGB 0 |
| @@ -112,6 +113,7 @@ struct dw_hdmi_i2c { |
| |
| u8 slave_reg; |
| bool is_regaddr; |
| + bool is_segment; |
| }; |
| |
| struct dw_hdmi_phy_data { |
| @@ -247,8 +249,12 @@ static int dw_hdmi_i2c_read(struct dw_hd |
| reinit_completion(&i2c->cmp); |
| |
| hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS); |
| - hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ, |
| - HDMI_I2CM_OPERATION); |
| + if (i2c->is_segment) |
| + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ_EXT, |
| + HDMI_I2CM_OPERATION); |
| + else |
| + hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ, |
| + HDMI_I2CM_OPERATION); |
| |
| stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); |
| if (!stat) |
| @@ -260,6 +266,7 @@ static int dw_hdmi_i2c_read(struct dw_hd |
| |
| *buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI); |
| } |
| + i2c->is_segment = false; |
| |
| return 0; |
| } |
| @@ -309,12 +316,6 @@ static int dw_hdmi_i2c_xfer(struct i2c_a |
| dev_dbg(hdmi->dev, "xfer: num: %d, addr: %#x\n", num, addr); |
| |
| for (i = 0; i < num; i++) { |
| - if (msgs[i].addr != addr) { |
| - dev_warn(hdmi->dev, |
| - "unsupported transfer, changed slave address\n"); |
| - return -EOPNOTSUPP; |
| - } |
| - |
| if (msgs[i].len == 0) { |
| dev_dbg(hdmi->dev, |
| "unsupported transfer %d/%d, no data\n", |
| @@ -334,15 +335,24 @@ static int dw_hdmi_i2c_xfer(struct i2c_a |
| /* Set slave device register address on transfer */ |
| i2c->is_regaddr = false; |
| |
| + /* Set segment pointer for I2C extended read mode operation */ |
| + i2c->is_segment = false; |
| + |
| for (i = 0; i < num; i++) { |
| dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n", |
| i + 1, num, msgs[i].len, msgs[i].flags); |
| - |
| - if (msgs[i].flags & I2C_M_RD) |
| - ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, msgs[i].len); |
| - else |
| - ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, msgs[i].len); |
| - |
| + if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) { |
| + i2c->is_segment = true; |
| + hdmi_writeb(hdmi, DDC_SEGMENT_ADDR, HDMI_I2CM_SEGADDR); |
| + hdmi_writeb(hdmi, *msgs[i].buf, HDMI_I2CM_SEGPTR); |
| + } else { |
| + if (msgs[i].flags & I2C_M_RD) |
| + ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, |
| + msgs[i].len); |
| + else |
| + ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, |
| + msgs[i].len); |
| + } |
| if (ret < 0) |
| break; |
| } |