diff mbox series

[v3,08/10] drm/display: bridge-connector: handle CEC adapters

Message ID 20250126-drm-hdmi-connector-cec-v3-8-5b5b2d4956da@linaro.org (mailing list archive)
State New, archived
Headers show
Series drm/display: generic HDMI CEC helpers | expand

Commit Message

Dmitry Baryshkov Jan. 26, 2025, 1:29 p.m. UTC
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 | 93 ++++++++++++++++++++++++++
 include/drm/drm_bridge.h                       | 21 ++++++
 2 files changed, 114 insertions(+)

Comments

Maxime Ripard Jan. 28, 2025, 4:14 p.m. UTC | #1
On Sun, Jan 26, 2025 at 03:29:13PM +0200, Dmitry Baryshkov wrote:
>  /* -----------------------------------------------------------------------------
>   * Bridge Connector Initialisation
>   */
> @@ -633,6 +711,21 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>  			if (ret)
>  				return ERR_PTR(ret);
>  		}
> +
> +		if (bridge->hdmi_cec_adapter_name) {
> +			if (!bridge->funcs->hdmi_cec_enable ||
> +			    !bridge->funcs->hdmi_cec_log_addr ||
> +			    !bridge->funcs->hdmi_cec_transmit)
> +				return ERR_PTR(-EINVAL);
> +
> +			ret = drm_connector_hdmi_cec_register(connector,
> +							      &drm_bridge_connector_hdmi_cec_ops,
> +							      bridge->hdmi_cec_adapter_name,
> +							      bridge->hdmi_cec_available_las,
> +							      bridge->hdmi_dev);
> +			if (ret)
> +				return ERR_PTR(ret);
> +		}

Maybe we can use a different bridge feature flag to trigger the CEC code
support instead?

Maxime
Dmitry Baryshkov Jan. 28, 2025, 11:44 p.m. UTC | #2
On Tue, Jan 28, 2025 at 05:14:06PM +0100, Maxime Ripard wrote:
> On Sun, Jan 26, 2025 at 03:29:13PM +0200, Dmitry Baryshkov wrote:
> >  /* -----------------------------------------------------------------------------
> >   * Bridge Connector Initialisation
> >   */
> > @@ -633,6 +711,21 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
> >  			if (ret)
> >  				return ERR_PTR(ret);
> >  		}
> > +
> > +		if (bridge->hdmi_cec_adapter_name) {
> > +			if (!bridge->funcs->hdmi_cec_enable ||
> > +			    !bridge->funcs->hdmi_cec_log_addr ||
> > +			    !bridge->funcs->hdmi_cec_transmit)
> > +				return ERR_PTR(-EINVAL);
> > +
> > +			ret = drm_connector_hdmi_cec_register(connector,
> > +							      &drm_bridge_connector_hdmi_cec_ops,
> > +							      bridge->hdmi_cec_adapter_name,
> > +							      bridge->hdmi_cec_available_las,
> > +							      bridge->hdmi_dev);
> > +			if (ret)
> > +				return ERR_PTR(ret);
> > +		}
> 
> Maybe we can use a different bridge feature flag to trigger the CEC code
> support instead?

it is possible, but what is the possible usecase? DP drivers should be
using DP_AUX CEC instead. And I think there are no other kinds of
bridges which implement CEC support. Meson driver does something strange
by registering CEC notifier from meson_encoder_hdmi. I think instead
this should be moved to the DW HDMI bridge itself
Neil Armstrong Jan. 29, 2025, 4:44 p.m. UTC | #3
On 29/01/2025 00:44, Dmitry Baryshkov wrote:
> On Tue, Jan 28, 2025 at 05:14:06PM +0100, Maxime Ripard wrote:
>> On Sun, Jan 26, 2025 at 03:29:13PM +0200, Dmitry Baryshkov wrote:
>>>   /* -----------------------------------------------------------------------------
>>>    * Bridge Connector Initialisation
>>>    */
>>> @@ -633,6 +711,21 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>>>   			if (ret)
>>>   				return ERR_PTR(ret);
>>>   		}
>>> +
>>> +		if (bridge->hdmi_cec_adapter_name) {
>>> +			if (!bridge->funcs->hdmi_cec_enable ||
>>> +			    !bridge->funcs->hdmi_cec_log_addr ||
>>> +			    !bridge->funcs->hdmi_cec_transmit)
>>> +				return ERR_PTR(-EINVAL);
>>> +
>>> +			ret = drm_connector_hdmi_cec_register(connector,
>>> +							      &drm_bridge_connector_hdmi_cec_ops,
>>> +							      bridge->hdmi_cec_adapter_name,
>>> +							      bridge->hdmi_cec_available_las,
>>> +							      bridge->hdmi_dev);
>>> +			if (ret)
>>> +				return ERR_PTR(ret);
>>> +		}
>>
>> Maybe we can use a different bridge feature flag to trigger the CEC code
>> support instead?
> 
> it is possible, but what is the possible usecase? DP drivers should be
> using DP_AUX CEC instead. And I think there are no other kinds of
> bridges which implement CEC support. Meson driver does something strange
> by registering CEC notifier from meson_encoder_hdmi. I think instead
> this should be moved to the DW HDMI bridge itself
> 

It was done before bridge_connector has any support for CEC to keep the
functionnality, I'll be happy to switch to this.

Neil
Dmitry Baryshkov Jan. 29, 2025, 6:32 p.m. UTC | #4
On Wed, Jan 29, 2025 at 05:44:31PM +0100, Neil Armstrong wrote:
> On 29/01/2025 00:44, Dmitry Baryshkov wrote:
> > On Tue, Jan 28, 2025 at 05:14:06PM +0100, Maxime Ripard wrote:
> > > On Sun, Jan 26, 2025 at 03:29:13PM +0200, Dmitry Baryshkov wrote:
> > > >   /* -----------------------------------------------------------------------------
> > > >    * Bridge Connector Initialisation
> > > >    */
> > > > @@ -633,6 +711,21 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
> > > >   			if (ret)
> > > >   				return ERR_PTR(ret);
> > > >   		}
> > > > +
> > > > +		if (bridge->hdmi_cec_adapter_name) {
> > > > +			if (!bridge->funcs->hdmi_cec_enable ||
> > > > +			    !bridge->funcs->hdmi_cec_log_addr ||
> > > > +			    !bridge->funcs->hdmi_cec_transmit)
> > > > +				return ERR_PTR(-EINVAL);
> > > > +
> > > > +			ret = drm_connector_hdmi_cec_register(connector,
> > > > +							      &drm_bridge_connector_hdmi_cec_ops,
> > > > +							      bridge->hdmi_cec_adapter_name,
> > > > +							      bridge->hdmi_cec_available_las,
> > > > +							      bridge->hdmi_dev);
> > > > +			if (ret)
> > > > +				return ERR_PTR(ret);
> > > > +		}
> > > 
> > > Maybe we can use a different bridge feature flag to trigger the CEC code
> > > support instead?
> > 
> > it is possible, but what is the possible usecase? DP drivers should be
> > using DP_AUX CEC instead. And I think there are no other kinds of
> > bridges which implement CEC support. Meson driver does something strange
> > by registering CEC notifier from meson_encoder_hdmi. I think instead
> > this should be moved to the DW HDMI bridge itself
> 
> It was done before bridge_connector has any support for CEC to keep the
> functionnality, I'll be happy to switch to this.

Well... Converting dw-hdmi to use all HDMI API is going to be
interesting anyway:

- iMX8, iMX6 attach with flags=0, so dw-hdmi creates connector (with all
  its fanciness).
- Ingenic and RCAR-DU use simple drm_bridge_connector, so they will just
  get all new features.
- Meson uses drm_bridge_connector + additional bits on top of it (like
  CEC, max_bpc, HDR).

Either we end up with a monstruous commit that reworks dw-hdmi _and_
drop extra bits from meson driver at the same time or we need some
well-thought plan. For example, first make some of the features of Meson
optional and dependent on their generic counterparts being registered by
drm_bridge_connector, then convert dw-hdmi _and_ use
drm_bridge_connector internally, finally drop extra meson features now
consumed by the core.
diff mbox series

Patch

diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 9f234bc647d5c0880d4c42aea130074b7fa54573..5b77fd59d79abddd419e611a7868b001857ccb37 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -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,82 @@  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_hdmi_cec_enable(struct drm_connector *connector, bool enable)
+{
+	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_enable(bridge, enable);
+}
+
+static int drm_bridge_connector_hdmi_cec_log_addr(struct drm_connector *connector, u8 logical_addr)
+{
+	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_log_addr(bridge, logical_addr);
+}
+
+static int drm_bridge_connector_hdmi_cec_transmit(struct drm_connector *connector,
+						  u8 attempts,
+						  u32 signal_free_time,
+						  struct cec_msg *msg)
+{
+	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_transmit(bridge, attempts,
+						signal_free_time,
+						msg);
+}
+
+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_data_set(bridge, connector);
+
+	if (!bridge->funcs->hdmi_cec_init)
+		return 0;
+
+	return bridge->funcs->hdmi_cec_init(connector, bridge);
+}
+
+static void drm_bridge_connector_hdmi_cec_unregister(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_data_set(bridge, NULL);
+
+	drm_connector_hdmi_cec_unregister(connector);
+}
+
+static const struct drm_connector_hdmi_cec_adapter_ops drm_bridge_connector_hdmi_cec_ops = {
+	.base.unregister = drm_bridge_connector_hdmi_cec_unregister,
+	.init = drm_bridge_connector_hdmi_cec_init,
+	.enable = drm_bridge_connector_hdmi_cec_enable,
+	.log_addr = drm_bridge_connector_hdmi_cec_log_addr,
+	.transmit = drm_bridge_connector_hdmi_cec_transmit,
+};
+
+
 /* -----------------------------------------------------------------------------
  * Bridge Connector Initialisation
  */
@@ -633,6 +711,21 @@  struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 			if (ret)
 				return ERR_PTR(ret);
 		}
+
+		if (bridge->hdmi_cec_adapter_name) {
+			if (!bridge->funcs->hdmi_cec_enable ||
+			    !bridge->funcs->hdmi_cec_log_addr ||
+			    !bridge->funcs->hdmi_cec_transmit)
+				return ERR_PTR(-EINVAL);
+
+			ret = drm_connector_hdmi_cec_register(connector,
+							      &drm_bridge_connector_hdmi_cec_ops,
+							      bridge->hdmi_cec_adapter_name,
+							      bridge->hdmi_cec_available_las,
+							      bridge->hdmi_dev);
+			if (ret)
+				return ERR_PTR(ret);
+		}
 	} else {
 		ret = drmm_connector_init(drm, connector,
 					  &drm_bridge_connector_funcs,
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index df1d72c7e176c75585283684acc2ef2ffb2f8bff..b55e80a57758e8b652eac0cd01cb245a04e221f5 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -32,6 +32,7 @@ 
 #include <drm/drm_mode_object.h>
 #include <drm/drm_modes.h>
 
+struct cec_msg;
 struct device_node;
 
 struct drm_bridge;
@@ -729,6 +730,16 @@  struct drm_bridge_funcs {
 				      struct drm_bridge *bridge,
 				      bool enable, int direction);
 
+	int (*hdmi_cec_init)(struct drm_connector *connector,
+			     struct drm_bridge *bridge);
+
+	int (*hdmi_cec_enable)(struct drm_bridge *bridge, bool enable);
+
+	int (*hdmi_cec_log_addr)(struct drm_bridge *bridge, u8 logical_addr);
+
+	int (*hdmi_cec_transmit)(struct drm_bridge *bridge, u8 attempts,
+				 u32 signal_free_time, struct cec_msg *msg);
+
 	/**
 	 * @debugfs_init:
 	 *
@@ -924,6 +935,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.