aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Verkuil <hans.verkuil@cisco.com>2017-07-11 02:30:44 -0400
committerMaxime Ripard <maxime.ripard@free-electrons.com>2017-07-18 12:27:50 -0400
commit998140d26723bcddef5857e39077898b0d1bdb8f (patch)
treeda37b2a50d4946b3e0d3a5a87233c522414e395c
parent9181b5bbdff2739efa196c204cb307cc3cab210a (diff)
sun4i_hdmi: add CEC support
Add HDMI CEC support to the Allwinner A10 SoC. This SoC uses a poor-man's CEC implementation by polling the CEC pin. It is using the CEC_PIN core implementation for such devices to do the heavy lifting. It just provides the callbacks to read/drive the CEC pin. Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Tested-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
-rw-r--r--drivers/gpu/drm/sun4i/Kconfig9
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi.h8
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c55
3 files changed, 71 insertions, 1 deletions
diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index 5cc116692913..06f05302ee75 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -22,6 +22,15 @@ config DRM_SUN4I_HDMI
22 Choose this option if you have an Allwinner SoC with an HDMI 22 Choose this option if you have an Allwinner SoC with an HDMI
23 controller. 23 controller.
24 24
25config DRM_SUN4I_HDMI_CEC
26 bool "Allwinner A10 HDMI CEC Support"
27 depends on DRM_SUN4I_HDMI
28 select CEC_CORE
29 depends on CEC_PIN
30 help
31 Choose this option if you have an Allwinner SoC with an HDMI
32 controller and want to use CEC.
33
25config DRM_SUN4I_BACKEND 34config DRM_SUN4I_BACKEND
26 tristate "Support for Allwinner A10 Display Engine Backend" 35 tristate "Support for Allwinner A10 Display Engine Backend"
27 default DRM_SUN4I 36 default DRM_SUN4I
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi.h b/drivers/gpu/drm/sun4i/sun4i_hdmi.h
index 0957ff2076ac..1457750988da 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi.h
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi.h
@@ -15,6 +15,8 @@
15#include <drm/drm_connector.h> 15#include <drm/drm_connector.h>
16#include <drm/drm_encoder.h> 16#include <drm/drm_encoder.h>
17 17
18#include <media/cec.h>
19
18#define SUN4I_HDMI_CTRL_REG 0x004 20#define SUN4I_HDMI_CTRL_REG 0x004
19#define SUN4I_HDMI_CTRL_ENABLE BIT(31) 21#define SUN4I_HDMI_CTRL_ENABLE BIT(31)
20 22
@@ -86,6 +88,11 @@
86#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_MASK BIT(21) 88#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_MASK BIT(21)
87#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_SHIFT 21 89#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_SHIFT 21
88 90
91#define SUN4I_HDMI_CEC 0x214
92#define SUN4I_HDMI_CEC_ENABLE BIT(11)
93#define SUN4I_HDMI_CEC_TX BIT(9)
94#define SUN4I_HDMI_CEC_RX BIT(8)
95
89#define SUN4I_HDMI_PKT_CTRL_REG(n) (0x2f0 + (4 * (n))) 96#define SUN4I_HDMI_PKT_CTRL_REG(n) (0x2f0 + (4 * (n)))
90#define SUN4I_HDMI_PKT_CTRL_TYPE(n, t) ((t) << (((n) % 4) * 4)) 97#define SUN4I_HDMI_PKT_CTRL_TYPE(n, t) ((t) << (((n) % 4) * 4))
91 98
@@ -172,6 +179,7 @@ struct sun4i_hdmi {
172 struct sun4i_drv *drv; 179 struct sun4i_drv *drv;
173 180
174 bool hdmi_monitor; 181 bool hdmi_monitor;
182 struct cec_adapter *cec_adap;
175}; 183};
176 184
177int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *clk); 185int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *clk);
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
index b74607feb35c..863a51618819 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -197,6 +197,7 @@ static int sun4i_hdmi_get_modes(struct drm_connector *connector)
197 hdmi->hdmi_monitor ? "an HDMI" : "a DVI"); 197 hdmi->hdmi_monitor ? "an HDMI" : "a DVI");
198 198
199 drm_mode_connector_update_edid_property(connector, edid); 199 drm_mode_connector_update_edid_property(connector, edid);
200 cec_s_phys_addr_from_edid(hdmi->cec_adap, edid);
200 ret = drm_add_edid_modes(connector, edid); 201 ret = drm_add_edid_modes(connector, edid);
201 kfree(edid); 202 kfree(edid);
202 203
@@ -215,8 +216,10 @@ sun4i_hdmi_connector_detect(struct drm_connector *connector, bool force)
215 216
216 if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_HPD_REG, reg, 217 if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_HPD_REG, reg,
217 reg & SUN4I_HDMI_HPD_HIGH, 218 reg & SUN4I_HDMI_HPD_HIGH,
218 0, 500000)) 219 0, 500000)) {
220 cec_phys_addr_invalidate(hdmi->cec_adap);
219 return connector_status_disconnected; 221 return connector_status_disconnected;
222 }
220 223
221 return connector_status_connected; 224 return connector_status_connected;
222} 225}
@@ -231,6 +234,40 @@ static const struct drm_connector_funcs sun4i_hdmi_connector_funcs = {
231 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 234 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
232}; 235};
233 236
237#ifdef CONFIG_DRM_SUN4I_HDMI_CEC
238static bool sun4i_hdmi_cec_pin_read(struct cec_adapter *adap)
239{
240 struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
241
242 return readl(hdmi->base + SUN4I_HDMI_CEC) & SUN4I_HDMI_CEC_RX;
243}
244
245static void sun4i_hdmi_cec_pin_low(struct cec_adapter *adap)
246{
247 struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
248
249 /* Start driving the CEC pin low */
250 writel(SUN4I_HDMI_CEC_ENABLE, hdmi->base + SUN4I_HDMI_CEC);
251}
252
253static void sun4i_hdmi_cec_pin_high(struct cec_adapter *adap)
254{
255 struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
256
257 /*
258 * Stop driving the CEC pin, the pull up will take over
259 * unless another CEC device is driving the pin low.
260 */
261 writel(0, hdmi->base + SUN4I_HDMI_CEC);
262}
263
264static const struct cec_pin_ops sun4i_hdmi_cec_pin_ops = {
265 .read = sun4i_hdmi_cec_pin_read,
266 .low = sun4i_hdmi_cec_pin_low,
267 .high = sun4i_hdmi_cec_pin_high,
268};
269#endif
270
234static int sun4i_hdmi_bind(struct device *dev, struct device *master, 271static int sun4i_hdmi_bind(struct device *dev, struct device *master,
235 void *data) 272 void *data)
236{ 273{
@@ -348,6 +385,17 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master,
348 goto err_del_i2c_adapter; 385 goto err_del_i2c_adapter;
349 } 386 }
350 387
388#ifdef CONFIG_DRM_SUN4I_HDMI_CEC
389 hdmi->cec_adap = cec_pin_allocate_adapter(&sun4i_hdmi_cec_pin_ops,
390 hdmi, "sun4i", CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
391 CEC_CAP_PASSTHROUGH | CEC_CAP_RC);
392 ret = PTR_ERR_OR_ZERO(hdmi->cec_adap);
393 if (ret < 0)
394 goto err_cleanup_connector;
395 writel(readl(hdmi->base + SUN4I_HDMI_CEC) & ~SUN4I_HDMI_CEC_TX,
396 hdmi->base + SUN4I_HDMI_CEC);
397#endif
398
351 drm_connector_helper_add(&hdmi->connector, 399 drm_connector_helper_add(&hdmi->connector,
352 &sun4i_hdmi_connector_helper_funcs); 400 &sun4i_hdmi_connector_helper_funcs);
353 ret = drm_connector_init(drm, &hdmi->connector, 401 ret = drm_connector_init(drm, &hdmi->connector,
@@ -363,11 +411,15 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master,
363 hdmi->connector.polled = DRM_CONNECTOR_POLL_CONNECT | 411 hdmi->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
364 DRM_CONNECTOR_POLL_DISCONNECT; 412 DRM_CONNECTOR_POLL_DISCONNECT;
365 413
414 ret = cec_register_adapter(hdmi->cec_adap, dev);
415 if (ret < 0)
416 goto err_cleanup_connector;
366 drm_mode_connector_attach_encoder(&hdmi->connector, &hdmi->encoder); 417 drm_mode_connector_attach_encoder(&hdmi->connector, &hdmi->encoder);
367 418
368 return 0; 419 return 0;
369 420
370err_cleanup_connector: 421err_cleanup_connector:
422 cec_delete_adapter(hdmi->cec_adap);
371 drm_encoder_cleanup(&hdmi->encoder); 423 drm_encoder_cleanup(&hdmi->encoder);
372err_del_i2c_adapter: 424err_del_i2c_adapter:
373 i2c_del_adapter(hdmi->i2c); 425 i2c_del_adapter(hdmi->i2c);
@@ -379,6 +431,7 @@ static void sun4i_hdmi_unbind(struct device *dev, struct device *master,
379{ 431{
380 struct sun4i_hdmi *hdmi = dev_get_drvdata(dev); 432 struct sun4i_hdmi *hdmi = dev_get_drvdata(dev);
381 433
434 cec_unregister_adapter(hdmi->cec_adap);
382 drm_connector_cleanup(&hdmi->connector); 435 drm_connector_cleanup(&hdmi->connector);
383 drm_encoder_cleanup(&hdmi->encoder); 436 drm_encoder_cleanup(&hdmi->encoder);
384 i2c_del_adapter(hdmi->i2c); 437 i2c_del_adapter(hdmi->i2c);