mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
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 <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>
This commit is contained in:
parent
c12907be57
commit
6b4468b0c6
@ -18,6 +18,7 @@
|
|||||||
#include <drm/drm_managed.h>
|
#include <drm/drm_managed.h>
|
||||||
#include <drm/drm_modeset_helper_vtables.h>
|
#include <drm/drm_modeset_helper_vtables.h>
|
||||||
#include <drm/drm_probe_helper.h>
|
#include <drm/drm_probe_helper.h>
|
||||||
|
#include <drm/display/drm_hdmi_state_helper.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DOC: overview
|
* DOC: overview
|
||||||
@ -87,6 +88,13 @@ struct drm_bridge_connector {
|
|||||||
* connector modes detection, if any (see &DRM_BRIDGE_OP_MODES).
|
* connector modes detection, if any (see &DRM_BRIDGE_OP_MODES).
|
||||||
*/
|
*/
|
||||||
struct drm_bridge *bridge_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) \
|
#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,
|
.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
|
* Bridge Connector Initialisation
|
||||||
*/
|
*/
|
||||||
@ -312,6 +374,8 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
|||||||
struct drm_connector *connector;
|
struct drm_connector *connector;
|
||||||
struct i2c_adapter *ddc = NULL;
|
struct i2c_adapter *ddc = NULL;
|
||||||
struct drm_bridge *bridge, *panel_bridge = 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 connector_type;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -348,6 +412,20 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
|||||||
bridge_connector->bridge_detect = bridge;
|
bridge_connector->bridge_detect = bridge;
|
||||||
if (bridge->ops & DRM_BRIDGE_OP_MODES)
|
if (bridge->ops & DRM_BRIDGE_OP_MODES)
|
||||||
bridge_connector->bridge_modes = bridge;
|
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))
|
if (!drm_bridge_get_next_bridge(bridge))
|
||||||
connector_type = bridge->type;
|
connector_type = bridge->type;
|
||||||
@ -370,9 +448,19 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
|||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = drmm_connector_init(drm, connector,
|
if (bridge_connector->bridge_hdmi)
|
||||||
&drm_bridge_connector_funcs,
|
ret = drmm_connector_hdmi_init(drm, connector,
|
||||||
connector_type, ddc);
|
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) {
|
if (ret) {
|
||||||
kfree(bridge_connector);
|
kfree(bridge_connector);
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
|
@ -762,6 +762,8 @@ static int bridges_show(struct seq_file *m, void *data)
|
|||||||
drm_puts(&p, " hpd");
|
drm_puts(&p, " hpd");
|
||||||
if (bridge->ops & DRM_BRIDGE_OP_MODES)
|
if (bridge->ops & DRM_BRIDGE_OP_MODES)
|
||||||
drm_puts(&p, " modes");
|
drm_puts(&p, " modes");
|
||||||
|
if (bridge->ops & DRM_BRIDGE_OP_HDMI)
|
||||||
|
drm_puts(&p, " hdmi");
|
||||||
drm_puts(&p, "\n");
|
drm_puts(&p, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -630,6 +630,52 @@ struct drm_bridge_funcs {
|
|||||||
*/
|
*/
|
||||||
void (*hpd_disable)(struct drm_bridge *bridge);
|
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:
|
* @debugfs_init:
|
||||||
*
|
*
|
||||||
@ -705,6 +751,16 @@ enum drm_bridge_ops {
|
|||||||
* this flag shall implement the &drm_bridge_funcs->get_modes callback.
|
* this flag shall implement the &drm_bridge_funcs->get_modes callback.
|
||||||
*/
|
*/
|
||||||
DRM_BRIDGE_OP_MODES = BIT(3),
|
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.
|
* @hpd_cb.
|
||||||
*/
|
*/
|
||||||
void *hpd_data;
|
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 *
|
static inline struct drm_bridge *
|
||||||
|
Loading…
Reference in New Issue
Block a user