[PATCH 2/2] drm: bridge/dw_hdmi: add dw hdmi i2c bus adapter support

2015-05-17 Thread Vladimir Zapolskiy
On 17.05.2015 21:03, Vladimir Zapolskiy wrote:
> The change adds support of internal HDMI I2C master controller, this
> subdevice is used by default, if "ddc-i2c-bus" DT property is omitted.
> 
> The main purpose of this functionality is to support reading EDID from
> an HDMI monitor on boards, which don't have an I2C bus connected to
> DDC pins.
> 
> Signed-off-by: Vladimir Zapolskiy 
> ---
>  drivers/gpu/drm/bridge/dw_hdmi.c | 332 
> ++-
>  1 file changed, 326 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c 
> b/drivers/gpu/drm/bridge/dw_hdmi.c
> index 49cafb6..2a52449 100644
> --- a/drivers/gpu/drm/bridge/dw_hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw_hdmi.c

[snip]

> + ret = i2c_add_adapter(adap);
> + if (ret) {
> + dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
> + devm_kfree(hdmi->i2c);

Immediately found a compilation problem in last minute update, I'll
resend the change, sorry.

> + hdmi->i2c = NULL;
> + return ERR_PTR(ret);
> + }
> +

--
With best wishes,
Vladimir


[PATCH 2/2] drm: bridge/dw_hdmi: add dw hdmi i2c bus adapter support

2015-05-17 Thread Vladimir Zapolskiy
The change adds support of internal HDMI I2C master controller, this
subdevice is used by default, if "ddc-i2c-bus" DT property is omitted.

The main purpose of this functionality is to support reading EDID from
an HDMI monitor on boards, which don't have an I2C bus connected to
DDC pins.

Signed-off-by: Vladimir Zapolskiy 
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 332 ++-
 1 file changed, 326 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 49cafb6..2a52449 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -1,15 +1,17 @@
 /*
+ * DesignWare High-Definition Multimedia Interface (HDMI) driver
+ *
+ * Copyright (C) 2013-2015 Mentor Graphics Inc.
  * Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
+ * Copyright (C) 2010, Guennadi Liakhovetski 
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
- * Designware High-Definition Multimedia Interface (HDMI) driver
- *
- * Copyright (C) 2010, Guennadi Liakhovetski 
  */
+
 #include 
 #include 
 #include 
@@ -28,6 +30,26 @@

 #include "dw_hdmi.h"

+/* HDMI_IH_I2CM_STAT0 and HDMI_IH_MUTE_I2CM_STAT0 register bits */
+#define HDMI_IH_I2CM_STAT0_ERROR   BIT(0)
+#define HDMI_IH_I2CM_STAT0_DONEBIT(1)
+
+/* HDMI_I2CM_OPERATION register bits */
+#define HDMI_I2CM_OPERATION_READ   BIT(0)
+#define HDMI_I2CM_OPERATION_READ_EXT   BIT(1)
+#define HDMI_I2CM_OPERATION_WRITE  BIT(4)
+
+/* HDMI_I2CM_INT register bits */
+#define HDMI_I2CM_INT_DONE_MASKBIT(2)
+#define HDMI_I2CM_INT_DONE_POL BIT(3)
+
+/* HDMI_I2CM_CTLINT register bits */
+#define HDMI_I2CM_CTLINT_ARB_MASK  BIT(2)
+#define HDMI_I2CM_CTLINT_ARB_POL   BIT(3)
+#define HDMI_I2CM_CTLINT_NAC_MASK  BIT(6)
+#define HDMI_I2CM_CTLINT_NAC_POL   BIT(7)
+
+
 #define HDMI_EDID_LEN  512

 #define RGB0
@@ -102,6 +124,17 @@ struct hdmi_data_info {
struct hdmi_vmode video_mode;
 };

+struct dw_hdmi_i2c {
+   struct i2c_adapter  adap;
+
+   spinlock_t  lock;
+   struct completion   cmp;
+   u8  stat;
+
+   u8  slave_reg;
+   boolis_regaddr;
+};
+
 struct dw_hdmi {
struct drm_connector connector;
struct drm_encoder *encoder;
@@ -111,6 +144,7 @@ struct dw_hdmi {
struct device *dev;
struct clk *isfr_clk;
struct clk *iahb_clk;
+   struct dw_hdmi_i2c *i2c;

struct hdmi_data_info hdmi_data;
const struct dw_hdmi_plat_data *plat_data;
@@ -179,6 +213,242 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 
data, unsigned int reg,
hdmi_modb(hdmi, data << shift, mask, reg);
 }

+static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
+{
+   unsigned long flags;
+
+   spin_lock_irqsave(>i2c->lock, flags);
+
+   /* Set Fast Mode speed */
+   hdmi_writeb(hdmi, 0x0b, HDMI_I2CM_DIV);
+
+   /* Software reset */
+   hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ);
+
+   /* Set done, not acknowledged and arbitration interrupt polarities */
+   hdmi_writeb(hdmi, HDMI_I2CM_INT_DONE_POL, HDMI_I2CM_INT);
+   hdmi_writeb(hdmi, HDMI_I2CM_CTLINT_NAC_POL | HDMI_I2CM_CTLINT_ARB_POL,
+   HDMI_I2CM_CTLINT);
+
+   /* Clear DONE and ERROR interrupts */
+   hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
+   HDMI_IH_I2CM_STAT0);
+
+   /* Mute DONE and ERROR interrupts */
+   hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
+   HDMI_IH_MUTE_I2CM_STAT0);
+
+   spin_unlock_irqrestore(>i2c->lock, flags);
+}
+
+static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi,
+   unsigned char *buf, int length)
+{
+   int stat;
+   unsigned long flags;
+   struct dw_hdmi_i2c *i2c = hdmi->i2c;
+
+   spin_lock_irqsave(>lock, flags);
+
+   if (!i2c->is_regaddr) {
+   dev_dbg(hdmi->dev, "set read register address to 0\n");
+   i2c->slave_reg = 0x00;
+   i2c->is_regaddr = true;
+   }
+
+   while (length--) {
+   hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS);
+   hdmi_writeb(hdmi,
+   HDMI_I2CM_OPERATION_READ, HDMI_I2CM_OPERATION);
+   i2c->stat = 0;
+
+   spin_unlock_irqrestore(>lock, flags);
+
+   stat = wait_for_completion_interruptible_timeout(>cmp,
+HZ / 10);
+   if (!stat)
+