mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	drm/bridge-connector: implement glue code for HDMI connector
In order to let bridge chains implement HDMI connector infrastructure, add necessary glue code to the drm_bridge_connector. In case there is a bridge that sets DRM_BRIDGE_OP_HDMI, drm_bridge_connector will register itself as a HDMI connector and provide proxy drm_connector_hdmi_funcs implementation. Note, to simplify implementation, there can be only one bridge in a chain that sets DRM_BRIDGE_OP_HDMI. Setting more than one is considered an error. This limitation can be lifted later, if the need arises. Acked-by: Maxime Ripard <mripard@kernel.org> Link: https://patchwork.freedesktop.org/patch/msgid/20240607-bridge-hdmi-connector-v5-3-ab384e6021af@linaro.org Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
This commit is contained in:
		
							parent
							
								
									c12907be57
								
							
						
					
					
						commit
						6b4468b0c6
					
				
					 3 changed files with 174 additions and 3 deletions
				
			
		| 
						 | 
					@ -18,6 +18,7 @@
 | 
				
			||||||
#include <drm/drm_managed.h>
 | 
					#include <drm/drm_managed.h>
 | 
				
			||||||
#include <drm/drm_modeset_helper_vtables.h>
 | 
					#include <drm/drm_modeset_helper_vtables.h>
 | 
				
			||||||
#include <drm/drm_probe_helper.h>
 | 
					#include <drm/drm_probe_helper.h>
 | 
				
			||||||
 | 
					#include <drm/display/drm_hdmi_state_helper.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * DOC: overview
 | 
					 * DOC: overview
 | 
				
			||||||
| 
						 | 
					@ -87,6 +88,13 @@ struct drm_bridge_connector {
 | 
				
			||||||
	 * connector modes detection, if any (see &DRM_BRIDGE_OP_MODES).
 | 
						 * connector modes detection, if any (see &DRM_BRIDGE_OP_MODES).
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	struct drm_bridge *bridge_modes;
 | 
						struct drm_bridge *bridge_modes;
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @bridge_hdmi:
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * The bridge in the chain that implements necessary support for the
 | 
				
			||||||
 | 
						 * HDMI connector infrastructure, if any (see &DRM_BRIDGE_OP_HDMI).
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						struct drm_bridge *bridge_hdmi;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define to_drm_bridge_connector(x) \
 | 
					#define to_drm_bridge_connector(x) \
 | 
				
			||||||
| 
						 | 
					@ -287,6 +295,60 @@ static const struct drm_connector_helper_funcs drm_bridge_connector_helper_funcs
 | 
				
			||||||
	.disable_hpd = drm_bridge_connector_disable_hpd,
 | 
						.disable_hpd = drm_bridge_connector_disable_hpd,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static enum drm_mode_status
 | 
				
			||||||
 | 
					drm_bridge_connector_tmds_char_rate_valid(const struct drm_connector *connector,
 | 
				
			||||||
 | 
										  const struct drm_display_mode *mode,
 | 
				
			||||||
 | 
										  unsigned long long tmds_rate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct drm_bridge_connector *bridge_connector =
 | 
				
			||||||
 | 
							to_drm_bridge_connector(connector);
 | 
				
			||||||
 | 
						struct drm_bridge *bridge;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bridge = bridge_connector->bridge_hdmi;
 | 
				
			||||||
 | 
						if (!bridge)
 | 
				
			||||||
 | 
							return MODE_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (bridge->funcs->hdmi_tmds_char_rate_valid)
 | 
				
			||||||
 | 
							return bridge->funcs->hdmi_tmds_char_rate_valid(bridge, mode, tmds_rate);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return MODE_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int drm_bridge_connector_clear_infoframe(struct drm_connector *connector,
 | 
				
			||||||
 | 
											enum hdmi_infoframe_type type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct drm_bridge_connector *bridge_connector =
 | 
				
			||||||
 | 
							to_drm_bridge_connector(connector);
 | 
				
			||||||
 | 
						struct drm_bridge *bridge;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bridge = bridge_connector->bridge_hdmi;
 | 
				
			||||||
 | 
						if (!bridge)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return bridge->funcs->hdmi_clear_infoframe(bridge, type);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int drm_bridge_connector_write_infoframe(struct drm_connector *connector,
 | 
				
			||||||
 | 
											enum hdmi_infoframe_type type,
 | 
				
			||||||
 | 
											const u8 *buffer, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct drm_bridge_connector *bridge_connector =
 | 
				
			||||||
 | 
							to_drm_bridge_connector(connector);
 | 
				
			||||||
 | 
						struct drm_bridge *bridge;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bridge = bridge_connector->bridge_hdmi;
 | 
				
			||||||
 | 
						if (!bridge)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return bridge->funcs->hdmi_write_infoframe(bridge, type, buffer, len);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct drm_connector_hdmi_funcs drm_bridge_connector_hdmi_funcs = {
 | 
				
			||||||
 | 
						.tmds_char_rate_valid = drm_bridge_connector_tmds_char_rate_valid,
 | 
				
			||||||
 | 
						.clear_infoframe = drm_bridge_connector_clear_infoframe,
 | 
				
			||||||
 | 
						.write_infoframe = drm_bridge_connector_write_infoframe,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* -----------------------------------------------------------------------------
 | 
					/* -----------------------------------------------------------------------------
 | 
				
			||||||
 * Bridge Connector Initialisation
 | 
					 * Bridge Connector Initialisation
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -312,6 +374,8 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 | 
				
			||||||
	struct drm_connector *connector;
 | 
						struct drm_connector *connector;
 | 
				
			||||||
	struct i2c_adapter *ddc = NULL;
 | 
						struct i2c_adapter *ddc = NULL;
 | 
				
			||||||
	struct drm_bridge *bridge, *panel_bridge = NULL;
 | 
						struct drm_bridge *bridge, *panel_bridge = NULL;
 | 
				
			||||||
 | 
						unsigned int supported_formats = BIT(HDMI_COLORSPACE_RGB);
 | 
				
			||||||
 | 
						unsigned int max_bpc = 8;
 | 
				
			||||||
	int connector_type;
 | 
						int connector_type;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -348,6 +412,20 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 | 
				
			||||||
			bridge_connector->bridge_detect = bridge;
 | 
								bridge_connector->bridge_detect = bridge;
 | 
				
			||||||
		if (bridge->ops & DRM_BRIDGE_OP_MODES)
 | 
							if (bridge->ops & DRM_BRIDGE_OP_MODES)
 | 
				
			||||||
			bridge_connector->bridge_modes = bridge;
 | 
								bridge_connector->bridge_modes = bridge;
 | 
				
			||||||
 | 
							if (bridge->ops & DRM_BRIDGE_OP_HDMI) {
 | 
				
			||||||
 | 
								if (bridge_connector->bridge_hdmi)
 | 
				
			||||||
 | 
									return ERR_PTR(-EBUSY);
 | 
				
			||||||
 | 
								if (!bridge->funcs->hdmi_write_infoframe ||
 | 
				
			||||||
 | 
								    !bridge->funcs->hdmi_clear_infoframe)
 | 
				
			||||||
 | 
									return ERR_PTR(-EINVAL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								bridge_connector->bridge_hdmi = bridge;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (bridge->supported_formats)
 | 
				
			||||||
 | 
									supported_formats = bridge->supported_formats;
 | 
				
			||||||
 | 
								if (bridge->max_bpc)
 | 
				
			||||||
 | 
									max_bpc = bridge->max_bpc;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!drm_bridge_get_next_bridge(bridge))
 | 
							if (!drm_bridge_get_next_bridge(bridge))
 | 
				
			||||||
			connector_type = bridge->type;
 | 
								connector_type = bridge->type;
 | 
				
			||||||
| 
						 | 
					@ -370,9 +448,19 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 | 
				
			||||||
		return ERR_PTR(-EINVAL);
 | 
							return ERR_PTR(-EINVAL);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = drmm_connector_init(drm, connector,
 | 
						if (bridge_connector->bridge_hdmi)
 | 
				
			||||||
				  &drm_bridge_connector_funcs,
 | 
							ret = drmm_connector_hdmi_init(drm, connector,
 | 
				
			||||||
				  connector_type, ddc);
 | 
										       bridge_connector->bridge_hdmi->vendor,
 | 
				
			||||||
 | 
										       bridge_connector->bridge_hdmi->product,
 | 
				
			||||||
 | 
										       &drm_bridge_connector_funcs,
 | 
				
			||||||
 | 
										       &drm_bridge_connector_hdmi_funcs,
 | 
				
			||||||
 | 
										       connector_type, ddc,
 | 
				
			||||||
 | 
										       supported_formats,
 | 
				
			||||||
 | 
										       max_bpc);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							ret = drmm_connector_init(drm, connector,
 | 
				
			||||||
 | 
										  &drm_bridge_connector_funcs,
 | 
				
			||||||
 | 
										  connector_type, ddc);
 | 
				
			||||||
	if (ret) {
 | 
						if (ret) {
 | 
				
			||||||
		kfree(bridge_connector);
 | 
							kfree(bridge_connector);
 | 
				
			||||||
		return ERR_PTR(ret);
 | 
							return ERR_PTR(ret);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -762,6 +762,8 @@ static int bridges_show(struct seq_file *m, void *data)
 | 
				
			||||||
			drm_puts(&p, " hpd");
 | 
								drm_puts(&p, " hpd");
 | 
				
			||||||
		if (bridge->ops & DRM_BRIDGE_OP_MODES)
 | 
							if (bridge->ops & DRM_BRIDGE_OP_MODES)
 | 
				
			||||||
			drm_puts(&p, " modes");
 | 
								drm_puts(&p, " modes");
 | 
				
			||||||
 | 
							if (bridge->ops & DRM_BRIDGE_OP_HDMI)
 | 
				
			||||||
 | 
								drm_puts(&p, " hdmi");
 | 
				
			||||||
		drm_puts(&p, "\n");
 | 
							drm_puts(&p, "\n");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -630,6 +630,52 @@ struct drm_bridge_funcs {
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	void (*hpd_disable)(struct drm_bridge *bridge);
 | 
						void (*hpd_disable)(struct drm_bridge *bridge);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @hdmi_tmds_char_rate_valid:
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Check whether a particular TMDS character rate is supported by the
 | 
				
			||||||
 | 
						 * driver.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * This callback is optional and should only be implemented by the
 | 
				
			||||||
 | 
						 * bridges that take part in the HDMI connector implementation. Bridges
 | 
				
			||||||
 | 
						 * that implement it shall set the DRM_BRIDGE_OP_HDMI flag in their
 | 
				
			||||||
 | 
						 * &drm_bridge->ops.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Returns:
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Either &drm_mode_status.MODE_OK or one of the failure reasons
 | 
				
			||||||
 | 
						 * in &enum drm_mode_status.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						enum drm_mode_status
 | 
				
			||||||
 | 
						(*hdmi_tmds_char_rate_valid)(const struct drm_bridge *bridge,
 | 
				
			||||||
 | 
									     const struct drm_display_mode *mode,
 | 
				
			||||||
 | 
									     unsigned long long tmds_rate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @hdmi_clear_infoframe:
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * This callback clears the infoframes in the hardware during commit.
 | 
				
			||||||
 | 
						 * It will be called multiple times, once for every disabled infoframe
 | 
				
			||||||
 | 
						 * type.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * This callback is optional but it must be implemented by bridges that
 | 
				
			||||||
 | 
						 * set the DRM_BRIDGE_OP_HDMI flag in their &drm_bridge->ops.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						int (*hdmi_clear_infoframe)(struct drm_bridge *bridge,
 | 
				
			||||||
 | 
									    enum hdmi_infoframe_type type);
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @hdmi_write_infoframe:
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Program the infoframe into the hardware. It will be called multiple
 | 
				
			||||||
 | 
						 * times, once for every updated infoframe type.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * This callback is optional but it must be implemented by bridges that
 | 
				
			||||||
 | 
						 * set the DRM_BRIDGE_OP_HDMI flag in their &drm_bridge->ops.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						int (*hdmi_write_infoframe)(struct drm_bridge *bridge,
 | 
				
			||||||
 | 
									    enum hdmi_infoframe_type type,
 | 
				
			||||||
 | 
									    const u8 *buffer, size_t len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * @debugfs_init:
 | 
						 * @debugfs_init:
 | 
				
			||||||
	 *
 | 
						 *
 | 
				
			||||||
| 
						 | 
					@ -705,6 +751,16 @@ enum drm_bridge_ops {
 | 
				
			||||||
	 * this flag shall implement the &drm_bridge_funcs->get_modes callback.
 | 
						 * this flag shall implement the &drm_bridge_funcs->get_modes callback.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	DRM_BRIDGE_OP_MODES = BIT(3),
 | 
						DRM_BRIDGE_OP_MODES = BIT(3),
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @DRM_BRIDGE_OP_HDMI: The bridge provides HDMI connector operations,
 | 
				
			||||||
 | 
						 * including infoframes support. Bridges that set this flag must
 | 
				
			||||||
 | 
						 * implement the &drm_bridge_funcs->write_infoframe callback.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Note: currently there can be at most one bridge in a chain that sets
 | 
				
			||||||
 | 
						 * this bit. This is to simplify corresponding glue code in connector
 | 
				
			||||||
 | 
						 * drivers.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						DRM_BRIDGE_OP_HDMI = BIT(4),
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -773,6 +829,31 @@ struct drm_bridge {
 | 
				
			||||||
	 * @hpd_cb.
 | 
						 * @hpd_cb.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	void *hpd_data;
 | 
						void *hpd_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @vendor: Vendor of the product to be used for the SPD InfoFrame
 | 
				
			||||||
 | 
						 * generation. This is required if @DRM_BRIDGE_OP_HDMI is set.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						const char *vendor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @product: Name of the product to be used for the SPD InfoFrame
 | 
				
			||||||
 | 
						 * generation. This is required if @DRM_BRIDGE_OP_HDMI is set.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						const char *product;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @supported_formats: Bitmask of @hdmi_colorspace listing supported
 | 
				
			||||||
 | 
						 * output formats. This is only relevant if @DRM_BRIDGE_OP_HDMI is set.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						unsigned int supported_formats;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @max_bpc: Maximum bits per char the HDMI bridge supports. Allowed
 | 
				
			||||||
 | 
						 * values are 8, 10 and 12. This is only relevant if
 | 
				
			||||||
 | 
						 * @DRM_BRIDGE_OP_HDMI is set.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						unsigned int max_bpc;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline struct drm_bridge *
 | 
					static inline struct drm_bridge *
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue