diff mbox series

[v3,2/2] drm: bridge: adv7511: fix edid read in hdcp state

Message ID 20231220135406.2318148-2-emas@bang-olufsen.dk (mailing list archive)
State New, archived
Headers show
Series [v3,1/2] drm: bridge: adv7511: fix reading edid segments | expand

Commit Message

Emil Abildgaard Svendsen Dec. 20, 2023, 1:54 p.m. UTC
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 <emas@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 mbox series

Patch

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