Skip to content

Commit

Permalink
drm/bridge-connector: implement glue code for HDMI connector
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Dmitry Baryshkov committed Jun 12, 2024
1 parent c12907b commit 6b4468b
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 3 deletions.
94 changes: 91 additions & 3 deletions drivers/gpu/drm/drm_bridge_connector.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) \
Expand Down Expand Up @@ -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
*/
Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/drm_debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

Expand Down
81 changes: 81 additions & 0 deletions include/drm/drm_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
*
Expand Down Expand Up @@ -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),
};

/**
Expand Down Expand Up @@ -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 *
Expand Down

0 comments on commit 6b4468b

Please sign in to comment.