aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVladimir Zapolskiy <vladimir_zapolskiy@mentor.com>2016-08-24 01:46:37 -0400
committerPhilipp Zabel <p.zabel@pengutronix.de>2016-09-19 02:07:24 -0400
commit3efc2fa3b777e65e344a7612d38a8278e78a0514 (patch)
tree2ebd3d55285160f4e724cb2a8cfd46eeeb6b7896
parent9f04a1f2b389a656486c1f9f918a3305569fba6a (diff)
drm: bridge/dw_hdmi: add dw hdmi i2c bus adapter support
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. 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, in my practice all tested HDMI monitors have at maximum one extension block. Signed-off-by: Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com> Acked-by: Rob Herring <robh@kernel.org> Tested-by: Philipp Zabel <p.zabel@pengutronix.de> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
-rw-r--r--Documentation/devicetree/bindings/display/bridge/dw_hdmi.txt4
-rw-r--r--drivers/gpu/drm/bridge/dw-hdmi.c265
-rw-r--r--drivers/gpu/drm/bridge/dw-hdmi.h19
3 files changed, 281 insertions, 7 deletions
diff --git a/Documentation/devicetree/bindings/display/bridge/dw_hdmi.txt b/Documentation/devicetree/bindings/display/bridge/dw_hdmi.txt
index dc1452f0d5d8..5e9a84d6e5f1 100644
--- a/Documentation/devicetree/bindings/display/bridge/dw_hdmi.txt
+++ b/Documentation/devicetree/bindings/display/bridge/dw_hdmi.txt
@@ -19,7 +19,9 @@ Required properties:
19 19
20Optional properties 20Optional properties
21- reg-io-width: the width of the reg:1,4, default set to 1 if not present 21- reg-io-width: the width of the reg:1,4, default set to 1 if not present
22- ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing 22- ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing,
23 if the property is omitted, a functionally reduced I2C bus
24 controller on DW HDMI is probed
23- clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec" 25- clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec"
24 26
25Example: 27Example:
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index ce3527cd0d25..cdf0a3a2e6f8 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -1,14 +1,15 @@
1/* 1/*
2 * DesignWare High-Definition Multimedia Interface (HDMI) driver
3 *
4 * Copyright (C) 2013-2015 Mentor Graphics Inc.
2 * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. 5 * Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
6 * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
3 * 7 *
4 * This program is free software; you can redistribute it and/or modify 8 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by 9 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or 10 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version. 11 * (at your option) any later version.
8 * 12 *
9 * Designware High-Definition Multimedia Interface (HDMI) driver
10 *
11 * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
12 */ 13 */
13#include <linux/module.h> 14#include <linux/module.h>
14#include <linux/irq.h> 15#include <linux/irq.h>
@@ -101,6 +102,17 @@ struct hdmi_data_info {
101 struct hdmi_vmode video_mode; 102 struct hdmi_vmode video_mode;
102}; 103};
103 104
105struct dw_hdmi_i2c {
106 struct i2c_adapter adap;
107
108 struct mutex lock; /* used to serialize data transfers */
109 struct completion cmp;
110 u8 stat;
111
112 u8 slave_reg;
113 bool is_regaddr;
114};
115
104struct dw_hdmi { 116struct dw_hdmi {
105 struct drm_connector connector; 117 struct drm_connector connector;
106 struct drm_encoder *encoder; 118 struct drm_encoder *encoder;
@@ -111,6 +123,7 @@ struct dw_hdmi {
111 struct device *dev; 123 struct device *dev;
112 struct clk *isfr_clk; 124 struct clk *isfr_clk;
113 struct clk *iahb_clk; 125 struct clk *iahb_clk;
126 struct dw_hdmi_i2c *i2c;
114 127
115 struct hdmi_data_info hdmi_data; 128 struct hdmi_data_info hdmi_data;
116 const struct dw_hdmi_plat_data *plat_data; 129 const struct dw_hdmi_plat_data *plat_data;
@@ -198,6 +211,201 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg,
198 hdmi_modb(hdmi, data << shift, mask, reg); 211 hdmi_modb(hdmi, data << shift, mask, reg);
199} 212}
200 213
214static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
215{
216 /* Software reset */
217 hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ);
218
219 /* Set Standard Mode speed (determined to be 100KHz on iMX6) */
220 hdmi_writeb(hdmi, 0x00, HDMI_I2CM_DIV);
221
222 /* Set done, not acknowledged and arbitration interrupt polarities */
223 hdmi_writeb(hdmi, HDMI_I2CM_INT_DONE_POL, HDMI_I2CM_INT);
224 hdmi_writeb(hdmi, HDMI_I2CM_CTLINT_NAC_POL | HDMI_I2CM_CTLINT_ARB_POL,
225 HDMI_I2CM_CTLINT);
226
227 /* Clear DONE and ERROR interrupts */
228 hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
229 HDMI_IH_I2CM_STAT0);
230
231 /* Mute DONE and ERROR interrupts */
232 hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
233 HDMI_IH_MUTE_I2CM_STAT0);
234}
235
236static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi,
237 unsigned char *buf, unsigned int length)
238{
239 struct dw_hdmi_i2c *i2c = hdmi->i2c;
240 int stat;
241
242 if (!i2c->is_regaddr) {
243 dev_dbg(hdmi->dev, "set read register address to 0\n");
244 i2c->slave_reg = 0x00;
245 i2c->is_regaddr = true;
246 }
247
248 while (length--) {
249 reinit_completion(&i2c->cmp);
250
251 hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS);
252 hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_READ,
253 HDMI_I2CM_OPERATION);
254
255 stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
256 if (!stat)
257 return -EAGAIN;
258
259 /* Check for error condition on the bus */
260 if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR)
261 return -EIO;
262
263 *buf++ = hdmi_readb(hdmi, HDMI_I2CM_DATAI);
264 }
265
266 return 0;
267}
268
269static int dw_hdmi_i2c_write(struct dw_hdmi *hdmi,
270 unsigned char *buf, unsigned int length)
271{
272 struct dw_hdmi_i2c *i2c = hdmi->i2c;
273 int stat;
274
275 if (!i2c->is_regaddr) {
276 /* Use the first write byte as register address */
277 i2c->slave_reg = buf[0];
278 length--;
279 buf++;
280 i2c->is_regaddr = true;
281 }
282
283 while (length--) {
284 reinit_completion(&i2c->cmp);
285
286 hdmi_writeb(hdmi, *buf++, HDMI_I2CM_DATAO);
287 hdmi_writeb(hdmi, i2c->slave_reg++, HDMI_I2CM_ADDRESS);
288 hdmi_writeb(hdmi, HDMI_I2CM_OPERATION_WRITE,
289 HDMI_I2CM_OPERATION);
290
291 stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
292 if (!stat)
293 return -EAGAIN;
294
295 /* Check for error condition on the bus */
296 if (i2c->stat & HDMI_IH_I2CM_STAT0_ERROR)
297 return -EIO;
298 }
299
300 return 0;
301}
302
303static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap,
304 struct i2c_msg *msgs, int num)
305{
306 struct dw_hdmi *hdmi = i2c_get_adapdata(adap);
307 struct dw_hdmi_i2c *i2c = hdmi->i2c;
308 u8 addr = msgs[0].addr;
309 int i, ret = 0;
310
311 dev_dbg(hdmi->dev, "xfer: num: %d, addr: %#x\n", num, addr);
312
313 for (i = 0; i < num; i++) {
314 if (msgs[i].addr != addr) {
315 dev_warn(hdmi->dev,
316 "unsupported transfer, changed slave address\n");
317 return -EOPNOTSUPP;
318 }
319
320 if (msgs[i].len == 0) {
321 dev_dbg(hdmi->dev,
322 "unsupported transfer %d/%d, no data\n",
323 i + 1, num);
324 return -EOPNOTSUPP;
325 }
326 }
327
328 mutex_lock(&i2c->lock);
329
330 /* Unmute DONE and ERROR interrupts */
331 hdmi_writeb(hdmi, 0x00, HDMI_IH_MUTE_I2CM_STAT0);
332
333 /* Set slave device address taken from the first I2C message */
334 hdmi_writeb(hdmi, addr, HDMI_I2CM_SLAVE);
335
336 /* Set slave device register address on transfer */
337 i2c->is_regaddr = false;
338
339 for (i = 0; i < num; i++) {
340 dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
341 i + 1, num, msgs[i].len, msgs[i].flags);
342
343 if (msgs[i].flags & I2C_M_RD)
344 ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf, msgs[i].len);
345 else
346 ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf, msgs[i].len);
347
348 if (ret < 0)
349 break;
350 }
351
352 if (!ret)
353 ret = num;
354
355 /* Mute DONE and ERROR interrupts */
356 hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
357 HDMI_IH_MUTE_I2CM_STAT0);
358
359 mutex_unlock(&i2c->lock);
360
361 return ret;
362}
363
364static u32 dw_hdmi_i2c_func(struct i2c_adapter *adapter)
365{
366 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
367}
368
369static const struct i2c_algorithm dw_hdmi_algorithm = {
370 .master_xfer = dw_hdmi_i2c_xfer,
371 .functionality = dw_hdmi_i2c_func,
372};
373
374static struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi *hdmi)
375{
376 struct i2c_adapter *adap;
377 struct dw_hdmi_i2c *i2c;
378 int ret;
379
380 i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
381 if (!i2c)
382 return ERR_PTR(-ENOMEM);
383
384 mutex_init(&i2c->lock);
385 init_completion(&i2c->cmp);
386
387 adap = &i2c->adap;
388 adap->class = I2C_CLASS_DDC;
389 adap->owner = THIS_MODULE;
390 adap->dev.parent = hdmi->dev;
391 adap->algo = &dw_hdmi_algorithm;
392 strlcpy(adap->name, "DesignWare HDMI", sizeof(adap->name));
393 i2c_set_adapdata(adap, hdmi);
394
395 ret = i2c_add_adapter(adap);
396 if (ret) {
397 dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
398 devm_kfree(hdmi->dev, i2c);
399 return ERR_PTR(ret);
400 }
401
402 hdmi->i2c = i2c;
403
404 dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
405
406 return adap;
407}
408
201static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts, 409static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
202 unsigned int n) 410 unsigned int n)
203{ 411{
@@ -1517,16 +1725,40 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
1517 .mode_set = dw_hdmi_bridge_mode_set, 1725 .mode_set = dw_hdmi_bridge_mode_set,
1518}; 1726};
1519 1727
1728static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi)
1729{
1730 struct dw_hdmi_i2c *i2c = hdmi->i2c;
1731 unsigned int stat;
1732
1733 stat = hdmi_readb(hdmi, HDMI_IH_I2CM_STAT0);
1734 if (!stat)
1735 return IRQ_NONE;
1736
1737 hdmi_writeb(hdmi, stat, HDMI_IH_I2CM_STAT0);
1738
1739 i2c->stat = stat;
1740
1741 complete(&i2c->cmp);
1742
1743 return IRQ_HANDLED;
1744}
1745
1520static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id) 1746static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
1521{ 1747{
1522 struct dw_hdmi *hdmi = dev_id; 1748 struct dw_hdmi *hdmi = dev_id;
1523 u8 intr_stat; 1749 u8 intr_stat;
1750 irqreturn_t ret = IRQ_NONE;
1751
1752 if (hdmi->i2c)
1753 ret = dw_hdmi_i2c_irq(hdmi);
1524 1754
1525 intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0); 1755 intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
1526 if (intr_stat) 1756 if (intr_stat) {
1527 hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); 1757 hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
1758 return IRQ_WAKE_THREAD;
1759 }
1528 1760
1529 return intr_stat ? IRQ_WAKE_THREAD : IRQ_NONE; 1761 return ret;
1530} 1762}
1531 1763
1532static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) 1764static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
@@ -1751,6 +1983,13 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
1751 */ 1983 */
1752 hdmi_init_clk_regenerator(hdmi); 1984 hdmi_init_clk_regenerator(hdmi);
1753 1985
1986 /* If DDC bus is not specified, try to register HDMI I2C bus */
1987 if (!hdmi->ddc) {
1988 hdmi->ddc = dw_hdmi_i2c_adapter(hdmi);
1989 if (IS_ERR(hdmi->ddc))
1990 hdmi->ddc = NULL;
1991 }
1992
1754 /* 1993 /*
1755 * Configure registers related to HDMI interrupt 1994 * Configure registers related to HDMI interrupt
1756 * generation before registering IRQ. 1995 * generation before registering IRQ.
@@ -1791,11 +2030,20 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
1791 hdmi->audio = platform_device_register_full(&pdevinfo); 2030 hdmi->audio = platform_device_register_full(&pdevinfo);
1792 } 2031 }
1793 2032
2033 /* Reset HDMI DDC I2C master controller and mute I2CM interrupts */
2034 if (hdmi->i2c)
2035 dw_hdmi_i2c_init(hdmi);
2036
1794 dev_set_drvdata(dev, hdmi); 2037 dev_set_drvdata(dev, hdmi);
1795 2038
1796 return 0; 2039 return 0;
1797 2040
1798err_iahb: 2041err_iahb:
2042 if (hdmi->i2c) {
2043 i2c_del_adapter(&hdmi->i2c->adap);
2044 hdmi->ddc = NULL;
2045 }
2046
1799 clk_disable_unprepare(hdmi->iahb_clk); 2047 clk_disable_unprepare(hdmi->iahb_clk);
1800err_isfr: 2048err_isfr:
1801 clk_disable_unprepare(hdmi->isfr_clk); 2049 clk_disable_unprepare(hdmi->isfr_clk);
@@ -1821,13 +2069,18 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
1821 2069
1822 clk_disable_unprepare(hdmi->iahb_clk); 2070 clk_disable_unprepare(hdmi->iahb_clk);
1823 clk_disable_unprepare(hdmi->isfr_clk); 2071 clk_disable_unprepare(hdmi->isfr_clk);
1824 i2c_put_adapter(hdmi->ddc); 2072
2073 if (hdmi->i2c)
2074 i2c_del_adapter(&hdmi->i2c->adap);
2075 else
2076 i2c_put_adapter(hdmi->ddc);
1825} 2077}
1826EXPORT_SYMBOL_GPL(dw_hdmi_unbind); 2078EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
1827 2079
1828MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 2080MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
1829MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>"); 2081MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
1830MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>"); 2082MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
2083MODULE_AUTHOR("Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>");
1831MODULE_DESCRIPTION("DW HDMI transmitter driver"); 2084MODULE_DESCRIPTION("DW HDMI transmitter driver");
1832MODULE_LICENSE("GPL"); 2085MODULE_LICENSE("GPL");
1833MODULE_ALIAS("platform:dw-hdmi"); 2086MODULE_ALIAS("platform:dw-hdmi");
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
index fc9a560429d6..6aadc840e888 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.h
+++ b/drivers/gpu/drm/bridge/dw-hdmi.h
@@ -566,6 +566,10 @@ enum {
566 HDMI_IH_PHY_STAT0_TX_PHY_LOCK = 0x2, 566 HDMI_IH_PHY_STAT0_TX_PHY_LOCK = 0x2,
567 HDMI_IH_PHY_STAT0_HPD = 0x1, 567 HDMI_IH_PHY_STAT0_HPD = 0x1,
568 568
569/* IH_I2CM_STAT0 and IH_MUTE_I2CM_STAT0 field values */
570 HDMI_IH_I2CM_STAT0_DONE = 0x2,
571 HDMI_IH_I2CM_STAT0_ERROR = 0x1,
572
569/* IH_MUTE_I2CMPHY_STAT0 field values */ 573/* IH_MUTE_I2CMPHY_STAT0 field values */
570 HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYDONE = 0x2, 574 HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYDONE = 0x2,
571 HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYERROR = 0x1, 575 HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYERROR = 0x1,
@@ -1032,6 +1036,21 @@ enum {
1032 HDMI_A_VIDPOLCFG_HSYNCPOL_MASK = 0x2, 1036 HDMI_A_VIDPOLCFG_HSYNCPOL_MASK = 0x2,
1033 HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH = 0x2, 1037 HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH = 0x2,
1034 HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW = 0x0, 1038 HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW = 0x0,
1039
1040/* I2CM_OPERATION field values */
1041 HDMI_I2CM_OPERATION_WRITE = 0x10,
1042 HDMI_I2CM_OPERATION_READ_EXT = 0x2,
1043 HDMI_I2CM_OPERATION_READ = 0x1,
1044
1045/* I2CM_INT field values */
1046 HDMI_I2CM_INT_DONE_POL = 0x8,
1047 HDMI_I2CM_INT_DONE_MASK = 0x4,
1048
1049/* I2CM_CTLINT field values */
1050 HDMI_I2CM_CTLINT_NAC_POL = 0x80,
1051 HDMI_I2CM_CTLINT_NAC_MASK = 0x40,
1052 HDMI_I2CM_CTLINT_ARB_POL = 0x8,
1053 HDMI_I2CM_CTLINT_ARB_MASK = 0x4,
1035}; 1054};
1036 1055
1037#endif /* __DW_HDMI_H__ */ 1056#endif /* __DW_HDMI_H__ */