[PATCH RFC v2 3/5] drm/bridge-connector: implement glue code for HDMI connector
Dmitry Baryshkov
dmitry.baryshkov at linaro.org
Sat Mar 9 10:31:30 UTC 2024
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.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov at linaro.org>
---
drivers/gpu/drm/drm_bridge_connector.c | 118 ++++++++++++++++++++++++++++++++-
include/drm/drm_bridge.h | 82 +++++++++++++++++++++++
2 files changed, 197 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge_connector.c b/drivers/gpu/drm/drm_bridge_connector.c
index 982552c9f92c..514dbb38dfed 100644
--- a/drivers/gpu/drm/drm_bridge_connector.c
+++ b/drivers/gpu/drm/drm_bridge_connector.c
@@ -86,6 +86,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) \
@@ -293,11 +300,86 @@ static int drm_bridge_connector_get_modes(struct drm_connector *connector)
return 0;
}
+static int drm_bridge_connector_atomic_check(struct drm_connector *connector,
+ struct drm_atomic_state *state)
+{
+ struct drm_bridge_connector *bridge_connector =
+ to_drm_bridge_connector(connector);
+ struct drm_bridge *bridge;
+ int ret;
+
+ bridge = bridge_connector->bridge_hdmi;
+ if (bridge) {
+ ret = drm_atomic_helper_connector_hdmi_check(connector, state);
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static const struct drm_connector_helper_funcs drm_bridge_connector_helper_funcs = {
.get_modes = drm_bridge_connector_get_modes,
/* No need for .mode_valid(), the bridges are checked by the core. */
.enable_hpd = drm_bridge_connector_enable_hpd,
.disable_hpd = drm_bridge_connector_disable_hpd,
+ .atomic_check = drm_bridge_connector_atomic_check,
+};
+
+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 bridge->funcs->tmds_char_rate_valid ?
+ bridge->funcs->tmds_char_rate_valid(bridge, mode, tmds_rate) :
+ MODE_OK;
+
+ return MODE_ERROR;
+}
+
+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 bridge->funcs->clear_infoframe ?
+ bridge->funcs->clear_infoframe(bridge, type) :
+ 0;
+
+ return -EINVAL;
+}
+
+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 bridge->funcs->write_infoframe(bridge, type, buffer, len);
+
+ return -EINVAL;
+}
+
+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,
};
/* -----------------------------------------------------------------------------
@@ -325,6 +407,10 @@ 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;
+ const char *vendor = "Unknown";
+ const char *product = "Unknown";
+ unsigned int supported_formats = BIT(HDMI_COLORSPACE_RGB);
+ unsigned int max_bpc = 8;
int connector_type;
int ret;
@@ -361,6 +447,23 @@ 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);
+
+ bridge_connector->bridge_hdmi = bridge;
+
+ if (bridge->supported_formats)
+ supported_formats = bridge->supported_formats;
+ if (bridge->max_bpc)
+ max_bpc = bridge->max_bpc;
+ }
+
+ if (bridge->vendor)
+ vendor = bridge->vendor;
+
+ if (bridge->product)
+ product = bridge->product;
if (!drm_bridge_get_next_bridge(bridge))
connector_type = bridge->type;
@@ -383,9 +486,18 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
return ERR_PTR(-EINVAL);
}
- ret = drm_connector_init_with_ddc(drm, connector,
- &drm_bridge_connector_funcs,
- connector_type, ddc);
+ if (bridge_connector->bridge_hdmi)
+ ret = drm_connector_hdmi_init(drm, connector,
+ vendor, product,
+ &drm_bridge_connector_funcs,
+ &drm_bridge_connector_hdmi_funcs,
+ connector_type, ddc,
+ supported_formats,
+ max_bpc);
+ else
+ ret = drm_connector_init_with_ddc(drm, connector,
+ &drm_bridge_connector_funcs,
+ connector_type, ddc);
if (ret) {
kfree(bridge_connector);
return ERR_PTR(ret);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 3606e1a7f965..f44f32b182b8 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -630,6 +630,54 @@ struct drm_bridge_funcs {
*/
void (*hpd_disable)(struct drm_bridge *bridge);
+ /**
+ * @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 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
+ (*tmds_char_rate_valid)(const struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ unsigned long long tmds_rate);
+
+ /**
+ * @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 and should only be implemented by the
+ * bridges that take part in the HDMI connector implementation. Bridges
+ * that implement it shall set set the DRM_BRIDGE_OP_HDMI flag in their
+ * &drm_bridge->ops.
+ */
+ int (*clear_infoframe)(struct drm_bridge *bridge,
+ enum hdmi_infoframe_type type);
+ /**
+ * @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 (*write_infoframe)(struct drm_bridge *bridge,
+ enum hdmi_infoframe_type type,
+ const u8 *buffer, size_t len);
+
/**
* @debugfs_init:
*
@@ -705,6 +753,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 +831,30 @@ struct drm_bridge {
* @hpd_cb.
*/
void *hpd_data;
+
+ /**
+ * @vendor: Vendor of the product to be used for the SPD InfoFrame
+ * generation.
+ */
+ const char *vendor;
+
+ /**
+ * @product: Name of the product to be used for the SPD InfoFrame
+ * generation.
+ */
+ 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. This is
+ * only relevant if @DRM_BRIDGE_OP_HDMI is set.
+ */
+ unsigned int max_bpc;
};
static inline struct drm_bridge *
--
2.39.2
More information about the dri-devel
mailing list