diff options
Diffstat (limited to 'drivers/gpu/drm/drm_bridge_connector.c')
-rw-r--r-- | drivers/gpu/drm/drm_bridge_connector.c | 107 |
1 files changed, 91 insertions, 16 deletions
diff --git a/drivers/gpu/drm/drm_bridge_connector.c b/drivers/gpu/drm/drm_bridge_connector.c index 982552c9f92c..0869b663f17e 100644 --- a/drivers/gpu/drm/drm_bridge_connector.c +++ b/drivers/gpu/drm/drm_bridge_connector.c @@ -15,8 +15,10 @@ #include <drm/drm_connector.h> #include <drm/drm_device.h> #include <drm/drm_edid.h> +#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 @@ -86,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) \ @@ -193,19 +202,6 @@ drm_bridge_connector_detect(struct drm_connector *connector, bool force) return status; } -static void drm_bridge_connector_destroy(struct drm_connector *connector) -{ - struct drm_bridge_connector *bridge_connector = - to_drm_bridge_connector(connector); - - drm_connector_unregister(connector); - drm_connector_cleanup(connector); - - fwnode_handle_put(connector->fwnode); - - kfree(bridge_connector); -} - static void drm_bridge_connector_debugfs_init(struct drm_connector *connector, struct dentry *root) { @@ -224,7 +220,6 @@ static const struct drm_connector_funcs drm_bridge_connector_funcs = { .reset = drm_atomic_helper_connector_reset, .detect = drm_bridge_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = drm_bridge_connector_destroy, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .debugfs_init = drm_bridge_connector_debugfs_init, @@ -300,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 */ @@ -325,10 +374,12 @@ 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; - bridge_connector = kzalloc(sizeof(*bridge_connector), GFP_KERNEL); + bridge_connector = drmm_kzalloc(drm, sizeof(*bridge_connector), GFP_KERNEL); if (!bridge_connector) return ERR_PTR(-ENOMEM); @@ -361,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; @@ -383,7 +448,17 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, return ERR_PTR(-EINVAL); } - ret = drm_connector_init_with_ddc(drm, connector, + 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) { |