* [PATCH 33/37] drm/bridge: samsung-dsim: implement .is_tail
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
This bridge is never a tail bridge, i.e. it always needs a following bridge
to complete the pipeline. Add a is_tail func to expose this.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/samsung-dsim.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
index 3c70a45c5dce..55f4b0f16ade 100644
--- a/drivers/gpu/drm/bridge/samsung-dsim.c
+++ b/drivers/gpu/drm/bridge/samsung-dsim.c
@@ -1716,6 +1716,11 @@ static const u32 samsung_dsim_pixel_output_fmts[] = {
MEDIA_BUS_FMT_RGB888_1X24,
};
+static bool samsung_dsim_is_tail(struct drm_bridge *bridge)
+{
+ return false;
+}
+
static bool samsung_dsim_pixel_output_fmt_supported(u32 fmt)
{
int i;
@@ -1836,6 +1841,7 @@ static int samsung_dsim_attach(struct drm_bridge *bridge,
}
static const struct drm_bridge_funcs samsung_dsim_bridge_funcs = {
+ .is_tail = samsung_dsim_is_tail,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
@@ -2225,6 +2231,7 @@ int samsung_dsim_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
+ dsi->bridge.ops = DRM_BRIDGE_OP_IS_TAIL;
dsi->bridge.of_node = dev->of_node;
dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
--
2.54.0
^ permalink raw reply related
* [PATCH 32/37] drm/bridge: display-connector: implement .is_tail
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
This bridge is always a tail bridge, i.e. it never needs a following bridge
to complete the pipeline. Add a is_tail func to expose this.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/display-connector.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c
index 16c0631adeb1..1c8111bdc89e 100644
--- a/drivers/gpu/drm/bridge/display-connector.c
+++ b/drivers/gpu/drm/bridge/display-connector.c
@@ -33,6 +33,11 @@ to_display_connector(struct drm_bridge *bridge)
return container_of(bridge, struct display_connector, bridge);
}
+static bool display_connector_is_tail(struct drm_bridge *bridge)
+{
+ return true;
+}
+
static int display_connector_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
@@ -176,6 +181,7 @@ static u32 *display_connector_get_input_bus_fmts(struct drm_bridge *bridge,
}
static const struct drm_bridge_funcs display_connector_bridge_funcs = {
+ .is_tail = display_connector_is_tail,
.attach = display_connector_attach,
.detect = display_connector_bridge_detect,
.edid_read = display_connector_edid_read,
@@ -370,6 +376,7 @@ static int display_connector_probe(struct platform_device *pdev)
conn->bridge.of_node = pdev->dev.of_node;
+ conn->bridge.ops = DRM_BRIDGE_OP_IS_TAIL;
if (conn->bridge.ddc)
conn->bridge.ops |= DRM_BRIDGE_OP_EDID
| DRM_BRIDGE_OP_DETECT;
--
2.54.0
^ permalink raw reply related
* [PATCH 34/37] drm/bridge: ti-sn65dsi83: implement .is_tail
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
This bridge is never a tail bridge, i.e. it always needs a following bridge
to complete the pipeline. Add a is_tail func to expose this.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/ti-sn65dsi83.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
index a4f2a86a09be..5613c8812318 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
@@ -293,6 +293,11 @@ static struct sn65dsi83 *bridge_to_sn65dsi83(struct drm_bridge *bridge)
return container_of(bridge, struct sn65dsi83, bridge);
}
+static bool sn65dsi83_is_tail(struct drm_bridge *bridge)
+{
+ return false;
+}
+
static int sn65dsi83_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
@@ -792,6 +797,7 @@ sn65dsi83_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
}
static const struct drm_bridge_funcs sn65dsi83_funcs = {
+ .is_tail = sn65dsi83_is_tail,
.attach = sn65dsi83_attach,
.detach = sn65dsi83_detach,
.atomic_enable = sn65dsi83_atomic_enable,
@@ -1066,6 +1072,7 @@ static int sn65dsi83_probe(struct i2c_client *client)
dev_set_drvdata(dev, ctx);
i2c_set_clientdata(client, ctx);
+ ctx->bridge.ops = DRM_BRIDGE_OP_IS_TAIL;
ctx->bridge.of_node = dev->of_node;
ctx->bridge.pre_enable_prev_first = true;
ctx->bridge.type = DRM_MODE_CONNECTOR_LVDS;
--
2.54.0
^ permalink raw reply related
* RE: [PATCH 0/7] soc: aspeed: Add AST2600 eSPI controller support
From: YH Chung @ 2026-05-19 10:40 UTC (permalink / raw)
To: Shulzhenko, Oleksandr, Arnd Bergmann, Andrew Jeffery,
Conor Dooley
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Joel Stanley,
Ryan Chen, Philipp Zabel, devicetree@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
openbmc@lists.ozlabs.org, maciej.lawniczak@intel.com, Mark Brown
In-Reply-To: <be4f662a-b986-4b4c-8263-2fd7b63c238a@linux.intel.com>
Hi Shulzhenko,
> My main concern is trying to understand whether it is completely impossible (or
> introduces too much effort that we'd better not to take) integrating this to SPI
> subsystem.
>
> From your reply I understand there are two potential blockers:
>
> a) Treating eSPI transfers as bus-level transfers (meaning that it will be necessary
> probably making separate driver for OOB/VW/Flash channels as they essentially
> use eSPI as a transport);
>
> b) Some logic being done by the hardware (i.e. LPC bridge).
>
> Please confirm my understanding:
>
> (a) is feasible, but requires many effort to re-define architecture
I think it may not be a good choice for our hardware, since eSPI has clearly
defined channel semantics and our hardware exposes different subsets of
registers for each channel accordingly. Therefore, it would not fit well with
the general messaging flow required by the SPI subsystem, because we would
have to re-serialize the packets and plumb them through the SPI subsystem to
a consumer that can handle the packet and perform the corresponding register
operations.
> (b) If something is done by the hardware - what is the driver impact? I recall
> eDAF use case when the driver wasn't involved at all - and flash access was fully
> done by the hardware (unless the controller is configured to handle it in SW
> mode).
For hardware mode, I do not think there is much direct driver implementation
impact. If the hardware handles the transaction completely, the driver may
only need to configure the controller during initialization.
Our concern is more about the design semantics. Integrating this into the SPI
subsystem would imply that eSPI packets are treated as low-level SPI-like bus
transfers. However, our hardware handles some channel packets automatically,
so exposing them as normal SPI transfers may not accurately represent how the
hardware operates.
^ permalink raw reply
* [PATCH] i2c: imx-lpi2c: reset controller in probe stage
From: Carlos Song (OSS) @ 2026-05-19 10:42 UTC (permalink / raw)
To: aisheng.dong, andi.shyti, Frank.Li, s.hauer, kernel, festevam
Cc: carlos.song, linux-i2c, imx, linux-arm-kernel, linux-kernel
From: Carlos Song <carlos.song@nxp.com>
Reset I2C controller in probe stage to avoid unexpected LPI2C controller
state left from previous stages and hang system boot.
Per the LPI2C reference manual, section 7.1.4 "Controller Control (MCR)",
the RST bit (bit 1) description states:
"The reset takes effect immediately and remains asserted until negated
by software. There is no minimum delay required before clearing the
software reset."
Therefore, it is safe to write 0 to MCR immediately after asserting the
RST bit without any additional delay.
Signed-off-by: Carlos Song <carlos.song@nxp.com>
---
drivers/i2c/busses/i2c-imx-lpi2c.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c
index bf8c1ce1c7ff..96f6dc23232e 100644
--- a/drivers/i2c/busses/i2c-imx-lpi2c.c
+++ b/drivers/i2c/busses/i2c-imx-lpi2c.c
@@ -1499,11 +1499,6 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
if (ret)
lpi2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
- ret = devm_request_irq(&pdev->dev, lpi2c_imx->irq, lpi2c_imx_isr, IRQF_NO_SUSPEND,
- pdev->name, lpi2c_imx);
- if (ret)
- return dev_err_probe(&pdev->dev, ret, "can't claim irq %d\n", lpi2c_imx->irq);
-
i2c_set_adapdata(&lpi2c_imx->adapter, lpi2c_imx);
platform_set_drvdata(pdev, lpi2c_imx);
@@ -1535,10 +1530,23 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
+ /*
+ * Reset all internal controller logic and registers to avoid effects of previous status
+ * The reset takes effect immediately and there is no minimum delay required before
+ * clearing the software reset.
+ */
+ writel(MCR_RST, lpi2c_imx->base + LPI2C_MCR);
+ writel(0, lpi2c_imx->base + LPI2C_MCR);
+
temp = readl(lpi2c_imx->base + LPI2C_PARAM);
lpi2c_imx->txfifosize = 1 << (temp & 0x0f);
lpi2c_imx->rxfifosize = 1 << ((temp >> 8) & 0x0f);
+ ret = devm_request_irq(&pdev->dev, lpi2c_imx->irq, lpi2c_imx_isr, IRQF_NO_SUSPEND,
+ pdev->name, lpi2c_imx);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "can't claim irq %d\n", lpi2c_imx->irq);
+
/* Init optional bus recovery function */
ret = lpi2c_imx_init_recovery_info(lpi2c_imx, pdev);
/* Give it another chance if pinctrl used is not ready yet */
--
2.43.0
^ permalink raw reply related
* [PATCH 31/37] drm/bridge: panel: implement .is_tail
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
This bridge is always a tail bridge, i.e. it never needs a following bridge
to complete the pipeline. Add a is_tail func to expose this.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/panel.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 4978ec98a082..102f987b1235 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -58,6 +58,11 @@ static const struct drm_connector_funcs panel_bridge_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
+static bool panel_bridge_is_tail(struct drm_bridge *bridge)
+{
+ return true;
+}
+
static int panel_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
@@ -206,6 +211,7 @@ static void panel_bridge_debugfs_init(struct drm_bridge *bridge,
}
static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
+ .is_tail = panel_bridge_is_tail,
.attach = panel_bridge_attach,
.detach = panel_bridge_detach,
.atomic_pre_enable = panel_bridge_atomic_pre_enable,
@@ -297,7 +303,7 @@ struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
panel_bridge->panel = panel;
panel_bridge->bridge.of_node = panel->dev->of_node;
- panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES;
+ panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES | DRM_BRIDGE_OP_IS_TAIL;
panel_bridge->bridge.type = connector_type;
panel_bridge->bridge.pre_enable_prev_first = panel->prepare_prev_first;
--
2.54.0
^ permalink raw reply related
* [PATCH 30/37] drm/bridge: add drm_bridge_is_tail() to know whether a bridge completes the pipeline
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
For bridge hotplug we need to successfully probe a card with an incomplete
bridge chain, i.e. a chain whose last bridge currently in bridge_chain
needs a next_bridge to work. Such a card would have no connector, and be
able to add one as soon as the followong bridges are added up to the tail
bridge (e.g. a panel_bridge or a connector_bridge).
Currently common DRM code is unable to tell whether the last bridge
currently in the chain is the tail bridge (= the pipeline is complete) or
not.
Add drm_bridge_is_tail(), and a .is_tail func for it to rely on, so common
code can know whether a bridge is a tail bridge or needs a next_bridge.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_bridge.c | 28 ++++++++++++++++++++++++++++
include/drm/drm_bridge.h | 19 +++++++++++++++++++
2 files changed, 47 insertions(+)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index c62d17e84d4f..2b539c9749a6 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -646,6 +646,34 @@ void drm_bridge_detach(struct drm_bridge *bridge)
drm_bridge_put(bridge);
}
+/**
+ * drm_bridge_is_tail - check whether the bridge is the last required to
+ * have a full pipeline
+ * @bridge: the bridge to check
+ *
+ * Tell whether this is a tail bridge, i.e. a bridge that does not need a
+ * next bridge to work. E.g. a panel_bridge is final, a DSI-to-LVDS bridge
+ * is not.
+ *
+ * In case of hotplug the last bridge currently in the chain (as in
+ * drm_bridge_chain_get_last_bridge()) might not be final, and be waiting
+ * for a pipeline tail to be connected.
+ *
+ * Return: true if this bridge does not need a next bridge to work, false
+ * otherwise
+ */
+bool drm_bridge_is_tail(struct drm_bridge *bridge)
+{
+ if (!(bridge->ops & DRM_BRIDGE_OP_IS_TAIL)) {
+ drm_warn_once(bridge->dev, "is_tail func not implemented by bridge %ps!",
+ bridge->funcs);
+ return false;
+ }
+
+ return bridge->funcs->is_tail(bridge);
+}
+EXPORT_SYMBOL(drm_bridge_is_tail);
+
/**
* DOC: bridge operations
*
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 4ba3a5deef9a..d783a6fb93a0 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -78,6 +78,19 @@ struct drm_bridge_funcs {
int (*attach)(struct drm_bridge *bridge, struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags);
+ /**
+ * @is_tail:
+ *
+ * Returns true if this is a tail bridge, i.e. it does not need a
+ * next bridge to work. E.g. a panel_bridge is a tail bridge, a
+ * DSI-to-LVDS bridge is not a tail bridge (no matter whether the
+ * next bridge is present or not).
+ *
+ * The @is_tail callback is optional but it is required if the
+ * bridge is part of a pipeline with hot-pluggable components.
+ */
+ bool (*is_tail)(struct drm_bridge *bridge);
+
/**
* @destroy:
*
@@ -1092,6 +1105,11 @@ enum drm_bridge_ops {
* &drm_bridge_funcs->hdmi_clear_spd_infoframe callbacks.
*/
DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME = BIT(10),
+ /**
+ * @DRM_BRIDGE_OP_IS_TAIL: The bridge implements the
+ * &drm_bridge_funcs->is_tail callback.
+ */
+ DRM_BRIDGE_OP_IS_TAIL = BIT(11),
};
/**
@@ -1323,6 +1341,7 @@ void drm_bridge_remove(struct drm_bridge *bridge);
int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
struct drm_bridge *previous,
enum drm_bridge_attach_flags flags);
+bool drm_bridge_is_tail(struct drm_bridge *bridge);
#ifdef CONFIG_OF
struct drm_bridge *of_drm_find_and_get_bridge(struct device_node *np);
--
2.54.0
^ permalink raw reply related
* [PATCH 26/37] drm: event-notifier: add mechanism to notify about hotplug events
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
In preparation for supporting DRM bridge hotplug, add an event notifier to
allow interested parties to be notified about events they need to react to.
For the initial implementation of bridge hotplug, two events are needed:
bridge detach (happening in drm_bridge.c) and MIPI device attach to MIPI
host (happening in drm_mipi_dsi.c).
For this reason implement the event notifier in a new common file that
event producers can easily use to send events.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
A different approach I have considered is keeping the event notifier in
drm_bridge.c (as in [0]) instead of a new centralized file. But then
another notifier would be needed in drm_mipi_dsi.c for the DSI attach
event. That would be particularly awkward because the designated component
to implement hotplug is the drm_bridge_connector, which would then need to
depend on DRM_MIPI_DSI even though it does nothing MIPI specific.
[0] https://lore.kernel.org/lkml/20250206-hotplug-drm-bridge-v6-12-9d6f2c9c3058@bootlin.com/
---
drivers/gpu/drm/Kconfig | 3 ++
drivers/gpu/drm/Makefile | 2 ++
drivers/gpu/drm/drm_event_notifier.c | 58 ++++++++++++++++++++++++++++++++++++
include/drm/drm_event_notifier.h | 36 ++++++++++++++++++++++
4 files changed, 99 insertions(+)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 8f5a8d3012e4..18eb33e0e5a0 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -33,6 +33,9 @@ endmenu
if DRM
+config DRM_EVENT_NOTIFIER
+ tristate
+
config DRM_MIPI_DBI
tristate
depends on DRM
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index e97faabcd783..18c9cceacdaa 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -96,6 +96,8 @@ drm-$(CONFIG_DRM_PANIC_SCREEN_QR_CODE) += drm_panic_qr.o
drm-$(CONFIG_DRM_RAS) += drm_ras.o drm_ras_nl.o drm_ras_genl_family.o
obj-$(CONFIG_DRM) += drm.o
+obj-$(CONFIG_DRM_EVENT_NOTIFIER) += drm_event_notifier.o
+
obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o
obj-$(CONFIG_DRM_PANEL_BACKLIGHT_QUIRKS) += drm_panel_backlight_quirks.o
diff --git a/drivers/gpu/drm/drm_event_notifier.c b/drivers/gpu/drm/drm_event_notifier.c
new file mode 100644
index 000000000000..76af4dd4cdb0
--- /dev/null
+++ b/drivers/gpu/drm/drm_event_notifier.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Internal event notifier for DRM drivers
+ *
+ * Copyright (C) 2026 GE HealthCare
+ * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
+ */
+
+#include <linux/module.h>
+#include <linux/notifier.h>
+
+#include <drm/drm_event_notifier.h>
+
+static BLOCKING_NOTIFIER_HEAD(drm_event_notifier);
+
+/**
+ * drm_event_notifier_register - Register to be notified of DRM events
+ * @nb: the notifier block to be registered
+ *
+ * @nb will be notified of events defined in &drm_event_notifier_event
+ *
+ * Returns 0 on success, %-EEXIST on error.
+ */
+int drm_event_notifier_register(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&drm_event_notifier, nb);
+}
+EXPORT_SYMBOL(drm_event_notifier_register);
+
+/**
+ * drm_event_notifier_unregister - Unregister from be notified of DRM events
+ * @nb: the notifier block to be unregistered
+ *
+ * @nb will stop being notified of events defined in &drm_event_notifier_event
+ *
+ * Returns zero on success or %-ENOENT on failure.
+ */
+int drm_event_notifier_unregister(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&drm_event_notifier, nb);
+}
+EXPORT_SYMBOL(drm_event_notifier_unregister);
+
+/**
+ * drm_event_notifier_notify - Emit an event to be notified to registered
+ * entities
+ * @event: event ID as defined in &drm_event_notifier_event
+ * @data: metadata associated to the event
+ */
+void drm_event_notifier_notify(unsigned long event, void *data)
+{
+ blocking_notifier_call_chain(&drm_event_notifier, event, data);
+}
+EXPORT_SYMBOL(drm_event_notifier_notify);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
+MODULE_DESCRIPTION("Notifier for DRM components addition/removal and attach/detach");
diff --git a/include/drm/drm_event_notifier.h b/include/drm/drm_event_notifier.h
new file mode 100644
index 000000000000..78afc014ec9a
--- /dev/null
+++ b/include/drm/drm_event_notifier.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Internal event notifier for DRM drivers
+ *
+ * Copyright (C) 2026 GE HealthCare
+ * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
+ */
+
+#ifndef _DRM_EVENT_NOTIFIER_H_
+#define _DRM_EVENT_NOTIFIER_H_
+
+/**
+ * enum drm_event_notifier_event - DRM bridge events
+ */
+enum drm_event_notifier_event {
+ /**
+ * @DRM_MIPI_DSI_ATTACHED: A MIPI DSI device has just been attached
+ * to its MIPI DSI host. @data is a pointer to the &struct
+ * mipi_dsi_device that has just attached.
+ */
+ DRM_MIPI_DSI_ATTACHED,
+ /**
+ * @DRM_BRIDGE_NOTIFY_DETACHED: A bridge has just been detached
+ * from the encoder bridge chain. Emitted at the end of
+ * drm_bridge_detach(), after removing the bridge from the encoder
+ * chain. @data is a pointer to the &struct drm_bridge that has
+ * just been detached.
+ */
+ DRM_BRIDGE_DETACHED,
+};
+
+int drm_event_notifier_register(struct notifier_block *nb);
+int drm_event_notifier_unregister(struct notifier_block *nb);
+void drm_event_notifier_notify(unsigned long event, void *data);
+
+#endif /* _DRM_EVENT_NOTIFIER_H_ */
--
2.54.0
^ permalink raw reply related
* [PATCH 28/37] drm/mipi-dsi: turn DRM_MIPI_DSI into a tristate
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
DRM_MIPI_DSI is currently a bool, but there's no reason to not be allowed
to build it as a loadable module.
Moreover being a bool prevents DRM_MIPI_DSI to depend on a tristate module
that is configured as 'm'.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 081660f3a0f2..3093d5f30724 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -43,7 +43,7 @@ config DRM_MIPI_DBI
select DRM_KMS_HELPER
config DRM_MIPI_DSI
- bool
+ tristate
depends on DRM
config DRM_KMS_HELPER
--
2.54.0
^ permalink raw reply related
* [PATCH 29/37] drm/mipi-dsi: notify about DSI attach
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
In preparation to support DRM bridge hotplug, let the drm_mipi_dsi code
emit an event when a DSI device is attached to the corresponding DSI host,
so that this event can trigger the actions needed to deconfigure the
pipeline and unregister the connector as appropriate.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/Kconfig | 1 +
drivers/gpu/drm/drm_mipi_dsi.c | 3 +++
2 files changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 3093d5f30724..288845b6621b 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -45,6 +45,7 @@ config DRM_MIPI_DBI
config DRM_MIPI_DSI
tristate
depends on DRM
+ select DRM_EVENT_NOTIFIER
config DRM_KMS_HELPER
tristate
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 3ac1dd5ad640..eaa474da4a51 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -34,6 +34,7 @@
#include <linux/slab.h>
#include <drm/display/drm_dsc.h>
+#include <drm/drm_event_notifier.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_print.h>
@@ -386,6 +387,8 @@ int mipi_dsi_attach(struct mipi_dsi_device *dsi)
dsi->attached = true;
+ drm_event_notifier_notify(DRM_MIPI_DSI_ATTACHED, dsi);
+
return 0;
}
EXPORT_SYMBOL(mipi_dsi_attach);
--
2.54.0
^ permalink raw reply related
* [PATCH 27/37] drm/bridge: notify about detached bridges
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
In preparation to support DRM bridge hotplug, let the drm_bridge code emit
an event when a bridge is detached, so that this event can trigger the
actions needed to deconfigure the pipeline and unregister the connector as
appropriate.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/Kconfig | 1 +
drivers/gpu/drm/drm_bridge.c | 4 ++++
2 files changed, 5 insertions(+)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 18eb33e0e5a0..081660f3a0f2 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -17,6 +17,7 @@ menuconfig DRM
# device and dmabuf fd. Let's make sure that is available for our userspace.
select KCMP
select VIDEO
+ select DRM_EVENT_NOTIFIER
help
Kernel-level support for the Direct Rendering Infrastructure (DRI)
introduced in XFree86 4.0. If you say Y here, you need to select
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index d45fb74ec8c2..c62d17e84d4f 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -34,6 +34,7 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
+#include <drm/drm_event_notifier.h>
#include <drm/drm_file.h>
#include <drm/drm_of.h>
#include <drm/drm_print.h>
@@ -638,6 +639,9 @@ void drm_bridge_detach(struct drm_bridge *bridge)
bridge->funcs->detach(bridge);
list_del_init(&bridge->chain_node);
+
+ drm_event_notifier_notify(DRM_BRIDGE_DETACHED, bridge);
+
bridge->dev = NULL;
drm_bridge_put(bridge);
}
--
2.54.0
^ permalink raw reply related
* [PATCH 24/37] drm/atomic: move drm_atomic_helper_disable_all() and drm_atomic_helper_shutdown() from drm_atomic_helper to drm_atomic
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
To support the upcoming DRM bridge hotplug, the drm_bridge.c code will need
to call drm_atomic_helper_shutdown() to disable the pipeline when the tail
bridges of the pipeline are removed without tearing down the card.
However this would create a module dependency loop between the drm module
(where drm_encoder is) and the drm_kms_helper module where
drm_atomic_helper_shutdown() function currently is and which already
depends on the drm module.
Solve by moving drm_atomic_helper_shutdown(), along with its callee
drm_atomic_helper_disable_all(), to drm_atomic which is in the drm
module. Use identical names except for dropping the "_helper" infix, and
make the original functions a deprecated wrapper to the new ones.
No changes to the functions body.
No functional changes except for moving the code to a different module.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_atomic.c | 115 ++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/drm_atomic_helper.c | 76 ++----------------------
include/drm/drm_atomic.h | 3 +
3 files changed, 124 insertions(+), 70 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 9b2009262c97..7d12c062c16d 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1993,6 +1993,121 @@ int __drm_atomic_helper_set_config(struct drm_mode_set *set,
}
EXPORT_SYMBOL(__drm_atomic_helper_set_config);
+/**
+ * drm_atomic_disable_all - disable all currently active outputs
+ * @dev: DRM device
+ * @ctx: lock acquisition context
+ *
+ * Loops through all connectors, finding those that aren't turned off and then
+ * turns them off by setting their DPMS mode to OFF and deactivating the CRTC
+ * that they are connected to.
+ *
+ * This is used for example in suspend/resume to disable all currently active
+ * functions when suspending. If you just want to shut down everything at e.g.
+ * driver unload, look at drm_atomic_helper_shutdown().
+ *
+ * Note that if callers haven't already acquired all modeset locks this might
+ * return -EDEADLK, which must be handled by calling drm_modeset_backoff().
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ *
+ * See also:
+ * drm_atomic_helper_suspend(), drm_atomic_helper_resume() and
+ * drm_atomic_helper_shutdown().
+ */
+int drm_atomic_disable_all(struct drm_device *dev,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ struct drm_atomic_commit *state;
+ struct drm_connector_state *conn_state;
+ struct drm_connector *conn;
+ struct drm_plane_state *plane_state;
+ struct drm_plane *plane;
+ struct drm_crtc_state *crtc_state;
+ struct drm_crtc *crtc;
+ int ret, i;
+
+ state = drm_atomic_commit_alloc(dev);
+ if (!state)
+ return -ENOMEM;
+
+ state->acquire_ctx = ctx;
+
+ drm_for_each_crtc(crtc, dev) {
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(crtc_state)) {
+ ret = PTR_ERR(crtc_state);
+ goto free;
+ }
+
+ crtc_state->active = false;
+
+ ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, NULL);
+ if (ret < 0)
+ goto free;
+
+ ret = drm_atomic_add_affected_planes(state, crtc);
+ if (ret < 0)
+ goto free;
+
+ ret = drm_atomic_add_affected_connectors(state, crtc);
+ if (ret < 0)
+ goto free;
+ }
+
+ for_each_new_connector_in_state(state, conn, conn_state, i) {
+ ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
+ if (ret < 0)
+ goto free;
+ }
+
+ for_each_new_plane_in_state(state, plane, plane_state, i) {
+ ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
+ if (ret < 0)
+ goto free;
+
+ drm_atomic_set_fb_for_plane(plane_state, NULL);
+ }
+
+ ret = drm_atomic_commit(state);
+free:
+ drm_atomic_commit_put(state);
+ return ret;
+}
+EXPORT_SYMBOL(drm_atomic_disable_all);
+
+/**
+ * drm_atomic_shutdown - shutdown all CRTC
+ * @dev: DRM device
+ *
+ * This shuts down all CRTC, which is useful for driver unloading. Shutdown on
+ * suspend should instead be handled with drm_atomic_helper_suspend(), since
+ * that also takes a snapshot of the modeset state to be restored on resume.
+ *
+ * This is just a convenience wrapper around drm_atomic_helper_disable_all(),
+ * and it is the atomic version of drm_helper_force_disable_all().
+ */
+void drm_atomic_shutdown(struct drm_device *dev)
+{
+ struct drm_modeset_acquire_ctx ctx;
+ int ret;
+
+ if (dev == NULL)
+ return;
+
+ DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
+
+ ret = drm_atomic_disable_all(dev, &ctx);
+ if (ret)
+ drm_err(dev,
+ "Disabling all crtc's during unload failed with %i\n",
+ ret);
+
+ DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
+}
+EXPORT_SYMBOL(drm_atomic_shutdown);
+
static void drm_atomic_private_obj_print_state(struct drm_printer *p,
const struct drm_private_state *state)
{
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 51f39edc31ed..afedfab9b568 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -3533,6 +3533,8 @@ EXPORT_SYMBOL(drm_atomic_helper_set_config);
* @dev: DRM device
* @ctx: lock acquisition context
*
+ * Deprecated wrapper to drm_atomic_disable_all().
+ *
* Loops through all connectors, finding those that aren't turned off and then
* turns them off by setting their DPMS mode to OFF and deactivating the CRTC
* that they are connected to.
@@ -3554,61 +3556,7 @@ EXPORT_SYMBOL(drm_atomic_helper_set_config);
int drm_atomic_helper_disable_all(struct drm_device *dev,
struct drm_modeset_acquire_ctx *ctx)
{
- struct drm_atomic_commit *state;
- struct drm_connector_state *conn_state;
- struct drm_connector *conn;
- struct drm_plane_state *plane_state;
- struct drm_plane *plane;
- struct drm_crtc_state *crtc_state;
- struct drm_crtc *crtc;
- int ret, i;
-
- state = drm_atomic_commit_alloc(dev);
- if (!state)
- return -ENOMEM;
-
- state->acquire_ctx = ctx;
-
- drm_for_each_crtc(crtc, dev) {
- crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(crtc_state)) {
- ret = PTR_ERR(crtc_state);
- goto free;
- }
-
- crtc_state->active = false;
-
- ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, NULL);
- if (ret < 0)
- goto free;
-
- ret = drm_atomic_add_affected_planes(state, crtc);
- if (ret < 0)
- goto free;
-
- ret = drm_atomic_add_affected_connectors(state, crtc);
- if (ret < 0)
- goto free;
- }
-
- for_each_new_connector_in_state(state, conn, conn_state, i) {
- ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
- if (ret < 0)
- goto free;
- }
-
- for_each_new_plane_in_state(state, plane, plane_state, i) {
- ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
- if (ret < 0)
- goto free;
-
- drm_atomic_set_fb_for_plane(plane_state, NULL);
- }
-
- ret = drm_atomic_commit(state);
-free:
- drm_atomic_commit_put(state);
- return ret;
+ return drm_atomic_disable_all(dev, ctx);
}
EXPORT_SYMBOL(drm_atomic_helper_disable_all);
@@ -3664,6 +3612,8 @@ EXPORT_SYMBOL(drm_atomic_helper_reset_crtc);
* drm_atomic_helper_shutdown - shutdown all CRTC
* @dev: DRM device
*
+ * Deprecated wrapper to drm_atomic_shutdown().
+ *
* This shuts down all CRTC, which is useful for driver unloading. Shutdown on
* suspend should instead be handled with drm_atomic_helper_suspend(), since
* that also takes a snapshot of the modeset state to be restored on resume.
@@ -3673,21 +3623,7 @@ EXPORT_SYMBOL(drm_atomic_helper_reset_crtc);
*/
void drm_atomic_helper_shutdown(struct drm_device *dev)
{
- struct drm_modeset_acquire_ctx ctx;
- int ret;
-
- if (dev == NULL)
- return;
-
- DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
-
- ret = drm_atomic_helper_disable_all(dev, &ctx);
- if (ret)
- drm_err(dev,
- "Disabling all crtc's during unload failed with %i\n",
- ret);
-
- DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
+ return drm_atomic_shutdown(dev);
}
EXPORT_SYMBOL(drm_atomic_helper_shutdown);
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 1a80a8cdf269..9eb3d1bfa084 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -1373,5 +1373,8 @@ drm_atomic_get_old_bridge_state(const struct drm_atomic_commit *state,
struct drm_bridge_state *
drm_atomic_get_new_bridge_state(const struct drm_atomic_commit *state,
struct drm_bridge *bridge);
+int drm_atomic_disable_all(struct drm_device *dev,
+ struct drm_modeset_acquire_ctx *ctx);
+void drm_atomic_shutdown(struct drm_device *dev);
#endif /* DRM_ATOMIC_H_ */
--
2.54.0
^ permalink raw reply related
* [PATCH 25/37] drm/bridge: shutdown and cleanup on bridge unplug
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
With the upcoming support for DRM bridge hot(un)plugging, bridges can be
removed at any time withotu tearing down the entire card. When this
happens, shutdown the pipeline and detach from the encoder chain the bridge
being removed along with all the following ones.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_bridge.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 7f2d1a81d730..d45fb74ec8c2 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -462,9 +462,17 @@ EXPORT_SYMBOL(devm_drm_bridge_add);
* it won't be found by users via of_drm_find_and_get_bridge(), and add it
* to the lingering bridge list, to keep track of it until its allocated
* memory is eventually freed.
+ *
+ * If the bridge is attached, also disable the active output and detach
+ * this bridge and the following ones.
*/
void drm_bridge_remove(struct drm_bridge *bridge)
{
+ if (bridge->encoder) {
+ drm_atomic_shutdown(bridge->dev);
+ drm_encoder_cleanup_from(bridge->encoder, bridge);
+ }
+
mutex_lock(&bridge_lock);
list_move_tail(&bridge->list, &bridge_lingering_list);
mutex_unlock(&bridge_lock);
--
2.54.0
^ permalink raw reply related
* Re: [PATCH] arm64/daifflags: Make local_daif_*() helpers __always_inline
From: Will Deacon @ 2026-05-19 10:39 UTC (permalink / raw)
To: Leonardo Bras
Cc: mark.rutland, catalin.marinas, kernel-team, leitao, leo.yan,
linux-arm-kernel, linux-kernel, palmer, paulmck, puranjay, rmikey,
usama.arif
In-Reply-To: <20260427140125.1773574-2-leo.bras@arm.com>
On Mon, Apr 27, 2026 at 03:01:26PM +0100, Leonardo Bras wrote:
> Make sure those helpers are always inlined and instrumentation safe.
>
> Signed-off-by: Leonardo Bras <leo.bras@arm.com>
> ---
> arch/arm64/include/asm/daifflags.h | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
b4 really doesn't like this patch being a reply to another one, so I'm
just replying to say that I've picked it up manually.
Cheers,
Will
^ permalink raw reply
* [PATCH 22/37] drm/bridge: initialize chain_node list head on detach and attach errors
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
When a bridge is detached it is removed from the encoder bridge_chain list,
but the bridge::chain_node list head is not cleared. This is going to be
problematic with the upcoming hotplug bridge support because if a bridge is
detached from the encoder chain but not yet removed, when later detaching
it the encoder code may think it is still attached, thus trying to detach
it twice.
Avoid this by clearing the list head on detach, so there's a clear and
simple way to know when a bridge is not attached anymore.
Do the same in the error management code in drm_bridge_attach(), so that
chain_node is always empty [as in list_empty()] when it is not
(yet|anymore) in the bridge chain.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_bridge.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 657f6609d7b1..7f2d1a81d730 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -598,7 +598,7 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
bridge->dev = NULL;
bridge->encoder = NULL;
mutex_lock(&encoder->bridge_chain_mutex);
- list_del(&bridge->chain_node);
+ list_del_init(&bridge->chain_node);
mutex_unlock(&encoder->bridge_chain_mutex);
if (ret != -EPROBE_DEFER)
@@ -629,7 +629,7 @@ void drm_bridge_detach(struct drm_bridge *bridge)
if (bridge->funcs->detach)
bridge->funcs->detach(bridge);
- list_del(&bridge->chain_node);
+ list_del_init(&bridge->chain_node);
bridge->dev = NULL;
drm_bridge_put(bridge);
}
--
2.54.0
^ permalink raw reply related
* [PATCH 23/37] drm/encoder: add drm_encoder_cleanup_from()
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
Supporting hardware whose final part of the DRM pipeline can be physically
removed requires the ability to detach all bridges from a given point to
the end of the pipeline.
Introduce a variant of drm_encoder_cleanup() for this.
Take care to not try detaching non-attached bridges. This is needed because
when two or more bridges are removed not in the backwards order,
drm_encoder_cleanup_from() is called more than once for bridges closer to
the panel.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
Note: in theory drm_encoder_cleanup() is now a superset of
drm_encoder_cleanup_from() and may be simplified to just call
drm_encoder_cleanup_from() and then do the extra actions. However the
common code is subtly different in terms of locking and checks, so this
would complicate the code in this patch and has thus been kept separate for
the time being to make reviewing sompler. Reimplementing
drm_encoder_cleanup() by using drm_encoder_cleanup_from() cvacn be done
later on.
A much simpler and now obsolete version of this patch (missing locking and
checks) previously appeared in
https://lore.kernel.org/lkml/20250206-hotplug-drm-bridge-v6-13-9d6f2c9c3058@bootlin.com/
---
drivers/gpu/drm/drm_encoder.c | 38 ++++++++++++++++++++++++++++++++++++++
include/drm/drm_encoder.h | 1 +
2 files changed, 39 insertions(+)
diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
index 0d5dbed06db4..40ece477b302 100644
--- a/drivers/gpu/drm/drm_encoder.c
+++ b/drivers/gpu/drm/drm_encoder.c
@@ -179,6 +179,44 @@ int drm_encoder_init(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_encoder_init);
+/**
+ * drm_encoder_cleanup_from - remove a given bridge and all the following
+ * @encoder: encoder whole list of bridges shall be pruned
+ * @bridge: first bridge to remove
+ *
+ * Removes from an encoder all the bridges starting with a given bridge
+ * and until the end of the chain.
+ *
+ * Does nothing if the bridge is not attached to an encoder chain.
+ *
+ * This should not be used in "normal" DRM pipelines. It is only useful for
+ * devices whose final part of the DRM chain can be physically removed and
+ * later reconnected (possibly with different hardware).
+ */
+void drm_encoder_cleanup_from(struct drm_encoder *encoder, struct drm_bridge *bridge)
+{
+ struct drm_bridge *next;
+ LIST_HEAD(tmplist);
+
+ /*
+ * We need the bridge_chain_mutex to modify the chain, but
+ * drm_bridge_detach() will call DRM_MODESET_LOCK_ALL_BEGIN() (in
+ * drm_modeset_lock_fini()), resulting in a possible ABBA circular
+ * deadlock. Avoid it by first moving all the bridges to a
+ * temporary list holding the lock, and then calling
+ * drm_bridge_detach() without the lock.
+ */
+ mutex_lock(&encoder->bridge_chain_mutex);
+ if (!list_empty(&bridge->chain_node))
+ list_for_each_entry_safe_from(bridge, next, &encoder->bridge_chain, chain_node)
+ list_move_tail(&bridge->chain_node, &tmplist);
+ mutex_unlock(&encoder->bridge_chain_mutex);
+
+ while (!list_empty(&tmplist))
+ drm_bridge_detach(list_first_entry(&tmplist, struct drm_bridge, chain_node));
+}
+EXPORT_SYMBOL(drm_encoder_cleanup_from);
+
/**
* drm_encoder_cleanup - cleans up an initialised encoder
* @encoder: encoder to cleanup
diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h
index eded7c34481a..d2a59f95692f 100644
--- a/include/drm/drm_encoder.h
+++ b/include/drm/drm_encoder.h
@@ -324,6 +324,7 @@ static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev,
}
void drm_encoder_cleanup(struct drm_encoder *encoder);
+void drm_encoder_cleanup_from(struct drm_encoder *encoder, struct drm_bridge *bridge);
/**
* drm_for_each_encoder_mask - iterate over encoders specified by bitmask
--
2.54.0
^ permalink raw reply related
* [PATCH 21/37] drm/bridge: initialize chain_node list head on allocation
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
In preparation to add a check to detect whether a bridge is not yet
attached, ensure the chain_node list_head is always empty [as in
list_empty()] since it is allocated, until it is attached.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_bridge.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 687b36eea0c7..657f6609d7b1 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -378,6 +378,7 @@ void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
return ERR_PTR(-ENOMEM);
bridge = container + offset;
+ INIT_LIST_HEAD(&bridge->chain_node);
INIT_LIST_HEAD(&bridge->list);
bridge->container = container;
bridge->funcs = funcs;
--
2.54.0
^ permalink raw reply related
* [PATCH 19/37] drm/bridge: samsung-dsim: move drm_bridge_add() call to probe
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
This bridge driver calls drm_bridge_add() in the DSI host .attach callback
instead of in the probe function.
This works for current use cases but is problematic for supporting hotplug
of DRM bridges. The problematic case is when this DSI host is always
present while its DSI device is hot-pluggable. In such case with the
current code the DRM card will not be populated until after the DSI device
attaches to the host, which could happen a very long time after booting, or
even not happen at all.
The reason is that the previous pipeline component (the encoder in this
case) when probing cannot find the samsung-dsim bridge. What happens is:
[1 and 2 can happen in any order, same result]
1) samsung-dsim probes (does not drm_bridge_add() itself)
2) The lcdif starts probing multiple times, but
lcdif_probe
-> lcdif_load
-> lcdif_attach_bridge
-> devm_drm_of_get_bridge() returns -EPROBE_DEFER because
the samsung-dsim is not in the global bridge_list
(deferred probe pending: imx-lcdif: Cannot connect bridge)
The samsung-dsim will not drm_bridge_add() itself until a DSI device will
try to mipi_dsi_attach() to the DSI Host, which can happen arbitratily late
on hot-pluggable hardware.
As a preliminary step to supporting hotplug move drm_bridge_add() at probe
time, so that the samsung-dsim DSI host bridge is available during boot,
even without a connected DSI device. This results in:
1) samsung-dsim probes (and adds to drm_bridge_add() itself)
2) The lcdif starts probing multiple times, but
lcdif_probe
-> lcdif_load
-> lcdif_attach_bridge
-> devm_drm_of_get_bridge() --> OK, returns samsung-dsim ptr
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/samsung-dsim.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
index 2af287221e22..cefb20ec68ad 100644
--- a/drivers/gpu/drm/bridge/samsung-dsim.c
+++ b/drivers/gpu/drm/bridge/samsung-dsim.c
@@ -1980,8 +1980,6 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
mipi_dsi_pixel_format_to_bpp(device->format),
device->mode_flags);
- drm_bridge_add(&dsi->bridge);
-
/*
* This is a temporary solution and should be made by more generic way.
*
@@ -2014,7 +2012,6 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO))
samsung_dsim_unregister_te_irq(dsi);
err_remove_panel_bridge:
- drm_bridge_remove(&dsi->bridge);
if (dsi->panel_bridge_added) {
drm_panel_bridge_remove(next_bridge);
dsi->panel_bridge_added = false;
@@ -2040,8 +2037,6 @@ static int samsung_dsim_host_detach(struct mipi_dsi_host *host,
samsung_dsim_unregister_te_irq(dsi);
- drm_bridge_remove(&dsi->bridge);
-
return 0;
}
@@ -2242,6 +2237,8 @@ int samsung_dsim_probe(struct platform_device *pdev)
goto err_disable_runtime;
}
+ drm_bridge_add(&dsi->bridge);
+
return 0;
err_disable_runtime:
@@ -2255,6 +2252,8 @@ void samsung_dsim_remove(struct platform_device *pdev)
{
struct samsung_dsim *dsi = platform_get_drvdata(pdev);
+ drm_bridge_remove(&dsi->bridge);
+
pm_runtime_disable(&pdev->dev);
if (dsi->plat_data->host_ops && dsi->plat_data->host_ops->unregister_host)
--
2.54.0
^ permalink raw reply related
* [PATCH 20/37] drm/bridge: samsung-dsim: attach: return -EPROBE_DEFER is next bridge not yet available
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
When samsung_dsim_attach() is called but the next_bridge is not (yet)
known, it calls drm_bridge_attach() unconditionally. In turn
drm_bridge_attach() will return -EINVAL because the bridge pointer in
NULL. -EINVAL is propagated transparently to the caller, resulting in the
whole attach operation to fail.
This is fine for current use cases, but not when introducing for bridge
hotplug, which implies the next bridge could be hot-plugged later on.
In preparation to support bridge hotplug, consider the absence of a
next_bridge an -EPROBE_DEFER, not a hard error.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/samsung-dsim.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
index cefb20ec68ad..3c70a45c5dce 100644
--- a/drivers/gpu/drm/bridge/samsung-dsim.c
+++ b/drivers/gpu/drm/bridge/samsung-dsim.c
@@ -1828,6 +1828,9 @@ static int samsung_dsim_attach(struct drm_bridge *bridge,
{
struct samsung_dsim *dsi = bridge_to_dsi(bridge);
+ if (!dsi->bridge.next_bridge)
+ return -EPROBE_DEFER;
+
return drm_bridge_attach(encoder, dsi->bridge.next_bridge, bridge,
flags);
}
--
2.54.0
^ permalink raw reply related
* [PATCH 16/37] drm/display: bridge-connector: move per-connector fields to the dynamic connector
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
The various bridge pointers and the hdmi_funcs pointer are per-connector
specific. In other words, when introducing bridge hotplug and the
dynamically-created connector, they will have to be set per each created
connector, possibly with different values from any previously created
connector.
Those pointers are currently in struct drm_bridge_connector, which will
persist across multiple connector creations and destructions. This means
when a dynamic connector is created before the previous one is destroyed,
these fields would be overwritten by the new connector but the old one will
still reference them. This would lead to possible put of incorrect briges,
resulting in use-after-free and leaks.
In preparation for bridge hotplug, move all those fields from struct
drm_bridge_connector to struct drm_bridge_connector_dynconn, so they have
the same allocation and lifetime of the connector they refer to.
Update the many locations where these fields are used.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 370 +++++++++++++------------
1 file changed, 187 insertions(+), 183 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index b4e2c8f1d32d..bd421b6beb7c 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -67,26 +67,6 @@ struct drm_bridge_connector_dynconn {
* @connector: the drm_connector exposed to user space
*/
struct drm_connector connector;
-};
-
-/**
- * struct drm_bridge_connector - A connector backed by a chain of bridges
- */
-struct drm_bridge_connector {
- /**
- * @drm: The DRM device we belong to
- */
- struct drm_device *drm;
- /**
- * @encoder:
- *
- * The encoder at the start of the bridges chain.
- */
- struct drm_encoder *encoder;
- /**
- * @dynconn: The DRM connector added by the drm_bridge_connector
- */
- struct drm_bridge_connector_dynconn *dynconn;
/**
* @bridge_edid:
*
@@ -154,24 +134,47 @@ struct drm_bridge_connector {
struct drm_connector_hdmi_funcs hdmi_funcs;
};
-static struct drm_bridge_connector *to_drm_bridge_connector(const struct drm_connector *connector)
+/**
+ * struct drm_bridge_connector - A connector backed by a chain of bridges
+ */
+struct drm_bridge_connector {
+ /**
+ * @drm: The DRM device we belong to
+ */
+ struct drm_device *drm;
+ /**
+ * @encoder:
+ *
+ * The encoder at the start of the bridges chain.
+ */
+ struct drm_encoder *encoder;
+ /**
+ * @dynconn: The DRM connector added by the drm_bridge_connector
+ */
+ struct drm_bridge_connector_dynconn *dynconn;
+};
+
+static struct drm_bridge_connector_dynconn *
+to_drm_bridge_connector_dynconn(const struct drm_connector *connector)
{
- const struct drm_bridge_connector_dynconn *dynconn =
- container_of(connector, struct drm_bridge_connector_dynconn, connector);
+ return container_of(connector, struct drm_bridge_connector_dynconn, connector);
+}
- return dynconn->bridge_connector;
+static struct drm_bridge_connector *to_drm_bridge_connector(const struct drm_connector *connector)
+{
+ return to_drm_bridge_connector_dynconn(connector)->bridge_connector;
}
-static void drm_bridge_connector_put_bridges(struct drm_bridge_connector *bridge_connector)
+static void drm_bridge_connector_put_bridges(struct drm_bridge_connector_dynconn *dynconn)
{
- drm_bridge_put(bridge_connector->bridge_edid);
- drm_bridge_put(bridge_connector->bridge_hpd);
- drm_bridge_put(bridge_connector->bridge_detect);
- drm_bridge_put(bridge_connector->bridge_modes);
- drm_bridge_put(bridge_connector->bridge_hdmi);
- drm_bridge_put(bridge_connector->bridge_hdmi_audio);
- drm_bridge_put(bridge_connector->bridge_dp_audio);
- drm_bridge_put(bridge_connector->bridge_hdmi_cec);
+ drm_bridge_put(dynconn->bridge_edid);
+ drm_bridge_put(dynconn->bridge_hpd);
+ drm_bridge_put(dynconn->bridge_detect);
+ drm_bridge_put(dynconn->bridge_modes);
+ drm_bridge_put(dynconn->bridge_hdmi);
+ drm_bridge_put(dynconn->bridge_hdmi_audio);
+ drm_bridge_put(dynconn->bridge_dp_audio);
+ drm_bridge_put(dynconn->bridge_hdmi_cec);
}
/* -----------------------------------------------------------------------------
@@ -225,7 +228,9 @@ static void drm_bridge_connector_enable_hpd(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
- struct drm_bridge *hpd = bridge_connector->bridge_hpd;
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
+ struct drm_bridge *hpd = dynconn->bridge_hpd;
if (hpd)
drm_bridge_hpd_enable(hpd, drm_bridge_connector_hpd_cb,
@@ -234,9 +239,9 @@ static void drm_bridge_connector_enable_hpd(struct drm_connector *connector)
static void drm_bridge_connector_disable_hpd(struct drm_connector *connector)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
- struct drm_bridge *hpd = bridge_connector->bridge_hpd;
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
+ struct drm_bridge *hpd = dynconn->bridge_hpd;
if (hpd)
drm_bridge_hpd_disable(hpd);
@@ -249,10 +254,10 @@ static void drm_bridge_connector_disable_hpd(struct drm_connector *connector)
static enum drm_connector_status
drm_bridge_connector_detect(struct drm_connector *connector, bool force)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
- struct drm_bridge *detect = bridge_connector->bridge_detect;
- struct drm_bridge *hdmi = bridge_connector->bridge_hdmi;
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
+ struct drm_bridge *detect = dynconn->bridge_detect;
+ struct drm_bridge *hdmi = dynconn->bridge_hdmi;
enum drm_connector_status status;
if (detect) {
@@ -281,9 +286,9 @@ drm_bridge_connector_detect(struct drm_connector *connector, bool force)
static void drm_bridge_connector_force(struct drm_connector *connector)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
- struct drm_bridge *hdmi = bridge_connector->bridge_hdmi;
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
+ struct drm_bridge *hdmi = dynconn->bridge_hdmi;
if (hdmi)
drm_atomic_helper_connector_hdmi_force(connector);
@@ -305,11 +310,11 @@ static void drm_bridge_connector_debugfs_init(struct drm_connector *connector,
static void drm_bridge_connector_reset(struct drm_connector *connector)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
drm_atomic_helper_connector_reset(connector);
- if (bridge_connector->bridge_hdmi)
+ if (dynconn->bridge_hdmi)
__drm_atomic_helper_connector_hdmi_reset(connector,
connector->state);
}
@@ -320,7 +325,7 @@ static void drm_bridge_connector_dynconn_destroy(struct drm_connector *connector
container_of(connector, struct drm_bridge_connector_dynconn, connector);
drm_connector_cleanup(connector);
- drm_bridge_connector_put_bridges(dynconn->bridge_connector);
+ drm_bridge_connector_put_bridges(dynconn);
kfree(dynconn);
}
@@ -370,15 +375,15 @@ static int drm_bridge_connector_get_modes_edid(struct drm_connector *connector,
static int drm_bridge_connector_get_modes(struct drm_connector *connector)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
/*
* If there is a HDMI bridge, EDID has been updated as a part of
* the .detect(). Just update the modes here.
*/
- bridge = bridge_connector->bridge_hdmi;
+ bridge = dynconn->bridge_hdmi;
if (bridge)
return drm_edid_connector_add_modes(connector);
@@ -386,7 +391,7 @@ static int drm_bridge_connector_get_modes(struct drm_connector *connector)
* If display exposes EDID, then we parse that in the normal way to
* build table of supported modes.
*/
- bridge = bridge_connector->bridge_edid;
+ bridge = dynconn->bridge_edid;
if (bridge)
return drm_bridge_connector_get_modes_edid(connector, bridge);
@@ -394,7 +399,7 @@ static int drm_bridge_connector_get_modes(struct drm_connector *connector)
* Otherwise if the display pipeline reports modes (e.g. with a fixed
* resolution panel or an analog TV output), query it.
*/
- bridge = bridge_connector->bridge_modes;
+ bridge = dynconn->bridge_modes;
if (bridge)
return bridge->funcs->get_modes(bridge, connector);
@@ -410,10 +415,10 @@ static enum drm_mode_status
drm_bridge_connector_mode_valid(struct drm_connector *connector,
const struct drm_display_mode *mode)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
- if (bridge_connector->bridge_hdmi)
+ if (dynconn->bridge_hdmi)
return drm_hdmi_connector_mode_valid(connector, mode);
return MODE_OK;
@@ -422,10 +427,10 @@ drm_bridge_connector_mode_valid(struct drm_connector *connector,
static int drm_bridge_connector_atomic_check(struct drm_connector *connector,
struct drm_atomic_commit *state)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
- if (bridge_connector->bridge_hdmi)
+ if (dynconn->bridge_hdmi)
return drm_atomic_helper_connector_hdmi_check(connector, state);
return 0;
@@ -444,11 +449,11 @@ 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_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi;
+ bridge = dynconn->bridge_hdmi;
if (!bridge)
return MODE_ERROR;
@@ -460,11 +465,11 @@ drm_bridge_connector_tmds_char_rate_valid(const struct drm_connector *connector,
static int drm_bridge_connector_clear_avi_infoframe(struct drm_connector *connector)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi;
+ bridge = dynconn->bridge_hdmi;
if (!bridge)
return -EINVAL;
@@ -474,11 +479,11 @@ static int drm_bridge_connector_clear_avi_infoframe(struct drm_connector *connec
static int drm_bridge_connector_write_avi_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi;
+ bridge = dynconn->bridge_hdmi;
if (!bridge)
return -EINVAL;
@@ -487,11 +492,11 @@ static int drm_bridge_connector_write_avi_infoframe(struct drm_connector *connec
static int drm_bridge_connector_clear_hdmi_infoframe(struct drm_connector *connector)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi;
+ bridge = dynconn->bridge_hdmi;
if (!bridge)
return -EINVAL;
@@ -501,11 +506,11 @@ static int drm_bridge_connector_clear_hdmi_infoframe(struct drm_connector *conne
static int drm_bridge_connector_write_hdmi_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi;
+ bridge = dynconn->bridge_hdmi;
if (!bridge)
return -EINVAL;
@@ -514,11 +519,11 @@ static int drm_bridge_connector_write_hdmi_infoframe(struct drm_connector *conne
static int drm_bridge_connector_clear_audio_infoframe(struct drm_connector *connector)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi;
+ bridge = dynconn->bridge_hdmi;
if (!bridge)
return -EINVAL;
@@ -528,11 +533,11 @@ static int drm_bridge_connector_clear_audio_infoframe(struct drm_connector *conn
static int drm_bridge_connector_write_audio_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi;
+ bridge = dynconn->bridge_hdmi;
if (!bridge)
return -EINVAL;
@@ -541,11 +546,11 @@ static int drm_bridge_connector_write_audio_infoframe(struct drm_connector *conn
static int drm_bridge_connector_clear_hdr_drm_infoframe(struct drm_connector *connector)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi;
+ bridge = dynconn->bridge_hdmi;
if (!bridge)
return -EINVAL;
@@ -555,11 +560,11 @@ static int drm_bridge_connector_clear_hdr_drm_infoframe(struct drm_connector *co
static int drm_bridge_connector_write_hdr_drm_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi;
+ bridge = dynconn->bridge_hdmi;
if (!bridge)
return -EINVAL;
@@ -568,11 +573,11 @@ static int drm_bridge_connector_write_hdr_drm_infoframe(struct drm_connector *co
static int drm_bridge_connector_clear_spd_infoframe(struct drm_connector *connector)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi;
+ bridge = dynconn->bridge_hdmi;
if (!bridge)
return -EINVAL;
@@ -582,11 +587,11 @@ static int drm_bridge_connector_clear_spd_infoframe(struct drm_connector *connec
static int drm_bridge_connector_write_spd_infoframe(struct drm_connector *connector,
const u8 *buffer, size_t len)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi;
+ bridge = dynconn->bridge_hdmi;
if (!bridge)
return -EINVAL;
@@ -596,11 +601,11 @@ static int drm_bridge_connector_write_spd_infoframe(struct drm_connector *connec
static const struct drm_edid *
drm_bridge_connector_read_edid(struct drm_connector *connector)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_edid;
+ bridge = dynconn->bridge_edid;
if (!bridge)
return NULL;
@@ -638,12 +643,12 @@ static const struct drm_connector_infoframe_funcs drm_bridge_connector_hdmi_spd_
static int drm_bridge_connector_audio_startup(struct drm_connector *connector)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- if (bridge_connector->bridge_hdmi_audio) {
- bridge = bridge_connector->bridge_hdmi_audio;
+ if (dynconn->bridge_hdmi_audio) {
+ bridge = dynconn->bridge_hdmi_audio;
if (!bridge->funcs->hdmi_audio_startup)
return 0;
@@ -651,8 +656,8 @@ static int drm_bridge_connector_audio_startup(struct drm_connector *connector)
return bridge->funcs->hdmi_audio_startup(bridge, connector);
}
- if (bridge_connector->bridge_dp_audio) {
- bridge = bridge_connector->bridge_dp_audio;
+ if (dynconn->bridge_dp_audio) {
+ bridge = dynconn->bridge_dp_audio;
if (!bridge->funcs->dp_audio_startup)
return 0;
@@ -667,18 +672,18 @@ static int drm_bridge_connector_audio_prepare(struct drm_connector *connector,
struct hdmi_codec_daifmt *fmt,
struct hdmi_codec_params *hparms)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- if (bridge_connector->bridge_hdmi_audio) {
- bridge = bridge_connector->bridge_hdmi_audio;
+ if (dynconn->bridge_hdmi_audio) {
+ bridge = dynconn->bridge_hdmi_audio;
return bridge->funcs->hdmi_audio_prepare(bridge, connector, fmt, hparms);
}
- if (bridge_connector->bridge_dp_audio) {
- bridge = bridge_connector->bridge_dp_audio;
+ if (dynconn->bridge_dp_audio) {
+ bridge = dynconn->bridge_dp_audio;
return bridge->funcs->dp_audio_prepare(bridge, connector, fmt, hparms);
}
@@ -688,17 +693,17 @@ static int drm_bridge_connector_audio_prepare(struct drm_connector *connector,
static void drm_bridge_connector_audio_shutdown(struct drm_connector *connector)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- if (bridge_connector->bridge_hdmi_audio) {
- bridge = bridge_connector->bridge_hdmi_audio;
+ if (dynconn->bridge_hdmi_audio) {
+ bridge = dynconn->bridge_hdmi_audio;
bridge->funcs->hdmi_audio_shutdown(bridge, connector);
}
- if (bridge_connector->bridge_dp_audio) {
- bridge = bridge_connector->bridge_dp_audio;
+ if (dynconn->bridge_dp_audio) {
+ bridge = dynconn->bridge_dp_audio;
bridge->funcs->dp_audio_shutdown(bridge, connector);
}
}
@@ -706,12 +711,12 @@ static void drm_bridge_connector_audio_shutdown(struct drm_connector *connector)
static int drm_bridge_connector_audio_mute_stream(struct drm_connector *connector,
bool enable, int direction)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- if (bridge_connector->bridge_hdmi_audio) {
- bridge = bridge_connector->bridge_hdmi_audio;
+ if (dynconn->bridge_hdmi_audio) {
+ bridge = dynconn->bridge_hdmi_audio;
if (!bridge->funcs->hdmi_audio_mute_stream)
return -ENOTSUPP;
@@ -720,8 +725,8 @@ static int drm_bridge_connector_audio_mute_stream(struct drm_connector *connecto
enable, direction);
}
- if (bridge_connector->bridge_dp_audio) {
- bridge = bridge_connector->bridge_dp_audio;
+ if (dynconn->bridge_dp_audio) {
+ bridge = dynconn->bridge_dp_audio;
if (!bridge->funcs->dp_audio_mute_stream)
return -ENOTSUPP;
@@ -742,22 +747,22 @@ static const struct drm_connector_hdmi_audio_funcs drm_bridge_connector_hdmi_aud
static int drm_bridge_connector_hdmi_cec_enable(struct drm_connector *connector, bool enable)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi_cec;
+ bridge = dynconn->bridge_hdmi_cec;
return bridge->funcs->hdmi_cec_enable(bridge, enable);
}
static int drm_bridge_connector_hdmi_cec_log_addr(struct drm_connector *connector, u8 logical_addr)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi_cec;
+ bridge = dynconn->bridge_hdmi_cec;
return bridge->funcs->hdmi_cec_log_addr(bridge, logical_addr);
}
@@ -767,11 +772,11 @@ static int drm_bridge_connector_hdmi_cec_transmit(struct drm_connector *connecto
u32 signal_free_time,
struct cec_msg *msg)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi_cec;
+ bridge = dynconn->bridge_hdmi_cec;
return bridge->funcs->hdmi_cec_transmit(bridge, attempts,
signal_free_time,
@@ -780,11 +785,11 @@ static int drm_bridge_connector_hdmi_cec_transmit(struct drm_connector *connecto
static int drm_bridge_connector_hdmi_cec_init(struct drm_connector *connector)
{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
+ struct drm_bridge_connector_dynconn *dynconn =
+ to_drm_bridge_connector_dynconn(connector);
struct drm_bridge *bridge;
- bridge = bridge_connector->bridge_hdmi_cec;
+ bridge = dynconn->bridge_hdmi_cec;
if (!bridge->funcs->hdmi_cec_init)
return 0;
@@ -821,7 +826,6 @@ static int drm_bridge_connector_get_bridges(struct drm_bridge_connector_dynconn
struct drm_bridge **panel_bridge,
bool *support_hdcp)
{
- struct drm_bridge_connector *bridge_connector = dynconn->bridge_connector;
struct drm_connector *connector = &dynconn->connector;
/*
@@ -831,7 +835,7 @@ static int drm_bridge_connector_get_bridges(struct drm_bridge_connector_dynconn
connector->ycbcr_420_allowed = true;
*connector_type = DRM_MODE_CONNECTOR_Unknown;
- drm_for_each_bridge_in_chain_scoped(bridge_connector->encoder, bridge) {
+ drm_for_each_bridge_in_chain_scoped(dynconn->bridge_connector->encoder, bridge) {
if (!bridge->interlace_allowed)
connector->interlace_allowed = false;
if (!bridge->ycbcr_420_allowed)
@@ -841,26 +845,26 @@ static int drm_bridge_connector_get_bridges(struct drm_bridge_connector_dynconn
* Ensure the last bridge declares OP_EDID or OP_MODES or both.
*/
if (bridge->ops & DRM_BRIDGE_OP_EDID || bridge->ops & DRM_BRIDGE_OP_MODES) {
- drm_bridge_put(bridge_connector->bridge_edid);
- bridge_connector->bridge_edid = NULL;
- drm_bridge_put(bridge_connector->bridge_modes);
- bridge_connector->bridge_modes = NULL;
+ drm_bridge_put(dynconn->bridge_edid);
+ dynconn->bridge_edid = NULL;
+ drm_bridge_put(dynconn->bridge_modes);
+ dynconn->bridge_modes = NULL;
if (bridge->ops & DRM_BRIDGE_OP_EDID)
- bridge_connector->bridge_edid = drm_bridge_get(bridge);
+ dynconn->bridge_edid = drm_bridge_get(bridge);
if (bridge->ops & DRM_BRIDGE_OP_MODES)
- bridge_connector->bridge_modes = drm_bridge_get(bridge);
+ dynconn->bridge_modes = drm_bridge_get(bridge);
}
if (bridge->ops & DRM_BRIDGE_OP_HPD) {
- drm_bridge_put(bridge_connector->bridge_hpd);
- bridge_connector->bridge_hpd = drm_bridge_get(bridge);
+ drm_bridge_put(dynconn->bridge_hpd);
+ dynconn->bridge_hpd = drm_bridge_get(bridge);
}
if (bridge->ops & DRM_BRIDGE_OP_DETECT) {
- drm_bridge_put(bridge_connector->bridge_detect);
- bridge_connector->bridge_detect = drm_bridge_get(bridge);
+ drm_bridge_put(dynconn->bridge_detect);
+ dynconn->bridge_detect = drm_bridge_get(bridge);
}
if (bridge->ops & DRM_BRIDGE_OP_HDMI) {
- if (bridge_connector->bridge_hdmi)
+ if (dynconn->bridge_hdmi)
return -EBUSY;
if (!bridge->funcs->hdmi_write_avi_infoframe ||
!bridge->funcs->hdmi_clear_avi_infoframe ||
@@ -883,7 +887,7 @@ static int drm_bridge_connector_get_bridges(struct drm_bridge_connector_dynconn
!bridge->funcs->hdmi_clear_spd_infoframe))
return -EINVAL;
- bridge_connector->bridge_hdmi = drm_bridge_get(bridge);
+ dynconn->bridge_hdmi = drm_bridge_get(bridge);
if (bridge->supported_formats)
*supported_formats = bridge->supported_formats;
@@ -892,10 +896,10 @@ static int drm_bridge_connector_get_bridges(struct drm_bridge_connector_dynconn
}
if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO) {
- if (bridge_connector->bridge_hdmi_audio)
+ if (dynconn->bridge_hdmi_audio)
return -EBUSY;
- if (bridge_connector->bridge_dp_audio)
+ if (dynconn->bridge_dp_audio)
return -EBUSY;
if (!bridge->hdmi_audio_max_i2s_playback_channels &&
@@ -906,14 +910,14 @@ static int drm_bridge_connector_get_bridges(struct drm_bridge_connector_dynconn
!bridge->funcs->hdmi_audio_shutdown)
return -EINVAL;
- bridge_connector->bridge_hdmi_audio = drm_bridge_get(bridge);
+ dynconn->bridge_hdmi_audio = drm_bridge_get(bridge);
}
if (bridge->ops & DRM_BRIDGE_OP_DP_AUDIO) {
- if (bridge_connector->bridge_dp_audio)
+ if (dynconn->bridge_dp_audio)
return -EBUSY;
- if (bridge_connector->bridge_hdmi_audio)
+ if (dynconn->bridge_hdmi_audio)
return -EBUSY;
if (!bridge->hdmi_audio_max_i2s_playback_channels &&
@@ -924,21 +928,21 @@ static int drm_bridge_connector_get_bridges(struct drm_bridge_connector_dynconn
!bridge->funcs->dp_audio_shutdown)
return -EINVAL;
- bridge_connector->bridge_dp_audio = drm_bridge_get(bridge);
+ dynconn->bridge_dp_audio = drm_bridge_get(bridge);
}
if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
- if (bridge_connector->bridge_hdmi_cec)
+ if (dynconn->bridge_hdmi_cec)
return -EBUSY;
- bridge_connector->bridge_hdmi_cec = drm_bridge_get(bridge);
+ dynconn->bridge_hdmi_cec = drm_bridge_get(bridge);
}
if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
- if (bridge_connector->bridge_hdmi_cec)
+ if (dynconn->bridge_hdmi_cec)
return -EBUSY;
- bridge_connector->bridge_hdmi_cec = drm_bridge_get(bridge);
+ dynconn->bridge_hdmi_cec = drm_bridge_get(bridge);
if (!bridge->funcs->hdmi_cec_enable ||
!bridge->funcs->hdmi_cec_log_addr ||
@@ -972,20 +976,20 @@ static int drm_bridge_connector_get_bridges(struct drm_bridge_connector_dynconn
return 0;
}
-static int drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector *bridge_connector)
+static int drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector_dynconn *dynconn)
{
- struct drm_connector *connector = &bridge_connector->dynconn->connector;
+ struct drm_connector *connector = &dynconn->connector;
int ret;
- if (bridge_connector->bridge_hdmi_audio ||
- bridge_connector->bridge_dp_audio) {
+ if (dynconn->bridge_hdmi_audio ||
+ dynconn->bridge_dp_audio) {
struct device *dev;
struct drm_bridge *bridge;
- if (bridge_connector->bridge_hdmi_audio)
- bridge = bridge_connector->bridge_hdmi_audio;
+ if (dynconn->bridge_hdmi_audio)
+ bridge = dynconn->bridge_hdmi_audio;
else
- bridge = bridge_connector->bridge_dp_audio;
+ bridge = dynconn->bridge_dp_audio;
dev = bridge->hdmi_audio_dev;
@@ -999,9 +1003,9 @@ static int drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector
return ret;
}
- if (bridge_connector->bridge_hdmi_cec &&
- bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
- struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
+ if (dynconn->bridge_hdmi_cec &&
+ dynconn->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
+ struct drm_bridge *bridge = dynconn->bridge_hdmi_cec;
ret = drmm_connector_hdmi_cec_notifier_register(connector, NULL,
bridge->hdmi_cec_dev);
@@ -1009,9 +1013,9 @@ static int drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector
return ret;
}
- if (bridge_connector->bridge_hdmi_cec &&
- bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
- struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
+ if (dynconn->bridge_hdmi_cec &&
+ dynconn->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
+ struct drm_bridge *bridge = dynconn->bridge_hdmi_cec;
ret = drmm_connector_hdmi_cec_register(connector,
&drm_bridge_connector_hdmi_cec_funcs,
@@ -1061,29 +1065,29 @@ static int drm_bridge_connector_add_connector(struct drm_bridge_connector *bridg
return ret;
}
- if (bridge_connector->bridge_hdmi) {
+ if (dynconn->bridge_hdmi) {
if (!connector->ycbcr_420_allowed)
supported_formats &= ~BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420);
- bridge_connector->hdmi_funcs = drm_bridge_connector_hdmi_funcs;
+ dynconn->hdmi_funcs = drm_bridge_connector_hdmi_funcs;
- if (bridge_connector->bridge_hdmi->ops & DRM_BRIDGE_OP_HDMI_AUDIO)
- bridge_connector->hdmi_funcs.audio =
+ if (dynconn->bridge_hdmi->ops & DRM_BRIDGE_OP_HDMI_AUDIO)
+ dynconn->hdmi_funcs.audio =
drm_bridge_connector_hdmi_audio_infoframe;
- if (bridge_connector->bridge_hdmi->ops & DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME)
- bridge_connector->hdmi_funcs.hdr_drm =
+ if (dynconn->bridge_hdmi->ops & DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME)
+ dynconn->hdmi_funcs.hdr_drm =
drm_bridge_connector_hdmi_hdr_drm_infoframe;
- if (bridge_connector->bridge_hdmi->ops & DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME)
- bridge_connector->hdmi_funcs.spd =
+ if (dynconn->bridge_hdmi->ops & DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME)
+ dynconn->hdmi_funcs.spd =
drm_bridge_connector_hdmi_spd_infoframe;
ret = drm_connector_hdmi_dynamic_init(bridge_connector->drm, connector,
- bridge_connector->bridge_hdmi->vendor,
- bridge_connector->bridge_hdmi->product,
+ dynconn->bridge_hdmi->vendor,
+ dynconn->bridge_hdmi->product,
&drm_bridge_connector_funcs,
- &bridge_connector->hdmi_funcs,
+ &dynconn->hdmi_funcs,
connector_type, ddc,
supported_formats,
max_bpc);
@@ -1093,7 +1097,7 @@ static int drm_bridge_connector_add_connector(struct drm_bridge_connector *bridg
connector_type, ddc);
}
if (ret) {
- drm_bridge_connector_put_bridges(dynconn->bridge_connector);
+ drm_bridge_connector_put_bridges(dynconn);
kfree(dynconn);
return ret;
}
@@ -1104,15 +1108,15 @@ static int drm_bridge_connector_add_connector(struct drm_bridge_connector *bridg
drm_bridge_connector_reset(connector);
- ret = drm_bridge_connector_init_hdmi_audio_cec(bridge_connector);
+ ret = drm_bridge_connector_init_hdmi_audio_cec(dynconn);
if (ret)
goto err_put;
drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
- if (bridge_connector->bridge_hpd)
+ if (dynconn->bridge_hpd)
connector->polled = DRM_CONNECTOR_POLL_HPD;
- else if (bridge_connector->bridge_detect)
+ else if (dynconn->bridge_detect)
connector->polled = DRM_CONNECTOR_POLL_CONNECT
| DRM_CONNECTOR_POLL_DISCONNECT;
--
2.54.0
^ permalink raw reply related
* [PATCH 18/37] drm/bridge: samsung-dsim: remove the panel_bridge on host_detach
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
In preparation for DRM bridge hot-plugging, we need to handle the dynamic
lifetime of the following bridge in case the samsung-dsim is always present
and the following bridge (next_bridge) is hot-unplugged.
Based on the 'if (!IS_ERR(panel))' check in samsung_dsim_host_attach(), the
next_bridge could be A) a panel bridge created by this driver via
devm_drm_panel_bridge_add() or B) a pre-existing bridge obtained via
of_drm_find_and_get_bridge().
For case B) we need to put that reference when the next_bridge is removed,
which is already handled by calling drm_bridge_clear_and_put() in
samsung_dsim_host_detach() and in the samsung_dsim_host_attach() error
management code.
In case A) we additionally have to remove the panel bridge. Currently it is
created by devm_drm_panel_bridge_add(), which adds two devm actions with
the refcounted panel bridge:
- drm_bridge_put_void() via devm_drm_bridge_alloc() on panel->dev
- devm_drm_panel_bridge_release() via devm_drm_panel_bridge_add_typed()
on the consumer device (samsung-dsim)
The first action is OK: being tied to panel->dev it will happen when the
panel is unplugged.
The second action is bound to the consumer device, so the devm semantics is
not useful here when introducing hotplug. Indeed we need to drop the
next_bridge in samsung_dsim_host_detach() anyway, before the driver .remove
function, in order to support {add, {attach, detach} x N, remove} hotplug
event sequences.
Thus move to the non-devm drm_panel_bridge_add() along with the matching
drm_panel_bridge_remove(), so the lifetime of the panel-bridge is tied to
the host_attach/host_detach cycle and not the whole samsung-dsim device
lifetime.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
In a previous discussion with Maxime he mentioned a plan to make every
drm_panel always have a wrapping bridge. With that done, all the code
handling the panel and adding the panel_bridge would become useless here
(and in many other places) and could be entirely removed. This patch is a
temporary solution until that happens. The best pointer I could find to
that discussion is [0], but there might be more recent material I could not
find at the moment.
[0] https://lore.kernel.org/lkml/20250218-faithful-white-magpie-da9ac9@houat/
---
drivers/gpu/drm/bridge/samsung-dsim.c | 17 ++++++++++++++---
include/drm/bridge/samsung-dsim.h | 2 ++
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
index 5b799619e07e..2af287221e22 100644
--- a/drivers/gpu/drm/bridge/samsung-dsim.c
+++ b/drivers/gpu/drm/bridge/samsung-dsim.c
@@ -1951,14 +1951,16 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
if (!remote)
return -ENODEV;
+ dsi->panel_bridge_added = false;
panel = of_drm_find_panel(remote);
if (!IS_ERR(panel)) {
- next_bridge = devm_drm_panel_bridge_add(dev, panel);
+ next_bridge = drm_panel_bridge_add(panel);
if (IS_ERR(next_bridge)) {
ret = PTR_ERR(next_bridge);
next_bridge = NULL; // Inhibit the cleanup action on an ERR_PTR
} else {
drm_bridge_get(next_bridge);
+ dsi->panel_bridge_added = true;
}
} else {
next_bridge = of_drm_find_and_get_bridge(remote);
@@ -1989,7 +1991,7 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) {
ret = samsung_dsim_register_te_irq(dsi, &device->dev);
if (ret)
- goto err_remove_bridge;
+ goto err_remove_panel_bridge;
}
// The next bridge can be used by host_ops->attach
@@ -2011,8 +2013,12 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
drm_bridge_clear_and_put(&dsi->bridge.next_bridge);
if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO))
samsung_dsim_unregister_te_irq(dsi);
-err_remove_bridge:
+err_remove_panel_bridge:
drm_bridge_remove(&dsi->bridge);
+ if (dsi->panel_bridge_added) {
+ drm_panel_bridge_remove(next_bridge);
+ dsi->panel_bridge_added = false;
+ }
return ret;
}
@@ -2022,6 +2028,11 @@ static int samsung_dsim_host_detach(struct mipi_dsi_host *host,
struct samsung_dsim *dsi = host_to_dsi(host);
const struct samsung_dsim_plat_data *pdata = dsi->plat_data;
+ if (dsi->panel_bridge_added) {
+ drm_panel_bridge_remove(dsi->bridge.next_bridge);
+ dsi->panel_bridge_added = false;
+ }
+
if (pdata->host_ops && pdata->host_ops->detach)
pdata->host_ops->detach(dsi, device);
diff --git a/include/drm/bridge/samsung-dsim.h b/include/drm/bridge/samsung-dsim.h
index 03005e474704..3620f7aa9394 100644
--- a/include/drm/bridge/samsung-dsim.h
+++ b/include/drm/bridge/samsung-dsim.h
@@ -131,6 +131,8 @@ struct samsung_dsim {
const struct samsung_dsim_plat_data *plat_data;
void *priv;
+
+ bool panel_bridge_added; /* true iff next_bridge is a panel_bridge we created */
};
extern int samsung_dsim_probe(struct platform_device *pdev);
--
2.54.0
^ permalink raw reply related
* [PATCH 17/37] drm/display: bridge-connector: protect dynconn creation and destruction with a mutex
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
Bridge hotplug will make the connector dynamically created and destroyed
based on hotplug events.
In preparation for that, add a mutex to mutually exclude connector creation
and destruction.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index bd421b6beb7c..b83da529cc7a 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -8,6 +8,7 @@
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/slab.h>
@@ -152,6 +153,11 @@ struct drm_bridge_connector {
* @dynconn: The DRM connector added by the drm_bridge_connector
*/
struct drm_bridge_connector_dynconn *dynconn;
+ /**
+ * @dynconn_mutex: Protect @dynconn from concurrent creation and
+ * destruction
+ */
+ struct mutex dynconn_mutex;
};
static struct drm_bridge_connector_dynconn *
@@ -810,6 +816,8 @@ static const struct drm_connector_hdmi_cec_funcs drm_bridge_connector_hdmi_cec_f
static void drm_bridge_connector_dynconn_release(struct drm_bridge_connector *bridge_connector)
{
+ guard(mutex)(&bridge_connector->dynconn_mutex);
+
if (!bridge_connector->dynconn)
return;
@@ -1050,6 +1058,8 @@ static int drm_bridge_connector_add_connector(struct drm_bridge_connector *bridg
int connector_type;
int ret;
+ guard(mutex)(&bridge_connector->dynconn_mutex);
+
struct drm_bridge_connector_dynconn *dynconn = kzalloc_obj(*dynconn);
if (!dynconn)
return -ENOMEM;
@@ -1177,6 +1187,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (!bridge_connector)
return ERR_PTR(-ENOMEM);
+ mutex_init(&bridge_connector->dynconn_mutex);
bridge_connector->drm = drm;
bridge_connector->encoder = encoder;
--
2.54.0
^ permalink raw reply related
* [PATCH] i2c: imx-lpi2c: mark I2C adapter when hardware is powered down
From: Carlos Song (OSS) @ 2026-05-19 10:41 UTC (permalink / raw)
To: aisheng.dong, andi.shyti, Frank.Li, s.hauer, kernel, festevam
Cc: carlos.song, linux-i2c, imx, linux-arm-kernel, linux-kernel,
stable
From: Carlos Song <carlos.song@nxp.com>
Mark the I2C adapter as suspended during system suspend to block further
transfers, and resume it on system resume. This prevents potential hangs
when the hardware is powered down but clients still attempt I2C transfers.
Fixes: 1ee867e465c1 ("i2c: imx-lpi2c: add target mode support")
Cc: stable@vger.kernel.org
Signed-off-by: Carlos Song <carlos.song@nxp.com>
---
drivers/i2c/busses/i2c-imx-lpi2c.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c
index a01c23696481..bf8c1ce1c7ff 100644
--- a/drivers/i2c/busses/i2c-imx-lpi2c.c
+++ b/drivers/i2c/busses/i2c-imx-lpi2c.c
@@ -1635,7 +1635,16 @@ static int __maybe_unused lpi2c_runtime_resume(struct device *dev)
static int __maybe_unused lpi2c_suspend_noirq(struct device *dev)
{
- return pm_runtime_force_suspend(dev);
+ struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ i2c_mark_adapter_suspended(&lpi2c_imx->adapter);
+
+ return 0;
}
static int __maybe_unused lpi2c_resume_noirq(struct device *dev)
@@ -1655,6 +1664,8 @@ static int __maybe_unused lpi2c_resume_noirq(struct device *dev)
if (lpi2c_imx->target)
lpi2c_imx_target_init(lpi2c_imx);
+ i2c_mark_adapter_resumed(&lpi2c_imx->adapter);
+
return 0;
}
--
2.43.0
^ permalink raw reply related
* [PATCH 15/37] drm/display: bridge-connector: allocate the connector dynamically
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
Currently the drm_bridge_connector has an embedded drm_connector, so their
allocation lifetimes are tied to each other. This is insufficient to
support DRM bridge hotplugging, which requires the connector to be added
and removed dynamically at runtime multiple times based on hotplug/unplug
events while the drm_bridge_connector is persistent.
Moreover the drm_connector is exposed to user space and thus an ongoing
operation (e.g. an ioctl) might last for an arbitrarily long time even
after the hardware gets removed. This means a new connector might have to
be added when the previous one is still referenced by user space.
In preparation to handle hotplug, allocate the drm-connector dynamically,
to allow:
* creating and destroying a connector multiple times during a single
drm_bridge_connector lifetime
* creating a new connector even though the previous one is still in use
and thus still refcounted and not yet freed
This commit does not introduce the actions in the two bullets (it will
happen in a later commit), it only moves to dynamic APIs for connector
allocation and init.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 156 ++++++++++++++++++-------
1 file changed, 114 insertions(+), 42 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 1fd104f1647b..b4e2c8f1d32d 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Copyright (C) 2026 GE HealthCare
+ * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
*/
#include <linux/export.h>
@@ -53,13 +55,24 @@
*/
/**
- * struct drm_bridge_connector - A connector backed by a chain of bridges
+ * drm_bridge_connector_dynconn - wrapper struct for the dynamically
+ * created drm_connector
*/
-struct drm_bridge_connector {
+struct drm_bridge_connector_dynconn {
/**
- * @base: The base DRM connector
+ * @bridge_connector: the owning bridge connector
*/
- struct drm_connector base;
+ struct drm_bridge_connector *bridge_connector;
+ /**
+ * @connector: the drm_connector exposed to user space
+ */
+ struct drm_connector connector;
+};
+
+/**
+ * struct drm_bridge_connector - A connector backed by a chain of bridges
+ */
+struct drm_bridge_connector {
/**
* @drm: The DRM device we belong to
*/
@@ -70,6 +83,10 @@ struct drm_bridge_connector {
* The encoder at the start of the bridges chain.
*/
struct drm_encoder *encoder;
+ /**
+ * @dynconn: The DRM connector added by the drm_bridge_connector
+ */
+ struct drm_bridge_connector_dynconn *dynconn;
/**
* @bridge_edid:
*
@@ -137,8 +154,13 @@ struct drm_bridge_connector {
struct drm_connector_hdmi_funcs hdmi_funcs;
};
-#define to_drm_bridge_connector(x) \
- container_of(x, struct drm_bridge_connector, base)
+static struct drm_bridge_connector *to_drm_bridge_connector(const struct drm_connector *connector)
+{
+ const struct drm_bridge_connector_dynconn *dynconn =
+ container_of(connector, struct drm_bridge_connector_dynconn, connector);
+
+ return dynconn->bridge_connector;
+}
static void drm_bridge_connector_put_bridges(struct drm_bridge_connector *bridge_connector)
{
@@ -152,13 +174,6 @@ static void drm_bridge_connector_put_bridges(struct drm_bridge_connector *bridge
drm_bridge_put(bridge_connector->bridge_hdmi_cec);
}
-static void drmm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
-{
- struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
-
- drm_bridge_connector_put_bridges(bridge_connector);
-}
-
/* -----------------------------------------------------------------------------
* Bridge Connector Hot-Plug Handling
*/
@@ -179,7 +194,7 @@ static void drm_bridge_connector_hpd_notify(struct drm_connector *connector,
static void drm_bridge_connector_handle_hpd(struct drm_bridge_connector *bridge_connector,
enum drm_connector_status status)
{
- struct drm_connector *connector = &bridge_connector->base;
+ struct drm_connector *connector = &bridge_connector->dynconn->connector;
struct drm_device *dev = bridge_connector->drm;
mutex_lock(&dev->mode_config.mutex);
@@ -299,6 +314,16 @@ static void drm_bridge_connector_reset(struct drm_connector *connector)
connector->state);
}
+static void drm_bridge_connector_dynconn_destroy(struct drm_connector *connector)
+{
+ struct drm_bridge_connector_dynconn *dynconn =
+ container_of(connector, struct drm_bridge_connector_dynconn, connector);
+
+ drm_connector_cleanup(connector);
+ drm_bridge_connector_put_bridges(dynconn->bridge_connector);
+ kfree(dynconn);
+}
+
static const struct drm_connector_funcs drm_bridge_connector_funcs = {
.reset = drm_bridge_connector_reset,
.detect = drm_bridge_connector_detect,
@@ -308,6 +333,7 @@ static const struct drm_connector_funcs drm_bridge_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.debugfs_init = drm_bridge_connector_debugfs_init,
.oob_hotplug_event = drm_bridge_connector_oob_hotplug_event,
+ .destroy = drm_bridge_connector_dynconn_destroy,
};
/* -----------------------------------------------------------------------------
@@ -777,7 +803,17 @@ static const struct drm_connector_hdmi_cec_funcs drm_bridge_connector_hdmi_cec_f
* Bridge Connector Initialisation
*/
-static int drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_connector,
+static void drm_bridge_connector_dynconn_release(struct drm_bridge_connector *bridge_connector)
+{
+ if (!bridge_connector->dynconn)
+ return;
+
+ drm_connector_unregister(&bridge_connector->dynconn->connector);
+ drm_connector_put(&bridge_connector->dynconn->connector);
+ bridge_connector->dynconn = NULL;
+}
+
+static int drm_bridge_connector_get_bridges(struct drm_bridge_connector_dynconn *dynconn,
int *connector_type,
unsigned int *supported_formats,
unsigned int *max_bpc,
@@ -785,7 +821,8 @@ static int drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_
struct drm_bridge **panel_bridge,
bool *support_hdcp)
{
- struct drm_connector *connector = &bridge_connector->base;
+ struct drm_bridge_connector *bridge_connector = dynconn->bridge_connector;
+ struct drm_connector *connector = &dynconn->connector;
/*
* TODO: Handle doublescan_allowed and stereo_allowed.
@@ -937,6 +974,7 @@ static int drm_bridge_connector_get_bridges(struct drm_bridge_connector *bridge_
static int drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector *bridge_connector)
{
+ struct drm_connector *connector = &bridge_connector->dynconn->connector;
int ret;
if (bridge_connector->bridge_hdmi_audio ||
@@ -951,7 +989,7 @@ static int drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector
dev = bridge->hdmi_audio_dev;
- ret = drm_connector_hdmi_audio_init(&bridge_connector->base, dev,
+ ret = drm_connector_hdmi_audio_init(connector, dev,
&drm_bridge_connector_hdmi_audio_funcs,
bridge->hdmi_audio_max_i2s_playback_channels,
bridge->hdmi_audio_i2s_formats,
@@ -965,8 +1003,7 @@ static int drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector
bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
- ret = drmm_connector_hdmi_cec_notifier_register(&bridge_connector->base,
- NULL,
+ ret = drmm_connector_hdmi_cec_notifier_register(connector, NULL,
bridge->hdmi_cec_dev);
if (ret)
return ret;
@@ -976,7 +1013,7 @@ static int drm_bridge_connector_init_hdmi_audio_cec(struct drm_bridge_connector
bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
- ret = drmm_connector_hdmi_cec_register(&bridge_connector->base,
+ ret = drmm_connector_hdmi_cec_register(connector,
&drm_bridge_connector_hdmi_cec_funcs,
bridge->hdmi_cec_adapter_name,
bridge->hdmi_cec_available_las,
@@ -1009,13 +1046,20 @@ static int drm_bridge_connector_add_connector(struct drm_bridge_connector *bridg
int connector_type;
int ret;
- connector = &bridge_connector->base;
+ struct drm_bridge_connector_dynconn *dynconn = kzalloc_obj(*dynconn);
+ if (!dynconn)
+ return -ENOMEM;
- ret = drm_bridge_connector_get_bridges(bridge_connector, &connector_type,
+ dynconn->bridge_connector = bridge_connector;
+ connector = &dynconn->connector;
+
+ ret = drm_bridge_connector_get_bridges(dynconn, &connector_type,
&supported_formats, &max_bpc, &ddc,
&panel_bridge, &support_hdcp);
- if (ret)
+ if (ret) {
+ kfree(dynconn);
return ret;
+ }
if (bridge_connector->bridge_hdmi) {
if (!connector->ycbcr_420_allowed)
@@ -1035,25 +1079,34 @@ static int drm_bridge_connector_add_connector(struct drm_bridge_connector *bridg
bridge_connector->hdmi_funcs.spd =
drm_bridge_connector_hdmi_spd_infoframe;
- ret = drmm_connector_hdmi_init(bridge_connector->drm, connector,
- bridge_connector->bridge_hdmi->vendor,
- bridge_connector->bridge_hdmi->product,
- &drm_bridge_connector_funcs,
- &bridge_connector->hdmi_funcs,
- connector_type, ddc,
- supported_formats,
- max_bpc);
+ ret = drm_connector_hdmi_dynamic_init(bridge_connector->drm, connector,
+ bridge_connector->bridge_hdmi->vendor,
+ bridge_connector->bridge_hdmi->product,
+ &drm_bridge_connector_funcs,
+ &bridge_connector->hdmi_funcs,
+ connector_type, ddc,
+ supported_formats,
+ max_bpc);
} else {
- ret = drmm_connector_init(bridge_connector->drm, connector,
- &drm_bridge_connector_funcs,
- connector_type, ddc);
+ ret = drm_connector_dynamic_init(bridge_connector->drm, connector,
+ &drm_bridge_connector_funcs,
+ connector_type, ddc);
}
- if (ret)
+ if (ret) {
+ drm_bridge_connector_put_bridges(dynconn->bridge_connector);
+ kfree(dynconn);
return ret;
+ }
+
+ /* From now on the connector is referenced and has to be put, not kfreed */
+
+ bridge_connector->dynconn = dynconn;
+
+ drm_bridge_connector_reset(connector);
ret = drm_bridge_connector_init_hdmi_audio_cec(bridge_connector);
if (ret)
- return ret;
+ goto err_put;
drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
@@ -1072,9 +1125,25 @@ static int drm_bridge_connector_add_connector(struct drm_bridge_connector *bridg
ret = drm_connector_attach_encoder(connector, bridge_connector->encoder);
if (ret)
- return ret;
+ goto err_put;
+
+ ret = drm_connector_dynamic_register(connector);
+ if (ret)
+ goto err_put;
return 0;
+
+err_put:
+ drm_connector_put(connector);
+ bridge_connector->dynconn = NULL;
+ return ret;
+}
+
+static void drm_bridge_connector_fini(struct drm_device *dev, void *res)
+{
+ struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)res;
+
+ drm_bridge_connector_dynconn_release(bridge_connector);
}
/**
@@ -1104,10 +1173,6 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (!bridge_connector)
return ERR_PTR(-ENOMEM);
- ret = drmm_add_action(drm, drmm_bridge_connector_put_bridges, bridge_connector);
- if (ret)
- return ERR_PTR(ret);
-
bridge_connector->drm = drm;
bridge_connector->encoder = encoder;
@@ -1115,6 +1180,13 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (ret)
return ERR_PTR(ret);
- return &bridge_connector->base;
+ ret = drmm_add_action_or_reset(drm, drm_bridge_connector_fini, bridge_connector);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (!bridge_connector->dynconn)
+ return ERR_PTR(-EINVAL);
+
+ return &bridge_connector->dynconn->connector;
}
EXPORT_SYMBOL_GPL(drm_bridge_connector_init);
--
2.54.0
^ permalink raw reply related
* [PATCH 14/37] drm/display: bridge-connector: add non-drmm variant of drm_bridge_connector_put_bridges()
From: Luca Ceresoli @ 2026-05-19 10:37 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Inki Dae,
Jagan Teki, Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel, Luca Ceresoli
In-Reply-To: <20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com>
drm_bridge_connector_put_bridges() is currently meant as a drmm cleanup
action. In preparation for bridge hotplug add a non-drmm action, so it can
be called more simply in non-drmm context.
To do this split drm_bridge_connector_put_bridges() in two variants, both
doing the same actions and only differing in the name and arguments:
* drm_bridge_connector_put_bridges() (same name as before) to be called
directly by the drm_bridge_connector code with a drm_bridge_connector
pointer as the only parameter
* drmm_bridge_connector_put_bridges() (same name + drmm prefix) providing
the same drmm calling convention as before
Update the only call site accordingly.
No functional changes.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 2e96af8efb55..1fd104f1647b 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -140,10 +140,8 @@ struct drm_bridge_connector {
#define to_drm_bridge_connector(x) \
container_of(x, struct drm_bridge_connector, base)
-static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
+static void drm_bridge_connector_put_bridges(struct drm_bridge_connector *bridge_connector)
{
- struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
-
drm_bridge_put(bridge_connector->bridge_edid);
drm_bridge_put(bridge_connector->bridge_hpd);
drm_bridge_put(bridge_connector->bridge_detect);
@@ -154,6 +152,13 @@ static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
drm_bridge_put(bridge_connector->bridge_hdmi_cec);
}
+static void drmm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
+{
+ struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
+
+ drm_bridge_connector_put_bridges(bridge_connector);
+}
+
/* -----------------------------------------------------------------------------
* Bridge Connector Hot-Plug Handling
*/
@@ -1099,7 +1104,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (!bridge_connector)
return ERR_PTR(-ENOMEM);
- ret = drmm_add_action(drm, drm_bridge_connector_put_bridges, bridge_connector);
+ ret = drmm_add_action(drm, drmm_bridge_connector_put_bridges, bridge_connector);
if (ret)
return ERR_PTR(ret);
--
2.54.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox