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>
This commit is contained in:
parent
9181b5bbdf
commit
998140d267
@ -22,6 +22,15 @@ config DRM_SUN4I_HDMI
|
||||
Choose this option if you have an Allwinner SoC with an HDMI
|
||||
controller.
|
||||
|
||||
config DRM_SUN4I_HDMI_CEC
|
||||
bool "Allwinner A10 HDMI CEC Support"
|
||||
depends on DRM_SUN4I_HDMI
|
||||
select CEC_CORE
|
||||
depends on CEC_PIN
|
||||
help
|
||||
Choose this option if you have an Allwinner SoC with an HDMI
|
||||
controller and want to use CEC.
|
||||
|
||||
config DRM_SUN4I_BACKEND
|
||||
tristate "Support for Allwinner A10 Display Engine Backend"
|
||||
default DRM_SUN4I
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
|
||||
#include <media/cec.h>
|
||||
|
||||
#define SUN4I_HDMI_CTRL_REG 0x004
|
||||
#define SUN4I_HDMI_CTRL_ENABLE BIT(31)
|
||||
|
||||
@ -86,6 +88,11 @@
|
||||
#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_MASK BIT(21)
|
||||
#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_SHIFT 21
|
||||
|
||||
#define SUN4I_HDMI_CEC 0x214
|
||||
#define SUN4I_HDMI_CEC_ENABLE BIT(11)
|
||||
#define SUN4I_HDMI_CEC_TX BIT(9)
|
||||
#define SUN4I_HDMI_CEC_RX BIT(8)
|
||||
|
||||
#define SUN4I_HDMI_PKT_CTRL_REG(n) (0x2f0 + (4 * (n)))
|
||||
#define SUN4I_HDMI_PKT_CTRL_TYPE(n, t) ((t) << (((n) % 4) * 4))
|
||||
|
||||
@ -172,6 +179,7 @@ struct sun4i_hdmi {
|
||||
struct sun4i_drv *drv;
|
||||
|
||||
bool hdmi_monitor;
|
||||
struct cec_adapter *cec_adap;
|
||||
};
|
||||
|
||||
int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *clk);
|
||||
|
@ -197,6 +197,7 @@ static int sun4i_hdmi_get_modes(struct drm_connector *connector)
|
||||
hdmi->hdmi_monitor ? "an HDMI" : "a DVI");
|
||||
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
cec_s_phys_addr_from_edid(hdmi->cec_adap, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
|
||||
@ -215,8 +216,10 @@ sun4i_hdmi_connector_detect(struct drm_connector *connector, bool force)
|
||||
|
||||
if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_HPD_REG, reg,
|
||||
reg & SUN4I_HDMI_HPD_HIGH,
|
||||
0, 500000))
|
||||
0, 500000)) {
|
||||
cec_phys_addr_invalidate(hdmi->cec_adap);
|
||||
return connector_status_disconnected;
|
||||
}
|
||||
|
||||
return connector_status_connected;
|
||||
}
|
||||
@ -231,6 +234,40 @@ static const struct drm_connector_funcs sun4i_hdmi_connector_funcs = {
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DRM_SUN4I_HDMI_CEC
|
||||
static bool sun4i_hdmi_cec_pin_read(struct cec_adapter *adap)
|
||||
{
|
||||
struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
|
||||
|
||||
return readl(hdmi->base + SUN4I_HDMI_CEC) & SUN4I_HDMI_CEC_RX;
|
||||
}
|
||||
|
||||
static void sun4i_hdmi_cec_pin_low(struct cec_adapter *adap)
|
||||
{
|
||||
struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
|
||||
|
||||
/* Start driving the CEC pin low */
|
||||
writel(SUN4I_HDMI_CEC_ENABLE, hdmi->base + SUN4I_HDMI_CEC);
|
||||
}
|
||||
|
||||
static void sun4i_hdmi_cec_pin_high(struct cec_adapter *adap)
|
||||
{
|
||||
struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
|
||||
|
||||
/*
|
||||
* Stop driving the CEC pin, the pull up will take over
|
||||
* unless another CEC device is driving the pin low.
|
||||
*/
|
||||
writel(0, hdmi->base + SUN4I_HDMI_CEC);
|
||||
}
|
||||
|
||||
static const struct cec_pin_ops sun4i_hdmi_cec_pin_ops = {
|
||||
.read = sun4i_hdmi_cec_pin_read,
|
||||
.low = sun4i_hdmi_cec_pin_low,
|
||||
.high = sun4i_hdmi_cec_pin_high,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int sun4i_hdmi_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
@ -348,6 +385,17 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master,
|
||||
goto err_del_i2c_adapter;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DRM_SUN4I_HDMI_CEC
|
||||
hdmi->cec_adap = cec_pin_allocate_adapter(&sun4i_hdmi_cec_pin_ops,
|
||||
hdmi, "sun4i", CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
|
||||
CEC_CAP_PASSTHROUGH | CEC_CAP_RC);
|
||||
ret = PTR_ERR_OR_ZERO(hdmi->cec_adap);
|
||||
if (ret < 0)
|
||||
goto err_cleanup_connector;
|
||||
writel(readl(hdmi->base + SUN4I_HDMI_CEC) & ~SUN4I_HDMI_CEC_TX,
|
||||
hdmi->base + SUN4I_HDMI_CEC);
|
||||
#endif
|
||||
|
||||
drm_connector_helper_add(&hdmi->connector,
|
||||
&sun4i_hdmi_connector_helper_funcs);
|
||||
ret = drm_connector_init(drm, &hdmi->connector,
|
||||
@ -363,11 +411,15 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master,
|
||||
hdmi->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
|
||||
DRM_CONNECTOR_POLL_DISCONNECT;
|
||||
|
||||
ret = cec_register_adapter(hdmi->cec_adap, dev);
|
||||
if (ret < 0)
|
||||
goto err_cleanup_connector;
|
||||
drm_mode_connector_attach_encoder(&hdmi->connector, &hdmi->encoder);
|
||||
|
||||
return 0;
|
||||
|
||||
err_cleanup_connector:
|
||||
cec_delete_adapter(hdmi->cec_adap);
|
||||
drm_encoder_cleanup(&hdmi->encoder);
|
||||
err_del_i2c_adapter:
|
||||
i2c_del_adapter(hdmi->i2c);
|
||||
@ -379,6 +431,7 @@ static void sun4i_hdmi_unbind(struct device *dev, struct device *master,
|
||||
{
|
||||
struct sun4i_hdmi *hdmi = dev_get_drvdata(dev);
|
||||
|
||||
cec_unregister_adapter(hdmi->cec_adap);
|
||||
drm_connector_cleanup(&hdmi->connector);
|
||||
drm_encoder_cleanup(&hdmi->encoder);
|
||||
i2c_del_adapter(hdmi->i2c);
|
||||
|
Loading…
Reference in New Issue
Block a user