From 6b4468b0c6ba37a16795da567b58dc80bc7fb439 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 7 Jun 2024 16:23:00 +0300 Subject: 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 Link: https://patchwork.freedesktop.org/patch/msgid/20240607-bridge-hdmi-connector-v5-3-ab384e6021af@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/drm_bridge_connector.c | 94 ++++++++++++++++++++++++++++++++-- drivers/gpu/drm/drm_debugfs.c | 2 + 2 files changed, 93 insertions(+), 3 deletions(-) (limited to 'drivers/gpu') diff --git a/drivers/gpu/drm/drm_bridge_connector.c b/drivers/gpu/drm/drm_bridge_connector.c index e093fc8928dc..0869b663f17e 100644 --- a/drivers/gpu/drm/drm_bridge_connector.c +++ b/drivers/gpu/drm/drm_bridge_connector.c @@ -18,6 +18,7 @@ #include #include #include +#include /** * 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,9 +448,19 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, return ERR_PTR(-EINVAL); } - ret = drmm_connector_init(drm, connector, - &drm_bridge_connector_funcs, - connector_type, ddc); + 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); if (ret) { kfree(bridge_connector); return ERR_PTR(ret); diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 8bec99251bee..6b239a24f1df 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -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"); } -- cgit