[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-Id: <20251126-feature_tilcdc-v1-20-49b9ef2e3aa0@bootlin.com>
Date: Wed, 26 Nov 2025 18:36:02 +0100
From: "Kory Maincent (TI.com)" <kory.maincent@...tlin.com>
To: Jyri Sarha <jyri.sarha@....fi>,
Tomi Valkeinen <tomi.valkeinen@...asonboard.com>,
Maarten Lankhorst <maarten.lankhorst@...ux.intel.com>,
Maxime Ripard <mripard@...nel.org>, Thomas Zimmermann <tzimmermann@...e.de>,
David Airlie <airlied@...il.com>, Simona Vetter <simona@...ll.ch>,
Rob Herring <robh@...nel.org>, Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>, Russell King <linux@...linux.org.uk>,
Bartosz Golaszewski <brgl@...ev.pl>, Tony Lindgren <tony@...mide.com>,
Andrzej Hajda <andrzej.hajda@...el.com>,
Neil Armstrong <neil.armstrong@...aro.org>, Robert Foss <rfoss@...nel.org>,
Laurent Pinchart <Laurent.pinchart@...asonboard.com>,
Jonas Karlman <jonas@...boo.se>, Jernej Skrabec <jernej.skrabec@...il.com>
Cc: Markus Schneider-Pargmann <msp@...libre.com>,
Luca Ceresoli <luca.ceresoli@...tlin.com>,
Louis Chauvet <louis.chauvet@...tlin.com>,
Thomas Petazzoni <thomas.petazzoni@...tlin.com>,
Miguel Gazquez <miguel.gazquez@...tlin.com>,
dri-devel@...ts.freedesktop.org, devicetree@...r.kernel.org,
linux-kernel@...r.kernel.org, linux-arm-kernel@...ts.infradead.org,
linux-omap@...r.kernel.org,
"Kory Maincent (TI.com)" <kory.maincent@...tlin.com>
Subject: [PATCH 20/21] 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.
Signed-off-by: Kory Maincent (TI.com) <kory.maincent@...tlin.com>
---
drivers/gpu/drm/bridge/tda998x_drv.c | 96 +++++++++++++++++++++++++++++++-----
1 file changed, 85 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tda998x_drv.c b/drivers/gpu/drm/bridge/tda998x_drv.c
index 43ace6ee2ca35..4d78615634691 100644
--- a/drivers/gpu/drm/bridge/tda998x_drv.c
+++ b/drivers/gpu/drm/bridge/tda998x_drv.c
@@ -1193,16 +1193,22 @@ static int tda998x_audio_codec_init(struct tda998x_priv *priv,
/* DRM connector functions */
-static enum drm_connector_status
-tda998x_connector_detect(struct drm_connector *connector, bool force)
+static enum drm_connector_status tda998x_conn_detect(struct tda998x_priv *priv)
{
- struct tda998x_priv *priv = conn_to_tda998x_priv(connector);
u8 val = cec_read(priv, REG_CEC_RXSHPDLEV);
return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected :
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 = {
.reset = drm_atomic_helper_connector_reset,
.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;
}
-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;
- int n;
/*
* 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)
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);
cec_notifier_set_phys_addr(priv->cec_notify,
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);
- if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
- DRM_ERROR("Fix bridge driver to make connector optional!");
- return -EINVAL;
- }
+ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
+ return 0;
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);
- 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,
@@ -1677,6 +1691,59 @@ static void tda998x_bridge_mode_set(struct drm_bridge *bridge,
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 = {
.attach = tda998x_bridge_attach,
.detach = tda998x_bridge_detach,
@@ -1684,6 +1751,10 @@ static const struct drm_bridge_funcs tda998x_bridge_funcs = {
.disable = tda998x_bridge_disable,
.mode_set = tda998x_bridge_mode_set,
.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 */
@@ -1872,6 +1943,7 @@ tda998x_probe(struct i2c_client *client)
/* enable HPD irq */
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);
@@ -1932,6 +2004,8 @@ tda998x_probe(struct i2c_client *client)
priv->bridge.of_node = dev->of_node;
#endif
+ priv->bridge.ops |= DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_DETECT;
+ priv->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
drm_bridge_add(&priv->bridge);
return 0;
--
2.43.0
Powered by blists - more mailing lists