@@ -9,6 +9,8 @@
#include <linux/property.h>
#include <linux/slab.h>
+#include <media/cec.h>
+
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
@@ -497,6 +499,80 @@ static const struct drm_connector_hdmi_audio_funcs drm_bridge_connector_hdmi_aud
.mute_stream = drm_bridge_connector_audio_mute_stream,
};
+static int drm_bridge_connector_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct drm_connector *connector = cec_get_drvdata(adap);
+ struct drm_bridge_connector *bridge_connector =
+ to_drm_bridge_connector(connector);
+ struct drm_bridge *bridge;
+
+ bridge = bridge_connector->bridge_hdmi;
+
+ return bridge->funcs->hdmi_cec_adap_enable(bridge, enable);
+}
+
+static int drm_bridge_connector_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct drm_connector *connector = cec_get_drvdata(adap);
+ struct drm_bridge_connector *bridge_connector =
+ to_drm_bridge_connector(connector);
+ struct drm_bridge *bridge;
+
+ bridge = bridge_connector->bridge_hdmi;
+
+ return bridge->funcs->hdmi_cec_adap_log_addr(bridge, logical_addr);
+}
+
+static int drm_bridge_connector_cec_adap_transmit(struct cec_adapter *adap,
+ u8 attempts,
+ u32 signal_free_time,
+ struct cec_msg *msg)
+{
+ struct drm_connector *connector = cec_get_drvdata(adap);
+ struct drm_bridge_connector *bridge_connector =
+ to_drm_bridge_connector(connector);
+ struct drm_bridge *bridge;
+
+ bridge = bridge_connector->bridge_hdmi;
+
+ return bridge->funcs->hdmi_cec_adap_transmit(bridge, attempts,
+ signal_free_time,
+ msg);
+}
+
+static const struct cec_adap_ops drm_bridge_connector_cec_adap_ops = {
+ .adap_enable = drm_bridge_connector_cec_adap_enable,
+ .adap_log_addr = drm_bridge_connector_cec_adap_log_addr,
+ .adap_transmit = drm_bridge_connector_cec_adap_transmit,
+};
+
+static int drm_bridge_connector_hdmi_cec_init(struct drm_connector *connector)
+{
+ struct drm_bridge_connector *bridge_connector =
+ to_drm_bridge_connector(connector);
+ struct drm_bridge *bridge;
+
+ bridge = bridge_connector->bridge_hdmi;
+
+ drm_bridge_cec_adapter_set(bridge, connector->cec.adapter);
+
+ if (!bridge->funcs->hdmi_cec_adap_init)
+ return 0;
+
+ return bridge->funcs->hdmi_cec_adap_init(connector, bridge);
+}
+
+static void drm_bridge_connector_hdmi_cec_uninit(struct drm_connector *connector)
+{
+ struct drm_bridge_connector *bridge_connector =
+ to_drm_bridge_connector(connector);
+ struct drm_bridge *bridge;
+
+ bridge = bridge_connector->bridge_hdmi;
+
+ drm_bridge_cec_adapter_set(bridge, NULL);
+}
+
/* -----------------------------------------------------------------------------
* Bridge Connector Initialisation
*/
@@ -633,6 +709,25 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (ret)
return ERR_PTR(ret);
}
+
+ if (bridge->hdmi_cec_adapter_name) {
+ u8 num_las = bridge->hdmi_cec_available_las ? : CEC_MAX_LOG_ADDRS;
+
+ if (!bridge->funcs->hdmi_cec_adap_enable ||
+ !bridge->funcs->hdmi_cec_adap_log_addr ||
+ !bridge->funcs->hdmi_cec_adap_transmit)
+ return ERR_PTR(-EINVAL);
+
+ ret = drm_connector_hdmi_cec_adapter_register(connector,
+ &drm_bridge_connector_cec_adap_ops,
+ bridge->hdmi_cec_adapter_name,
+ num_las,
+ drm_bridge_connector_hdmi_cec_init,
+ drm_bridge_connector_hdmi_cec_uninit,
+ bridge->hdmi_dev);
+ if (ret)
+ return ERR_PTR(ret);
+ }
} else {
ret = drmm_connector_init(drm, connector,
&drm_bridge_connector_funcs,
@@ -33,6 +33,7 @@
#include <drm/drm_modes.h>
struct cec_adapter;
+struct cec_msg;
struct device_node;
struct drm_bridge;
@@ -730,6 +731,20 @@ struct drm_bridge_funcs {
struct drm_bridge *bridge,
bool enable, int direction);
+ int (*hdmi_cec_adap_init)(struct drm_connector *connector,
+ struct drm_bridge *bridge);
+
+ int (*hdmi_cec_adap_enable)(struct drm_bridge *bridge,
+ bool enable);
+
+ int (*hdmi_cec_adap_log_addr)(struct drm_bridge *bridge,
+ u8 logical_addr);
+
+ int (*hdmi_cec_adap_transmit)(struct drm_bridge *bridge,
+ u8 attempts,
+ u32 signal_free_time,
+ struct cec_msg *msg);
+
/**
* @debugfs_init:
*
@@ -925,6 +940,16 @@ struct drm_bridge {
*/
bool hdmi_cec_notifier;
+ /**
+ * @hdmi_cec_adapter_name: the name of the adapter to register
+ */
+ const char *hdmi_cec_adapter_name;
+
+ /**
+ * @hdmi_cec_available_las: number of logical addresses, CEC_MAX_LOG_ADDRS if unset
+ */
+ u8 hdmi_cec_available_las;
+
/** private: */
/**
* @hpd_mutex: Protects the @hpd_cb and @hpd_data fields.
Implement necessary glue code to let DRM bridge drivers to implement CEC adapters support. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> --- drivers/gpu/drm/display/drm_bridge_connector.c | 95 ++++++++++++++++++++++++++ include/drm/drm_bridge.h | 25 +++++++ 2 files changed, 120 insertions(+)