Change check of DDC status. Instead of silently not reading EDID when in
"IDLE" state [1]. Wait for HDCP being initialized because the EDID and
HDCP block shares memory [2].

[1]
ADV7511 Programming Guide revision G: Table 11: DDCController Status:

  0xC8 [3:0]  DDC Controller State

  0b0000      In Reset (No Hot Plug Detected)
  0b0001      Reading EDID
  0b0010      IDLE (Waiting for HDCP Requested)
  0b0011      Initializing HDCP
  0b0100      HDCP Enabled
  0b0101      Initializing HDCP Repeater

[2]
ADV7511 Programming Guide revision G: 4.6.1.1 EDID Definitions:

  EDID and HDCP use a shared memory space. During HDCP repeater
  initialization, the EDID data is overwritten with HDCP information.
  EDID is not re-read after HDCP initialization. If the user would like
  to re-buffer an EDID segment the EDID re-read register described in
  section 4.6.1.4 should be used.

Fixes: 9c8af882bf12 ("drm: Add adv7511 encoder driver")

Signed-off-by: Emil Svendsen <e...@bang-olufsen.dk>
---
 drivers/gpu/drm/bridge/adv7511/adv7511.h     |  9 +++
 drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 65 ++++++++++++++++----
 2 files changed, 62 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h 
b/drivers/gpu/drm/bridge/adv7511/adv7511.h
index 39c9ece373b0..16d77f4a0aa8 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511.h
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h
@@ -247,6 +247,15 @@ enum adv7511_input_sync_pulse {
        ADV7511_INPUT_SYNC_PULSE_NONE = 3,
 };
 
+enum adv7511_ddc_status {
+       ADV7511_DDC_STATUS_IN_RESET = 0,
+       ADV7511_DDC_STATUS_READING_EDID = 1,
+       ADV7511_DDC_STATUS_WAIT_HDCP = 2,
+       ADV7511_DDC_STATUS_INIT_HDCP = 3,
+       ADV7511_DDC_STATUS_HDCP_ENABLED = 4,
+       ADV7511_DDC_STATUS_INIT_HDCP_REPEATER = 5,
+};
+
 /**
  * enum adv7511_sync_polarity - Polarity for the input sync signals
  * @ADV7511_SYNC_POLARITY_PASSTHROUGH:  Sync polarity matches that of
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c 
b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
index 9b6294120516..f50efb3beb69 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
@@ -533,6 +533,48 @@ static int adv7511_wait_for_edid(struct adv7511 *adv7511, 
int timeout)
        return adv7511->edid_read ? 0 : -EIO;
 }
 
+static int adv7511_wait_for_hdcp(struct adv7511 *adv7511)
+{
+       struct device *dev = &adv7511->i2c_edid->dev;
+       unsigned int status = 0;
+       const int interval = 25;
+       int timeout = 100;
+       int ret = -EINVAL;
+
+       for (; timeout > 0; timeout -= interval) {
+               ret = regmap_read(adv7511->regmap, ADV7511_REG_DDC_STATUS,
+                                 &status);
+               if (ret < 0)
+                       return ret;
+
+               status &= 0x0F;
+
+               switch (status) {
+               case ADV7511_DDC_STATUS_IN_RESET:
+               case ADV7511_DDC_STATUS_READING_EDID:
+               case ADV7511_DDC_STATUS_HDCP_ENABLED:
+                       return 0;
+               case ADV7511_DDC_STATUS_WAIT_HDCP:
+               case ADV7511_DDC_STATUS_INIT_HDCP:
+               case ADV7511_DDC_STATUS_INIT_HDCP_REPEATER:
+                       dev_dbg(dev, "DDC status 0x%x\n", status);
+                       break;
+               default:
+                       dev_err(dev, "Unknown status 0x%x\n", status);
+                       return -EIO;
+               }
+
+               msleep(interval);
+       }
+
+       if (status) {
+               dev_warn(dev, "Stuck in HDCP state 0x%x\n", status);
+               ret = -EAGAIN;
+       }
+
+       return ret;
+}
+
 static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block,
                                  size_t len)
 {
@@ -547,21 +589,20 @@ static int adv7511_get_edid_block(void *data, u8 *buf, 
unsigned int block,
                return -EINVAL;
 
        if (adv7511->current_edid_segment != edid_segment) {
-               unsigned int status;
-
-               ret = regmap_read(adv7511->regmap, ADV7511_REG_DDC_STATUS,
-                                 &status);
+               /*
+                * EDID and HDCP shares memory so make sure HDCP is done before
+                * reading EDID.
+                */
+               ret = adv7511_wait_for_hdcp(adv7511);
                if (ret < 0)
                        return ret;
 
-               if (status != 2) {
-                       adv7511->edid_read = false;
-                       regmap_write(adv7511->regmap, ADV7511_REG_EDID_SEGMENT,
-                                    edid_segment);
-                       ret = adv7511_wait_for_edid(adv7511, 200);
-                       if (ret < 0)
-                               return ret;
-               }
+               adv7511->edid_read = false;
+               regmap_write(adv7511->regmap, ADV7511_REG_EDID_SEGMENT,
+                            edid_segment);
+               ret = adv7511_wait_for_edid(adv7511, 200);
+               if (ret < 0)
+                       return ret;
 
                /* Break this apart, hopefully more I2C controllers will
                 * support 64 byte transfers than 256 byte transfers
-- 
2.34.1

Reply via email to