Commit 6b4468b0 authored by Dmitry Baryshkov's avatar Dmitry Baryshkov
Browse files

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: default avatarMaxime Ripard <mripard@kernel.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20240607-bridge-hdmi-connector-v5-3-ab384e6021af@linaro.org


Signed-off-by: default avatarDmitry Baryshkov <dmitry.baryshkov@linaro.org>
parent c12907be
Loading
Loading
Loading
Loading
+91 −3
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_probe_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>

/**
 * DOC: overview
@@ -87,6 +88,13 @@ struct drm_bridge_connector {
	 * connector modes detection, if any (see &DRM_BRIDGE_OP_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) \
@@ -287,6 +295,60 @@ static const struct drm_connector_helper_funcs drm_bridge_connector_helper_funcs
	.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
 */
@@ -312,6 +374,8 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
	struct drm_connector *connector;
	struct i2c_adapter *ddc = 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 ret;

@@ -348,6 +412,20 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
			bridge_connector->bridge_detect = bridge;
		if (bridge->ops & DRM_BRIDGE_OP_MODES)
			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))
			connector_type = bridge->type;
@@ -370,6 +448,16 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
		return ERR_PTR(-EINVAL);
	}

	if (bridge_connector->bridge_hdmi)
		ret = drmm_connector_hdmi_init(drm, connector,
					       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);
+2 −0
Original line number Diff line number Diff line
@@ -762,6 +762,8 @@ static int bridges_show(struct seq_file *m, void *data)
			drm_puts(&p, " hpd");
		if (bridge->ops & DRM_BRIDGE_OP_MODES)
			drm_puts(&p, " modes");
		if (bridge->ops & DRM_BRIDGE_OP_HDMI)
			drm_puts(&p, " hdmi");
		drm_puts(&p, "\n");
	}

+81 −0
Original line number Diff line number Diff line
@@ -630,6 +630,52 @@ struct drm_bridge_funcs {
	 */
	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:
	 *
@@ -705,6 +751,16 @@ enum drm_bridge_ops {
	 * this flag shall implement the &drm_bridge_funcs->get_modes callback.
	 */
	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.
	 */
	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 *