Lines Matching +full:i2c +full:- +full:hdmi
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd.
6 * Author: Algea Cao <algea.cao@rock-chips.com>
10 #include <linux/hdmi.h>
11 #include <linux/i2c.h>
28 #include <sound/hdmi-codec.h>
30 #include "dw-hdmi-qp.h"
55 struct dw_hdmi_qp_i2c *i2c; member
65 static void dw_hdmi_qp_write(struct dw_hdmi_qp *hdmi, unsigned int val, in dw_hdmi_qp_write() argument
68 regmap_write(hdmi->regm, offset, val); in dw_hdmi_qp_write()
71 static unsigned int dw_hdmi_qp_read(struct dw_hdmi_qp *hdmi, int offset) in dw_hdmi_qp_read() argument
75 regmap_read(hdmi->regm, offset, &val); in dw_hdmi_qp_read()
80 static void dw_hdmi_qp_mod(struct dw_hdmi_qp *hdmi, unsigned int data, in dw_hdmi_qp_mod() argument
83 regmap_update_bits(hdmi->regm, reg, mask, data); in dw_hdmi_qp_mod()
86 static int dw_hdmi_qp_i2c_read(struct dw_hdmi_qp *hdmi, in dw_hdmi_qp_i2c_read() argument
89 struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; in dw_hdmi_qp_i2c_read() local
92 if (!i2c->is_regaddr) { in dw_hdmi_qp_i2c_read()
93 dev_dbg(hdmi->dev, "set read register address to 0\n"); in dw_hdmi_qp_i2c_read()
94 i2c->slave_reg = 0x00; in dw_hdmi_qp_i2c_read()
95 i2c->is_regaddr = true; in dw_hdmi_qp_i2c_read()
98 while (length--) { in dw_hdmi_qp_i2c_read()
99 reinit_completion(&i2c->cmp); in dw_hdmi_qp_i2c_read()
101 dw_hdmi_qp_mod(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR, in dw_hdmi_qp_i2c_read()
104 if (i2c->is_segment) in dw_hdmi_qp_i2c_read()
105 dw_hdmi_qp_mod(hdmi, I2CM_EXT_READ, I2CM_WR_MASK, in dw_hdmi_qp_i2c_read()
108 dw_hdmi_qp_mod(hdmi, I2CM_FM_READ, I2CM_WR_MASK, in dw_hdmi_qp_i2c_read()
111 stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); in dw_hdmi_qp_i2c_read()
113 dev_err(hdmi->dev, "i2c read timed out\n"); in dw_hdmi_qp_i2c_read()
114 dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0); in dw_hdmi_qp_i2c_read()
115 return -EAGAIN; in dw_hdmi_qp_i2c_read()
119 if (i2c->stat & I2CM_NACK_RCVD_IRQ) { in dw_hdmi_qp_i2c_read()
120 dev_err(hdmi->dev, "i2c read error\n"); in dw_hdmi_qp_i2c_read()
121 dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0); in dw_hdmi_qp_i2c_read()
122 return -EIO; in dw_hdmi_qp_i2c_read()
125 *buf++ = dw_hdmi_qp_read(hdmi, I2CM_INTERFACE_RDDATA_0_3) & 0xff; in dw_hdmi_qp_i2c_read()
126 dw_hdmi_qp_mod(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); in dw_hdmi_qp_i2c_read()
129 i2c->is_segment = false; in dw_hdmi_qp_i2c_read()
134 static int dw_hdmi_qp_i2c_write(struct dw_hdmi_qp *hdmi, in dw_hdmi_qp_i2c_write() argument
137 struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; in dw_hdmi_qp_i2c_write() local
140 if (!i2c->is_regaddr) { in dw_hdmi_qp_i2c_write()
142 i2c->slave_reg = buf[0]; in dw_hdmi_qp_i2c_write()
143 length--; in dw_hdmi_qp_i2c_write()
145 i2c->is_regaddr = true; in dw_hdmi_qp_i2c_write()
148 while (length--) { in dw_hdmi_qp_i2c_write()
149 reinit_completion(&i2c->cmp); in dw_hdmi_qp_i2c_write()
151 dw_hdmi_qp_write(hdmi, *buf++, I2CM_INTERFACE_WRDATA_0_3); in dw_hdmi_qp_i2c_write()
152 dw_hdmi_qp_mod(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR, in dw_hdmi_qp_i2c_write()
154 dw_hdmi_qp_mod(hdmi, I2CM_FM_WRITE, I2CM_WR_MASK, in dw_hdmi_qp_i2c_write()
157 stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); in dw_hdmi_qp_i2c_write()
159 dev_err(hdmi->dev, "i2c write time out!\n"); in dw_hdmi_qp_i2c_write()
160 dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0); in dw_hdmi_qp_i2c_write()
161 return -EAGAIN; in dw_hdmi_qp_i2c_write()
165 if (i2c->stat & I2CM_NACK_RCVD_IRQ) { in dw_hdmi_qp_i2c_write()
166 dev_err(hdmi->dev, "i2c write nack!\n"); in dw_hdmi_qp_i2c_write()
167 dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0); in dw_hdmi_qp_i2c_write()
168 return -EIO; in dw_hdmi_qp_i2c_write()
171 dw_hdmi_qp_mod(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); in dw_hdmi_qp_i2c_write()
180 struct dw_hdmi_qp *hdmi = i2c_get_adapdata(adap); in dw_hdmi_qp_i2c_xfer() local
181 struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; in dw_hdmi_qp_i2c_xfer() local
187 * The internal I2C controller does not support the multi-byte in dw_hdmi_qp_i2c_xfer()
190 * unsupported I2C operations. in dw_hdmi_qp_i2c_xfer()
192 return -EOPNOTSUPP; in dw_hdmi_qp_i2c_xfer()
196 dev_err(hdmi->dev, in dw_hdmi_qp_i2c_xfer()
199 return -EOPNOTSUPP; in dw_hdmi_qp_i2c_xfer()
203 guard(mutex)(&i2c->lock); in dw_hdmi_qp_i2c_xfer()
206 dw_hdmi_qp_mod(hdmi, I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, in dw_hdmi_qp_i2c_xfer()
210 /* Set slave device address taken from the first I2C message */ in dw_hdmi_qp_i2c_xfer()
214 dw_hdmi_qp_mod(hdmi, addr << 5, I2CM_SLVADDR, I2CM_INTERFACE_CONTROL0); in dw_hdmi_qp_i2c_xfer()
217 i2c->is_regaddr = false; in dw_hdmi_qp_i2c_xfer()
219 /* Set segment pointer for I2C extended read mode operation */ in dw_hdmi_qp_i2c_xfer()
220 i2c->is_segment = false; in dw_hdmi_qp_i2c_xfer()
224 i2c->is_segment = true; in dw_hdmi_qp_i2c_xfer()
225 dw_hdmi_qp_mod(hdmi, DDC_SEGMENT_ADDR, I2CM_SEG_ADDR, in dw_hdmi_qp_i2c_xfer()
227 dw_hdmi_qp_mod(hdmi, *msgs[i].buf << 7, I2CM_SEG_PTR, in dw_hdmi_qp_i2c_xfer()
231 ret = dw_hdmi_qp_i2c_read(hdmi, msgs[i].buf, in dw_hdmi_qp_i2c_xfer()
234 ret = dw_hdmi_qp_i2c_write(hdmi, msgs[i].buf, in dw_hdmi_qp_i2c_xfer()
245 dw_hdmi_qp_mod(hdmi, 0, I2CM_OP_DONE_MASK_N | I2CM_NACK_RCVD_MASK_N, in dw_hdmi_qp_i2c_xfer()
261 static struct i2c_adapter *dw_hdmi_qp_i2c_adapter(struct dw_hdmi_qp *hdmi) in dw_hdmi_qp_i2c_adapter() argument
263 struct dw_hdmi_qp_i2c *i2c; in dw_hdmi_qp_i2c_adapter() local
267 i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL); in dw_hdmi_qp_i2c_adapter()
268 if (!i2c) in dw_hdmi_qp_i2c_adapter()
269 return ERR_PTR(-ENOMEM); in dw_hdmi_qp_i2c_adapter()
271 mutex_init(&i2c->lock); in dw_hdmi_qp_i2c_adapter()
272 init_completion(&i2c->cmp); in dw_hdmi_qp_i2c_adapter()
274 adap = &i2c->adap; in dw_hdmi_qp_i2c_adapter()
275 adap->owner = THIS_MODULE; in dw_hdmi_qp_i2c_adapter()
276 adap->dev.parent = hdmi->dev; in dw_hdmi_qp_i2c_adapter()
277 adap->algo = &dw_hdmi_qp_algorithm; in dw_hdmi_qp_i2c_adapter()
278 strscpy(adap->name, "DesignWare HDMI QP", sizeof(adap->name)); in dw_hdmi_qp_i2c_adapter()
280 i2c_set_adapdata(adap, hdmi); in dw_hdmi_qp_i2c_adapter()
282 ret = devm_i2c_add_adapter(hdmi->dev, adap); in dw_hdmi_qp_i2c_adapter()
284 dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name); in dw_hdmi_qp_i2c_adapter()
285 devm_kfree(hdmi->dev, i2c); in dw_hdmi_qp_i2c_adapter()
289 hdmi->i2c = i2c; in dw_hdmi_qp_i2c_adapter()
290 dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name); in dw_hdmi_qp_i2c_adapter()
295 static int dw_hdmi_qp_config_avi_infoframe(struct dw_hdmi_qp *hdmi, in dw_hdmi_qp_config_avi_infoframe() argument
301 dev_err(hdmi->dev, "failed to configure avi infoframe\n"); in dw_hdmi_qp_config_avi_infoframe()
302 return -EINVAL; in dw_hdmi_qp_config_avi_infoframe()
306 * DW HDMI QP IP uses a different byte format from standard AVI info in dw_hdmi_qp_config_avi_infoframe()
310 dw_hdmi_qp_write(hdmi, val, PKT_AVI_CONTENTS0); in dw_hdmi_qp_config_avi_infoframe()
321 dw_hdmi_qp_write(hdmi, val, PKT_AVI_CONTENTS1 + i * 4); in dw_hdmi_qp_config_avi_infoframe()
324 dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_FIELDRATE, PKTSCHED_PKT_CONFIG1); in dw_hdmi_qp_config_avi_infoframe()
326 dw_hdmi_qp_mod(hdmi, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, in dw_hdmi_qp_config_avi_infoframe()
332 static int dw_hdmi_qp_config_drm_infoframe(struct dw_hdmi_qp *hdmi, in dw_hdmi_qp_config_drm_infoframe() argument
338 dev_err(hdmi->dev, "failed to configure drm infoframe\n"); in dw_hdmi_qp_config_drm_infoframe()
339 return -EINVAL; in dw_hdmi_qp_config_drm_infoframe()
342 dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); in dw_hdmi_qp_config_drm_infoframe()
345 dw_hdmi_qp_write(hdmi, val, PKT_DRMI_CONTENTS0); in dw_hdmi_qp_config_drm_infoframe()
353 dw_hdmi_qp_write(hdmi, val, in dw_hdmi_qp_config_drm_infoframe()
357 dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_FIELDRATE, PKTSCHED_PKT_CONFIG1); in dw_hdmi_qp_config_drm_infoframe()
358 dw_hdmi_qp_mod(hdmi, PKTSCHED_DRMI_TX_EN, PKTSCHED_DRMI_TX_EN, in dw_hdmi_qp_config_drm_infoframe()
367 struct dw_hdmi_qp *hdmi = bridge->driver_private; in dw_hdmi_qp_bridge_atomic_enable() local
368 struct drm_atomic_state *state = old_state->base.state; in dw_hdmi_qp_bridge_atomic_enable()
373 connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); in dw_hdmi_qp_bridge_atomic_enable()
381 if (connector->display_info.is_hdmi) { in dw_hdmi_qp_bridge_atomic_enable()
382 dev_dbg(hdmi->dev, "%s mode=HDMI rate=%llu\n", in dw_hdmi_qp_bridge_atomic_enable()
383 __func__, conn_state->hdmi.tmds_char_rate); in dw_hdmi_qp_bridge_atomic_enable()
386 dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__); in dw_hdmi_qp_bridge_atomic_enable()
390 hdmi->phy.ops->init(hdmi, hdmi->phy.data); in dw_hdmi_qp_bridge_atomic_enable()
392 dw_hdmi_qp_mod(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); in dw_hdmi_qp_bridge_atomic_enable()
393 dw_hdmi_qp_mod(hdmi, op_mode, OPMODE_DVI, LINK_CONFIG0); in dw_hdmi_qp_bridge_atomic_enable()
401 struct dw_hdmi_qp *hdmi = bridge->driver_private; in dw_hdmi_qp_bridge_atomic_disable() local
403 hdmi->phy.ops->disable(hdmi, hdmi->phy.data); in dw_hdmi_qp_bridge_atomic_disable()
409 struct dw_hdmi_qp *hdmi = bridge->driver_private; in dw_hdmi_qp_bridge_detect() local
411 return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); in dw_hdmi_qp_bridge_detect()
418 struct dw_hdmi_qp *hdmi = bridge->driver_private; in dw_hdmi_qp_bridge_edid_read() local
421 drm_edid = drm_edid_read_ddc(connector, bridge->ddc); in dw_hdmi_qp_bridge_edid_read()
423 dev_dbg(hdmi->dev, "failed to get edid\n"); in dw_hdmi_qp_bridge_edid_read()
433 struct dw_hdmi_qp *hdmi = bridge->driver_private; in dw_hdmi_qp_bridge_tmds_char_rate_valid() local
436 dev_dbg(hdmi->dev, "Unsupported TMDS char rate: %lld\n", rate); in dw_hdmi_qp_bridge_tmds_char_rate_valid()
446 struct dw_hdmi_qp *hdmi = bridge->driver_private; in dw_hdmi_qp_bridge_clear_infoframe() local
450 dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, in dw_hdmi_qp_bridge_clear_infoframe()
455 dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); in dw_hdmi_qp_bridge_clear_infoframe()
459 dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type); in dw_hdmi_qp_bridge_clear_infoframe()
469 struct dw_hdmi_qp *hdmi = bridge->driver_private; in dw_hdmi_qp_bridge_write_infoframe() local
475 return dw_hdmi_qp_config_avi_infoframe(hdmi, buffer, len); in dw_hdmi_qp_bridge_write_infoframe()
478 return dw_hdmi_qp_config_drm_infoframe(hdmi, buffer, len); in dw_hdmi_qp_bridge_write_infoframe()
481 dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type); in dw_hdmi_qp_bridge_write_infoframe()
501 struct dw_hdmi_qp *hdmi = dev_id; in dw_hdmi_qp_main_hardirq() local
502 struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; in dw_hdmi_qp_main_hardirq() local
505 stat = dw_hdmi_qp_read(hdmi, MAINUNIT_1_INT_STATUS); in dw_hdmi_qp_main_hardirq()
507 i2c->stat = stat & (I2CM_OP_DONE_IRQ | I2CM_READ_REQUEST_IRQ | in dw_hdmi_qp_main_hardirq()
510 if (i2c->stat) { in dw_hdmi_qp_main_hardirq()
511 dw_hdmi_qp_write(hdmi, i2c->stat, MAINUNIT_1_INT_CLEAR); in dw_hdmi_qp_main_hardirq()
512 complete(&i2c->cmp); in dw_hdmi_qp_main_hardirq()
528 static void dw_hdmi_qp_init_hw(struct dw_hdmi_qp *hdmi) in dw_hdmi_qp_init_hw() argument
530 dw_hdmi_qp_write(hdmi, 0, MAINUNIT_0_INT_MASK_N); in dw_hdmi_qp_init_hw()
531 dw_hdmi_qp_write(hdmi, 0, MAINUNIT_1_INT_MASK_N); in dw_hdmi_qp_init_hw()
532 dw_hdmi_qp_write(hdmi, 428571429, TIMER_BASE_CONFIG0); in dw_hdmi_qp_init_hw()
535 dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0); in dw_hdmi_qp_init_hw()
537 dw_hdmi_qp_write(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0); in dw_hdmi_qp_init_hw()
539 dw_hdmi_qp_mod(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0); in dw_hdmi_qp_init_hw()
542 dw_hdmi_qp_write(hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR, in dw_hdmi_qp_init_hw()
545 if (hdmi->phy.ops->setup_hpd) in dw_hdmi_qp_init_hw()
546 hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); in dw_hdmi_qp_init_hw()
553 struct device *dev = &pdev->dev; in dw_hdmi_qp_bind()
554 struct dw_hdmi_qp *hdmi; in dw_hdmi_qp_bind() local
558 if (!plat_data->phy_ops || !plat_data->phy_ops->init || in dw_hdmi_qp_bind()
559 !plat_data->phy_ops->disable || !plat_data->phy_ops->read_hpd) { in dw_hdmi_qp_bind()
561 return ERR_PTR(-ENODEV); in dw_hdmi_qp_bind()
564 hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); in dw_hdmi_qp_bind()
565 if (!hdmi) in dw_hdmi_qp_bind()
566 return ERR_PTR(-ENOMEM); in dw_hdmi_qp_bind()
568 hdmi->dev = dev; in dw_hdmi_qp_bind()
574 hdmi->regm = devm_regmap_init_mmio(dev, regs, &dw_hdmi_qp_regmap_config); in dw_hdmi_qp_bind()
575 if (IS_ERR(hdmi->regm)) { in dw_hdmi_qp_bind()
577 return ERR_CAST(hdmi->regm); in dw_hdmi_qp_bind()
580 hdmi->phy.ops = plat_data->phy_ops; in dw_hdmi_qp_bind()
581 hdmi->phy.data = plat_data->phy_data; in dw_hdmi_qp_bind()
583 dw_hdmi_qp_init_hw(hdmi); in dw_hdmi_qp_bind()
585 ret = devm_request_threaded_irq(dev, plat_data->main_irq, in dw_hdmi_qp_bind()
587 IRQF_SHARED, dev_name(dev), hdmi); in dw_hdmi_qp_bind()
591 hdmi->bridge.driver_private = hdmi; in dw_hdmi_qp_bind()
592 hdmi->bridge.funcs = &dw_hdmi_qp_bridge_funcs; in dw_hdmi_qp_bind()
593 hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | in dw_hdmi_qp_bind()
597 hdmi->bridge.of_node = pdev->dev.of_node; in dw_hdmi_qp_bind()
598 hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; in dw_hdmi_qp_bind()
599 hdmi->bridge.vendor = "Synopsys"; in dw_hdmi_qp_bind()
600 hdmi->bridge.product = "DW HDMI QP TX"; in dw_hdmi_qp_bind()
602 hdmi->bridge.ddc = dw_hdmi_qp_i2c_adapter(hdmi); in dw_hdmi_qp_bind()
603 if (IS_ERR(hdmi->bridge.ddc)) in dw_hdmi_qp_bind()
604 return ERR_CAST(hdmi->bridge.ddc); in dw_hdmi_qp_bind()
606 ret = devm_drm_bridge_add(dev, &hdmi->bridge); in dw_hdmi_qp_bind()
610 ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, in dw_hdmi_qp_bind()
615 return hdmi; in dw_hdmi_qp_bind()
619 void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi) in dw_hdmi_qp_resume() argument
621 dw_hdmi_qp_init_hw(hdmi); in dw_hdmi_qp_resume()
625 MODULE_AUTHOR("Algea Cao <algea.cao@rock-chips.com>");
627 MODULE_DESCRIPTION("DW HDMI QP transmitter library");