Skip to contentSkip to conversation

drm/sun4i: hdmi: Implement I2C adapter for A10s DDC bus

PATCH Superseded Jonathan Liu 1 participant 1 message Jun 28, 2017

Subsystems (4)

v6 of 8 Superseded Latest is v8 →

lore
Share & tools
lore
#0 lore raw

PATCH v6 1/1 drm/sun4i: hdmi: Implement I2C adapter for A10s DDC bus

The documentation for drm_do_get_edid in drivers/gpu/drm/drm_edid.c states:
"As in the general case the DDC bus is accessible by the kernel at the I2C level, drivers must make all reasonable efforts to expose it as an I2C adapter and use drm_get_edid() instead of abusing this function."

Exposing the DDC bus as an I2C adapter is more beneficial as it can be used for purposes other than reading the EDID such as modifying the EDID or using the HDMI DDC pins as an I2C bus through the I2C dev interface from userspace (e.g. i2c-tools).

Implement this for A10s.

---
Changes for v6:
- Use fixed byte time of 100 us instead of dynamically calculating from DDC clock that is set to a fixed 100 MHz rate anyway
- Change is_fifo_flag_unset to not read the status register as well to be more consistent with is_err_status

Changes for v5:
- Use devm_kzalloc instead of devm_kmemdup and remove const struct i2c_adapter
- Rework to use readl_poll_timeout for checking FIFO status

Changes for v4:
- Carry over copyright from initial I2C code into sun4i_hdmi_i2c.c
- Clean up indentation in sun4i_hdmi.h
- Rename SUN4I_HDMI_DDC_MAX_TRANSFER_SIZE to SUN4I_HDMI_DDC_BYTE_COUNT_MAX and group it under the SUN4I_HDMI_DDC_BYTE_COUNT_REG define, changing the value to use the GENMASK macro to make it clear that it is derived from the width of the field in the register
- Fix SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_UNDERFLOW typo which should be SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW
- Remove redundant rewriting of SUN4I_HDMI_DDC_INT_STATUS_REG register
- Change struct i2c_adapter to be const by using devm_kmemdup on creation
- Return -ETIMEDOUT instead of -EIO if there is timeout while transferring an I2C message
- Instead of waiting for 1-2 bytes to transfer, wait for the time it would take for remaining bytes to transfer (limited by FIFO size)
- Add additional comments

Changes for v3:
- Explain why drm_do_get_edid should be used and why it's better to expose it as an I2C adapter in commit message
- Reorder bit defines in descending order for consistency
- Keep old unused macros instead of removing them
- The v2 algorithm split large transfers into 16 byte transfers but this may cause a large single write to be treated as multiple writes causing data corruption. The algorithm has been reworked to not split larger transfers and make more use of the FIFO to avoid this.
- Moved the creation of the DDC clock from sun4i_hdmi_enc.c to sun4i_hdmi_i2c.c
- Reformatted code
- Instead of masking bits that we don't want to check for errors, explicitly check the error bits
- Clear error bits at start of transfer in case of error from previous transfer
- Poll for completion of FIFO clear after setting FIFO clear bit

Changes for v2:
- Rebased against Maxime's sunxi-drm/for-next branch
- Fix up error paths in sun4i_hdmi_bind so that the I2C adapter is deleted if any of the calls after the I2C adapter is created fails
- Remove unnecessary includes in sun4i_hdmi_i2c.c

 drivers/gpu/drm/sun4i/Makefile         |   1 +
 drivers/gpu/drm/sun4i/sun4i_hdmi.h     |  23 ++++
 drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 101 ++------------
 drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c | 233 +++++++++++++++++++++++++++++++++
 4 files changed, 268 insertions(+), 90 deletions(-)
 create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c
drivers/gpu/drm/sun4i/Makefile +1 -0
index e29fd3a2ba9c..43c753cafc88 100644
@@ -2,6 +2,7 @@ sun4i-drm-y += sun4i_drv.o
 sun4i-drm-y += sun4i_framebuffer.o
 
 sun4i-drm-hdmi-y += sun4i_hdmi_enc.o
+sun4i-drm-hdmi-y += sun4i_hdmi_i2c.o
 sun4i-drm-hdmi-y += sun4i_hdmi_ddc_clk.o
 sun4i-drm-hdmi-y += sun4i_hdmi_tmds_clk.o
 
drivers/gpu/drm/sun4i/sun4i_hdmi.h +23 -0
index 2f2f2ff1ea63..eaff92fe236c 100644
@@ -96,6 +96,7 @@
 #define SUN4I_HDMI_DDC_CTRL_ENABLE		BIT(31)
 #define SUN4I_HDMI_DDC_CTRL_START_CMD		BIT(30)
 #define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK	BIT(8)
+#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE	(1 << 8)
 #define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ	(0 << 8)
 #define SUN4I_HDMI_DDC_CTRL_RESET		BIT(0)
 
@@ -105,14 +106,33 @@
 #define SUN4I_HDMI_DDC_ADDR_OFFSET(off)		(((off) & 0xff) << 8)
 #define SUN4I_HDMI_DDC_ADDR_SLAVE(addr)		((addr) & 0xff)
 
+#define SUN4I_HDMI_DDC_INT_STATUS_REG		0x50c
+#define SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION	BIT(7)
+#define SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW		BIT(6)
+#define SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW		BIT(5)
+#define SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST			BIT(4)
+#define SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR		BIT(3)
+#define SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR			BIT(2)
+#define SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR			BIT(1)
+#define SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE		BIT(0)
+
 #define SUN4I_HDMI_DDC_FIFO_CTRL_REG	0x510
 #define SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR		BIT(31)
 
+#define SUN4I_HDMI_DDC_FIFO_STATUS_REG	0x514
+#define SUN4I_HDMI_DDC_FIFO_STATUS_FULL		BIT(6)
+#define SUN4I_HDMI_DDC_FIFO_STATUS_EMPTY	BIT(5)
+#define SUN4I_HDMI_DDC_FIFO_STATUS_LEVEL_MASK	GENMASK(4, 0)
+
 #define SUN4I_HDMI_DDC_FIFO_DATA_REG	0x518
+
 #define SUN4I_HDMI_DDC_BYTE_COUNT_REG	0x51c
+#define SUN4I_HDMI_DDC_BYTE_COUNT_MAX		GENMASK(9, 0)
 
 #define SUN4I_HDMI_DDC_CMD_REG		0x520
 #define SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ	6
+#define SUN4I_HDMI_DDC_CMD_IMPLICIT_READ	5
+#define SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE	3
 
 #define SUN4I_HDMI_DDC_CLK_REG		0x528
 #define SUN4I_HDMI_DDC_CLK_M(m)			(((m) & 0x7) << 3)
@@ -146,6 +166,8 @@ struct sun4i_hdmi {
 	struct clk		*ddc_clk;
 	struct clk		*tmds_clk;
 
+	struct i2c_adapter	*i2c;
+
 	struct sun4i_drv	*drv;
 
 	bool			hdmi_monitor;
@@ -153,5 +175,6 @@ struct sun4i_hdmi {
 
 int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *clk);
 int sun4i_tmds_create(struct sun4i_hdmi *hdmi);
+int sun4i_hdmi_i2c_create(struct device *dev, struct sun4i_hdmi *hdmi);
 
 #endif /* _SUN4I_HDMI_H_ */
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +11 -90

154 lines not on this page · raw message

drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c new file +233 -0

233 lines not on this page · raw message

Signature · 1 line
2.13.1