mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 18:22:00 -04:00
drm/bridge: tda998x: Add support for DRM_BRIDGE_ATTACH_NO_CONNECTOR
Add support for the DRM_BRIDGE_ATTACH_NO_CONNECTOR flag to allow display controller drivers to create their own connectors. This modernizes the driver to work with the current DRM bridge framework. The implementation includes: - Refactoring detection and EDID reading into bridge-usable helpers - Adding bridge operations: edid_read, detect, hpd_enable, hpd_disable - Setting appropriate bridge ops (DRM_BRIDGE_OP_EDID, DRM_BRIDGE_OP_DETECT, DRM_BRIDGE_OP_HPD) and connector type (HDMIA) - Skipping connector creation when DRM_BRIDGE_ATTACH_NO_CONNECTOR is set - Handling conditional connector cleanup in bridge_detach The driver maintains backward compatibility by continuing to create its own connector when the flag is not set. Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com> Signed-off-by: Kory Maincent (TI.com) <kory.maincent@bootlin.com> Link: https://patch.msgid.link/20260123-feature_tilcdc-v5-24-5a44d2aa3f6f@bootlin.com Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
This commit is contained in:
committed by
Luca Ceresoli
parent
8ab51f56fd
commit
c76a8be4fe
@@ -1193,16 +1193,22 @@ static int tda998x_audio_codec_init(struct tda998x_priv *priv,
|
|||||||
|
|
||||||
/* DRM connector functions */
|
/* DRM connector functions */
|
||||||
|
|
||||||
static enum drm_connector_status
|
static enum drm_connector_status tda998x_conn_detect(struct tda998x_priv *priv)
|
||||||
tda998x_connector_detect(struct drm_connector *connector, bool force)
|
|
||||||
{
|
{
|
||||||
struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
|
|
||||||
u8 val = cec_read(priv, REG_CEC_RXSHPDLEV);
|
u8 val = cec_read(priv, REG_CEC_RXSHPDLEV);
|
||||||
|
|
||||||
return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected :
|
return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected :
|
||||||
connector_status_disconnected;
|
connector_status_disconnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum drm_connector_status
|
||||||
|
tda998x_connector_detect(struct drm_connector *connector, bool force)
|
||||||
|
{
|
||||||
|
struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
|
||||||
|
|
||||||
|
return tda998x_conn_detect(priv);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct drm_connector_funcs tda998x_connector_funcs = {
|
static const struct drm_connector_funcs tda998x_connector_funcs = {
|
||||||
.reset = drm_atomic_helper_connector_reset,
|
.reset = drm_atomic_helper_connector_reset,
|
||||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||||
@@ -1276,11 +1282,10 @@ static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tda998x_connector_get_modes(struct drm_connector *connector)
|
static const struct drm_edid *tda998x_edid_read(struct tda998x_priv *priv,
|
||||||
|
struct drm_connector *connector)
|
||||||
{
|
{
|
||||||
struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
|
|
||||||
const struct drm_edid *drm_edid;
|
const struct drm_edid *drm_edid;
|
||||||
int n;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we get killed while waiting for the HPD timeout, return
|
* If we get killed while waiting for the HPD timeout, return
|
||||||
@@ -1298,6 +1303,16 @@ static int tda998x_connector_get_modes(struct drm_connector *connector)
|
|||||||
if (priv->rev == TDA19988)
|
if (priv->rev == TDA19988)
|
||||||
reg_set(priv, REG_TX4, TX4_PD_RAM);
|
reg_set(priv, REG_TX4, TX4_PD_RAM);
|
||||||
|
|
||||||
|
return drm_edid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tda998x_connector_get_modes(struct drm_connector *connector)
|
||||||
|
{
|
||||||
|
struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
|
||||||
|
const struct drm_edid *drm_edid;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
drm_edid = tda998x_edid_read(priv, connector);
|
||||||
drm_edid_connector_update(connector, drm_edid);
|
drm_edid_connector_update(connector, drm_edid);
|
||||||
cec_notifier_set_phys_addr(priv->cec_notify,
|
cec_notifier_set_phys_addr(priv->cec_notify,
|
||||||
connector->display_info.source_physical_address);
|
connector->display_info.source_physical_address);
|
||||||
@@ -1365,10 +1380,8 @@ static int tda998x_bridge_attach(struct drm_bridge *bridge,
|
|||||||
{
|
{
|
||||||
struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge);
|
struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge);
|
||||||
|
|
||||||
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
|
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
|
||||||
DRM_ERROR("Fix bridge driver to make connector optional!");
|
return 0;
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tda998x_connector_init(priv, bridge->dev);
|
return tda998x_connector_init(priv, bridge->dev);
|
||||||
}
|
}
|
||||||
@@ -1377,7 +1390,8 @@ static void tda998x_bridge_detach(struct drm_bridge *bridge)
|
|||||||
{
|
{
|
||||||
struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge);
|
struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge);
|
||||||
|
|
||||||
drm_connector_cleanup(&priv->connector);
|
if (priv->connector.dev)
|
||||||
|
drm_connector_cleanup(&priv->connector);
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum drm_mode_status tda998x_bridge_mode_valid(struct drm_bridge *bridge,
|
static enum drm_mode_status tda998x_bridge_mode_valid(struct drm_bridge *bridge,
|
||||||
@@ -1677,6 +1691,59 @@ static void tda998x_bridge_mode_set(struct drm_bridge *bridge,
|
|||||||
mutex_unlock(&priv->audio_mutex);
|
mutex_unlock(&priv->audio_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct drm_edid *
|
||||||
|
tda998x_bridge_edid_read(struct drm_bridge *bridge,
|
||||||
|
struct drm_connector *connector)
|
||||||
|
{
|
||||||
|
struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge);
|
||||||
|
const struct drm_edid *drm_edid;
|
||||||
|
const struct edid *edid;
|
||||||
|
|
||||||
|
drm_edid = tda998x_edid_read(priv, connector);
|
||||||
|
if (!drm_edid) {
|
||||||
|
dev_dbg(&priv->hdmi->dev, "failed to get edid\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIXME: This should use connector->display_info.has_audio from
|
||||||
|
* a path that has read the EDID and called
|
||||||
|
* drm_edid_connector_update().
|
||||||
|
*/
|
||||||
|
edid = drm_edid_raw(drm_edid);
|
||||||
|
|
||||||
|
dev_dbg(&priv->hdmi->dev, "got edid: width[%d] x height[%d]\n",
|
||||||
|
edid->width_cm, edid->height_cm);
|
||||||
|
|
||||||
|
priv->sink_has_audio = drm_detect_monitor_audio(edid);
|
||||||
|
cec_notifier_set_phys_addr_from_edid(priv->cec_notify, edid);
|
||||||
|
|
||||||
|
return drm_edid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum drm_connector_status
|
||||||
|
tda998x_bridge_detect(struct drm_bridge *bridge,
|
||||||
|
struct drm_connector *connector)
|
||||||
|
{
|
||||||
|
struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge);
|
||||||
|
|
||||||
|
return tda998x_conn_detect(priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tda998x_bridge_hpd_enable(struct drm_bridge *bridge)
|
||||||
|
{
|
||||||
|
struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge);
|
||||||
|
|
||||||
|
cec_write(priv, REG_CEC_RXSHPDINTENA, CEC_RXSHPDLEV_HPD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tda998x_bridge_hpd_disable(struct drm_bridge *bridge)
|
||||||
|
{
|
||||||
|
struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge);
|
||||||
|
|
||||||
|
cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct drm_bridge_funcs tda998x_bridge_funcs = {
|
static const struct drm_bridge_funcs tda998x_bridge_funcs = {
|
||||||
.attach = tda998x_bridge_attach,
|
.attach = tda998x_bridge_attach,
|
||||||
.detach = tda998x_bridge_detach,
|
.detach = tda998x_bridge_detach,
|
||||||
@@ -1684,6 +1751,10 @@ static const struct drm_bridge_funcs tda998x_bridge_funcs = {
|
|||||||
.disable = tda998x_bridge_disable,
|
.disable = tda998x_bridge_disable,
|
||||||
.mode_set = tda998x_bridge_mode_set,
|
.mode_set = tda998x_bridge_mode_set,
|
||||||
.enable = tda998x_bridge_enable,
|
.enable = tda998x_bridge_enable,
|
||||||
|
.edid_read = tda998x_bridge_edid_read,
|
||||||
|
.detect = tda998x_bridge_detect,
|
||||||
|
.hpd_enable = tda998x_bridge_hpd_enable,
|
||||||
|
.hpd_disable = tda998x_bridge_hpd_disable,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* I2C driver functions */
|
/* I2C driver functions */
|
||||||
@@ -1872,6 +1943,7 @@ tda998x_probe(struct i2c_client *client)
|
|||||||
|
|
||||||
/* enable HPD irq */
|
/* enable HPD irq */
|
||||||
cec_write(priv, REG_CEC_RXSHPDINTENA, CEC_RXSHPDLEV_HPD);
|
cec_write(priv, REG_CEC_RXSHPDINTENA, CEC_RXSHPDLEV_HPD);
|
||||||
|
priv->bridge.ops = DRM_BRIDGE_OP_HPD;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->cec_notify = cec_notifier_conn_register(dev, NULL, NULL);
|
priv->cec_notify = cec_notifier_conn_register(dev, NULL, NULL);
|
||||||
@@ -1932,6 +2004,8 @@ tda998x_probe(struct i2c_client *client)
|
|||||||
priv->bridge.of_node = dev->of_node;
|
priv->bridge.of_node = dev->of_node;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
priv->bridge.ops |= DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_DETECT;
|
||||||
|
priv->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
|
||||||
drm_bridge_add(&priv->bridge);
|
drm_bridge_add(&priv->bridge);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user