* [PATCH 01/11] drm: of: drm_of_find_panel_or_bridge: simplify freeing the remote node pointer
2026-08-14 14:05 [PATCH 00/11] drm/panel: add a panel_bridge to every panel Luca Ceresoli
@ 2026-08-14 14:05 ` Luca Ceresoli
2026-08-14 14:05 ` [PATCH 02/11] drm: of: drm_of_find_panel_or_bridge: simplify error return paths Luca Ceresoli
` (10 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Luca Ceresoli @ 2026-08-14 14:05 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, Jessica Zhang,
Linus Walleij, Inki Dae, Jagan Teki, Marek Szyprowski
Cc: Dmitry Baryshkov, Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel,
linux-kernel, Luca Ceresoli
In prepataion to further modify the logic of this function, simplify
putting the struct device_node pointer by using a cleanup action.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_of.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
index e495117735e1..b42a321f3052 100644
--- a/drivers/gpu/drm/drm_of.c
+++ b/drivers/gpu/drm/drm_of.c
@@ -288,7 +288,6 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
struct drm_bridge **bridge)
{
int ret = -EPROBE_DEFER;
- struct device_node *remote;
if (WARN_ON(!panel))
return -EINVAL;
@@ -304,7 +303,8 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
if (!of_graph_is_present(np))
return -ENODEV;
- remote = of_graph_get_remote_node(np, port, endpoint);
+ struct device_node *remote __free(device_node) =
+ of_graph_get_remote_node(np, port, endpoint);
if (!remote)
return -ENODEV;
@@ -326,7 +326,6 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
}
- of_node_put(remote);
return ret;
}
EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH 02/11] drm: of: drm_of_find_panel_or_bridge: simplify error return paths
2026-08-14 14:05 [PATCH 00/11] drm/panel: add a panel_bridge to every panel Luca Ceresoli
2026-08-14 14:05 ` [PATCH 01/11] drm: of: drm_of_find_panel_or_bridge: simplify freeing the remote node pointer Luca Ceresoli
@ 2026-08-14 14:05 ` Luca Ceresoli
2026-08-14 14:05 ` [PATCH 03/11] drm: of: drm_of_find_panel_or_bridge: prioritize the bridge, not the panel Luca Ceresoli
` (9 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Luca Ceresoli @ 2026-08-14 14:05 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, Jessica Zhang,
Linus Walleij, Inki Dae, Jagan Teki, Marek Szyprowski
Cc: Dmitry Baryshkov, Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel,
linux-kernel, Luca Ceresoli
In prepataion to further modify the logic of this function, simplify the
error-returning code.
With this change, on any error there is an immediate 'return -<ERRNO>;'
statement, without having to carry on the return value until the end.
Additionally, clear both the panel and the bridge pointers at the
beginning. Even though this is redundant in some code paths, it allows to
have a simpler code in the rest of the function.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_of.c | 25 +++++++++++--------------
1 file changed, 11 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
index b42a321f3052..8ec352f3df93 100644
--- a/drivers/gpu/drm/drm_of.c
+++ b/drivers/gpu/drm/drm_of.c
@@ -287,12 +287,12 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
struct drm_panel **panel,
struct drm_bridge **bridge)
{
- int ret = -EPROBE_DEFER;
-
if (WARN_ON(!panel))
return -EINVAL;
*panel = NULL;
+ if (bridge)
+ *bridge = NULL;
/*
* of_graph_get_remote_node() produces a noisy error message if port
@@ -310,23 +310,20 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
*panel = of_drm_find_panel(remote);
if (!IS_ERR(*panel))
- ret = 0;
- else
- *panel = NULL;
+ return 0;
+
+ *panel = NULL;
if (bridge) {
- if (ret) {
- /* No panel found yet, check for a bridge next. */
- *bridge = of_drm_find_bridge(remote);
- if (*bridge)
- ret = 0;
- } else {
- *bridge = NULL;
- }
+ /* No panel found yet, check for a bridge next. */
+ *bridge = of_drm_find_bridge(remote);
+ if (*bridge)
+ return 0;
+ *bridge = NULL;
}
- return ret;
+ return -EPROBE_DEFER;
}
EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH 03/11] drm: of: drm_of_find_panel_or_bridge: prioritize the bridge, not the panel
2026-08-14 14:05 [PATCH 00/11] drm/panel: add a panel_bridge to every panel Luca Ceresoli
2026-08-14 14:05 ` [PATCH 01/11] drm: of: drm_of_find_panel_or_bridge: simplify freeing the remote node pointer Luca Ceresoli
2026-08-14 14:05 ` [PATCH 02/11] drm: of: drm_of_find_panel_or_bridge: simplify error return paths Luca Ceresoli
@ 2026-08-14 14:05 ` Luca Ceresoli
2026-08-14 14:20 ` sashiko-bot
2026-08-14 14:05 ` [PATCH 04/11] drm/panel: merge the drm_kms_helper module into the drm module Luca Ceresoli
` (8 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Luca Ceresoli @ 2026-08-14 14:05 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, Jessica Zhang,
Linus Walleij, Inki Dae, Jagan Teki, Marek Szyprowski
Cc: Dmitry Baryshkov, Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel,
linux-kernel, Luca Ceresoli
By the documentation drm_of_find_panel_or_bridge() returns a "drm_panel or
drm_bridge", without specifying which one is returned in case both exist.
Definitely it never returns both. If both exist (and @bridge is != NULL),
the current implementation prioritizes the drm_panel pointer and returns
that. In most cases (including devm_drm_of_get_bridge() and
drmm_of_get_bridge()) this is used to implement the following logic
(simplified):
drm_of_find_panel_or_bridge(..., &panel, &bridge);
if (panel)
bridge = [devm_]drm_panel_bridge_add[_typed](panel);
Work is in progress to make every drm_panel automatically create a
panel_bridge, so a panel_bridge will always be present for every
drm_panel. This means the above logic would create a panel_bridge that
already exists. Avoid it by returning the drm_bridge when both are present,
instead of the drm_panel.
For the case where @bridge == NULL, this commit does not change anything:
the 'if (bridge)' body is never executed and the drm_panel (if found) is
always returned.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_of.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
index 8ec352f3df93..f92f02f9b202 100644
--- a/drivers/gpu/drm/drm_of.c
+++ b/drivers/gpu/drm/drm_of.c
@@ -308,14 +308,7 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
if (!remote)
return -ENODEV;
- *panel = of_drm_find_panel(remote);
- if (!IS_ERR(*panel))
- return 0;
-
- *panel = NULL;
-
if (bridge) {
- /* No panel found yet, check for a bridge next. */
*bridge = of_drm_find_bridge(remote);
if (*bridge)
return 0;
@@ -323,6 +316,13 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
*bridge = NULL;
}
+ /* No bridge found yet, check for a panel next. */
+ *panel = of_drm_find_panel(remote);
+ if (!IS_ERR(*panel))
+ return 0;
+
+ *panel = NULL;
+
return -EPROBE_DEFER;
}
EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 03/11] drm: of: drm_of_find_panel_or_bridge: prioritize the bridge, not the panel
2026-08-14 14:05 ` [PATCH 03/11] drm: of: drm_of_find_panel_or_bridge: prioritize the bridge, not the panel Luca Ceresoli
@ 2026-08-14 14:20 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 14:20 UTC (permalink / raw)
To: Luca Ceresoli; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Changing the lookup priority from panel to bridge breaks drivers that use `panel != NULL` checks to apply panel-specific hardware constraints or initialization logic.
--
--- Patch [3]: [PATCH 03/11] drm: of: drm_of_find_panel_or_bridge: prioritize the bridge, not the panel ---
commit e0a1351b53c705f02789905fd967720e44e8a076
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
drm: of: drm_of_find_panel_or_bridge: prioritize the bridge, not the panel
This commit changes drm_of_find_panel_or_bridge() to prioritize returning
a drm_bridge instead of a drm_panel when both are present. This avoids
re-creating a panel_bridge if a panel already automatically creates one.
> diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
> index 8ec352f3df93e..f92f02f9b202a 100644
> --- a/drivers/gpu/drm/drm_of.c
> +++ b/drivers/gpu/drm/drm_of.c
> @@ -308,14 +308,7 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
> if (!remote)
> return -ENODEV;
>
> - *panel = of_drm_find_panel(remote);
> - if (!IS_ERR(*panel))
> - return 0;
> -
> - *panel = NULL;
> -
> if (bridge) {
> - /* No panel found yet, check for a bridge next. */
> *bridge = of_drm_find_bridge(remote);
> if (*bridge)
> return 0;
[Severity: High]
Will changing the lookup priority to return a bridge before a panel break
drivers that rely on the panel pointer being non-NULL to apply
hardware-specific logic?
For example, in sun4i_rgb_mode_valid(), if a panel wraps itself in a bridge,
the panel pointer will be NULL, causing valid modes to be rejected:
drivers/gpu/drm/sun4i/sun4i_rgb.c:sun4i_rgb_mode_valid() {
if (rgb->panel) {
DRM_DEBUG_DRIVER("RGB panel used, skipping clock rate checks");
goto out;
}
...
}
Similarly in fsl_dcu_create_outputs(), it relies on panel to enforce
hdisplay alignment and custom connector initialization:
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c:fsl_dcu_create_outputs() {
if (panel) {
ret = devm_add_action_or_reset(fsl_dev->dev,
fsl_dcu_panel_put_action, panel);
if (ret)
return ret;
fsl_dev->connector.panel = panel;
return fsl_dcu_attach_panel(fsl_dev, panel);
}
...
}
And tegra_sor_dp_enable() uses the panel pointer to identify eDPs and
misconfigures internal eDP panels as standard DP displays if panel is NULL:
drivers/gpu/drm/tegra/sor.c:tegra_sor_dp_enable() {
if (output->panel)
value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK;
else
value |= SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK;
tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
...
}
Could this cause display initialization to fail on these platforms?
>
> *bridge = NULL;
> }
>
> + /* No bridge found yet, check for a panel next. */
> + *panel = of_drm_find_panel(remote);
> + if (!IS_ERR(*panel))
> + return 0;
> +
> + *panel = NULL;
> +
> return -EPROBE_DEFER;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-drm-bridge-every-panel-v1-0-19cd5277cc8d@bootlin.com?part=3
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 04/11] drm/panel: merge the drm_kms_helper module into the drm module
2026-08-14 14:05 [PATCH 00/11] drm/panel: add a panel_bridge to every panel Luca Ceresoli
` (2 preceding siblings ...)
2026-08-14 14:05 ` [PATCH 03/11] drm: of: drm_of_find_panel_or_bridge: prioritize the bridge, not the panel Luca Ceresoli
@ 2026-08-14 14:05 ` Luca Ceresoli
2026-08-14 14:13 ` sashiko-bot
2026-08-14 14:05 ` [PATCH 05/11] drm/bridge: panel: add a panel_bridge to every panel Luca Ceresoli
` (7 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Luca Ceresoli @ 2026-08-14 14:05 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, Jessica Zhang,
Linus Walleij, Inki Dae, Jagan Teki, Marek Szyprowski
Cc: Dmitry Baryshkov, Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel,
linux-kernel, Luca Ceresoli
Work is in progress to make every drm_panel automatically create a
panel_bridge [0][1].
This requires the panel code to call the drm_panel_bridge APIs. However
this would create a circular dependency loop on modular builds:
__devm_drm_panel_alloc() [drm]
-> drm_panel_bridge_add() [drm_kms_helper]
-> drm_bridge.c APIs [drm]
Moving just the panel_brige.o file from [drm_kms_helper] to [drm] does not
work because the panel bridge code uses the drm_atomic_helper and
drm_probe_helper which add further dependencies on symbols in the
[drm_kms_helper] module.
So take a simple approach, and move the entire drm_kms_helper into the
[drm] module.
Link: https://lore.kernel.org/all/emuj2innmp6zmzd7pyakqzjqpdzhly6qfhakya3ydwmd63pl26@5jwxaidpikjw/ [0]
Link: https://lore.kernel.org/lkml/20250206-hotplug-drm-bridge-v6-8-9d6f2c9c3058@bootlin.com/ [1]
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
*Note* This is just a quick implementation to open a discussion while
having a working implementation. If the approach is approved there will be
more details to polish.
---
drivers/gpu/drm/Makefile | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index e97faabcd783..945d4f117c3f 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -139,7 +139,7 @@ obj-$(CONFIG_DRM_TTM_HELPER) += drm_ttm_helper.o
# Modesetting helpers
#
-drm_kms_helper-y := \
+drm-$(CONFIG_DRM_KMS_HELPER) := \
drm_atomic_helper.o \
drm_atomic_state_helper.o \
drm_bridge_helper.o \
@@ -156,9 +156,8 @@ drm_kms_helper-y := \
drm_self_refresh_helper.o \
drm_simple_kms_helper.o \
drm_vblank_helper.o
-drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
-drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
-obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
+drm-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
+drm-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
#
# Drivers and the rest
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 04/11] drm/panel: merge the drm_kms_helper module into the drm module
2026-08-14 14:05 ` [PATCH 04/11] drm/panel: merge the drm_kms_helper module into the drm module Luca Ceresoli
@ 2026-08-14 14:13 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 14:13 UTC (permalink / raw)
To: Luca Ceresoli; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] The use of `:=` instead of `+=` for `drm-$(CONFIG_DRM_KMS_HELPER)` overwrites the previously defined `drm-y` list, causing all core DRM objects to be excluded from the build.
- [Low] Merging `drm_kms_helper` into the `drm` module breaks the build or module loading when `CONFIG_DRM=y` and `CONFIG_DRM_KMS_HELPER=m` because Kbuild ignores `drm-m` for built-in objects.
--
--- Patch [4]: [PATCH 04/11] drm/panel: merge the drm_kms_helper module into the drm module ---
commit d831428a6450fb748ee21e10b4e68b33d57ee2c3
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
drm/panel: merge the drm_kms_helper module into the drm module
Work is in progress to make every drm_panel automatically create a panel_bridge,
which requires calling drm_panel_bridge APIs. This would create a circular
dependency loop on modular builds. This commit attempts to move the entire
drm_kms_helper into the drm module to resolve the dependency loop.
Link: https://lore.kernel.org/all/emuj2innmp6zmzd7pyakqzjqpdzhly6qfhakya3ydwmd63pl26@5jwxaidpikjw/
Link: https://lore.kernel.org/lkml/20250206-hotplug-drm-bridge-v6-8-9d6f2c9c3058@bootlin.com/
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -139,7 +139,7 @@ obj-$(CONFIG_DRM_TTM_HELPER) += drm_ttm_helper.o
> # Modesetting helpers
> #
>
> -drm_kms_helper-y := \
> +drm-$(CONFIG_DRM_KMS_HELPER) := \
[Severity: Low]
Does this `:=` assignment overwrite the core DRM objects defined earlier in
the Makefile? If `CONFIG_DRM_KMS_HELPER=y`, this evaluates to `drm-y :=`,
replacing the previously built list for `drm-y`. This would cause the core
DRM files to be dropped from the build.
[Severity: Low]
What happens to the build if `CONFIG_DRM=y` and `CONFIG_DRM_KMS_HELPER=m`?
In this configuration, the objects are appended to `drm-m`. Since the
parent object is built-in (`obj-y += drm.o`), Kbuild ignores the `drm-m`
list entirely. Could this cause the KMS helpers to be silently omitted
from compilation, leading to unresolved symbols in modular drivers that
depend on them?
> drm_atomic_helper.o \
> drm_atomic_state_helper.o \
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-drm-bridge-every-panel-v1-0-19cd5277cc8d@bootlin.com?part=4
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 05/11] drm/bridge: panel: add a panel_bridge to every panel
2026-08-14 14:05 [PATCH 00/11] drm/panel: add a panel_bridge to every panel Luca Ceresoli
` (3 preceding siblings ...)
2026-08-14 14:05 ` [PATCH 04/11] drm/panel: merge the drm_kms_helper module into the drm module Luca Ceresoli
@ 2026-08-14 14:05 ` Luca Ceresoli
2026-08-14 14:25 ` sashiko-bot
2026-08-14 14:05 ` [PATCH 06/11] drm/bridge: tc358767: don't create a panel_bridge Luca Ceresoli
` (6 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Luca Ceresoli @ 2026-08-14 14:05 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, Jessica Zhang,
Linus Walleij, Inki Dae, Jagan Teki, Marek Szyprowski
Cc: Dmitry Baryshkov, Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel,
linux-kernel, Luca Ceresoli
Adding a panel does currently not add a panel_bridge wrapping it. Usually
the panel_bridge creation happens later, when some other driver (e.g. the
previous bridge or the encoder) calls *_of_get_bridge() and the following
element in the pipeline is a panel.
This has some drawbacks:
* hte bridge API is currently the best practice to access various
components of the pipeline, especially with complex cards where bridges
can be combined in different ways on different hardware
* the panel_bridge is not created in the context of the driver of the
underlying physical device (the panel driver), but of some other driver
* that other driver is not aware of whether the returned drm_bridge
pointer is a panel_bridge created on the fly, a pre-existing
panel_bridge or a non-panel bridge
* removal of a panel_bridge requires calling drm_panel_bridge_remove(),
but the other driver doesn't know whether this is needed because it
doesn't know whether it has created a panel_bridge or not
So far the current approach has been working because devm and drmm ensure
the panel bridge would be dealloacted at some later point. However with the
upcoming implementation of dynamic bridge lifetime this will get more
complicated.
Switch to the new approach: always create a panel_bridge with a drm_panel,
thus matching the lifetime of the drm_panel and the panel_bridge wrapping
it. This makes lifetime much more straightforward to understand and to
further develop on.
As a consequence devm_drm_of_get_bridge() and drmm_of_get_bridge() don't
need to look for a panel anymore and become simple wrappers to
of_drm_get_bridge_by_endpoint(). Also deprecate them as they can be
replaced by of_drm_get_bridge_by_endpoint() which also handles refcount.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/panel.c | 47 +++++++++++++++++++++---------------------
drivers/gpu/drm/drm_panel.c | 3 +++
include/drm/drm_panel.h | 8 +++++++
3 files changed, 34 insertions(+), 24 deletions(-)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 02388a3de626..d86555254aa9 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -412,6 +412,11 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
{
struct drm_bridge **ptr, *bridge;
+ if (panel->bridge) {
+ dev_dbg(dev, "returning existing bridge=%p\n", panel->bridge);
+ return panel->bridge;
+ }
+
ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
@@ -495,9 +500,12 @@ EXPORT_SYMBOL(drm_panel_bridge_connector);
* @port: port in the device tree node
* @endpoint: endpoint in the device tree node
*
+ * This function is deprecated and should not be used in new drivers. The
+ * returned bridge refcount is not incremented! Replace by
+ * of_drm_get_bridge_by_endpoint() and handle bridge refcount.
+ *
* Given a DT node's port and endpoint number, finds the connected node
- * and returns the associated bridge if any, or creates and returns a
- * drm panel bridge instance if a panel is connected.
+ * and returns the associated bridge if any.
*
* Returns a pointer to the bridge if successful, or an error pointer
* otherwise.
@@ -507,18 +515,12 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
u32 port, u32 endpoint)
{
struct drm_bridge *bridge;
- struct drm_panel *panel;
- int ret;
- ret = drm_of_find_panel_or_bridge(np, port, endpoint,
- &panel, &bridge);
- if (ret)
- return ERR_PTR(ret);
+ bridge = of_drm_get_bridge_by_endpoint(np, port, endpoint);
+ if (IS_ERR(bridge))
+ return bridge;
- if (panel) {
- bridge = devm_drm_panel_bridge_add(dev, panel);
- drm_panel_put(panel);
- }
+ drm_bridge_put(bridge);
return bridge;
}
@@ -531,9 +533,12 @@ EXPORT_SYMBOL(devm_drm_of_get_bridge);
* @port: port in the device tree node
* @endpoint: endpoint in the device tree node
*
+ * This function is deprecated and should not be used in new drivers. The
+ * returned bridge refcount is not incremented! Replace by
+ * of_drm_get_bridge_by_endpoint() and handle bridge refcount.
+ *
* Given a DT node's port and endpoint number, finds the connected node
- * and returns the associated bridge if any, or creates and returns a
- * drm panel bridge instance if a panel is connected.
+ * and returns the associated bridge if any.
*
* Returns a drmm managed pointer to the bridge if successful, or an error
* pointer otherwise.
@@ -543,18 +548,12 @@ struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
u32 port, u32 endpoint)
{
struct drm_bridge *bridge;
- struct drm_panel *panel;
- int ret;
- ret = drm_of_find_panel_or_bridge(np, port, endpoint,
- &panel, &bridge);
- if (ret)
- return ERR_PTR(ret);
+ bridge = of_drm_get_bridge_by_endpoint(np, port, endpoint);
+ if (IS_ERR(bridge))
+ return bridge;
- if (panel) {
- bridge = drmm_panel_bridge_add(drm, panel);
- drm_panel_put(panel);
- }
+ drm_bridge_put(bridge);
return bridge;
}
diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
index f8f6082e637f..95fc95d1b4b7 100644
--- a/drivers/gpu/drm/drm_panel.c
+++ b/drivers/gpu/drm/drm_panel.c
@@ -453,6 +453,9 @@ void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset,
drm_panel_init(panel, dev, funcs, connector_type);
+ panel->bridge = devm_drm_panel_bridge_add(dev, panel);
+ WARN_ON(!panel->bridge);
+
return container;
}
EXPORT_SYMBOL(__devm_drm_panel_alloc);
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
index b87323443f49..9a042675eb3e 100644
--- a/include/drm/drm_panel.h
+++ b/include/drm/drm_panel.h
@@ -210,6 +210,14 @@ struct drm_panel {
*/
struct device *dev;
+ /**
+ * @bridge:
+ *
+ * Pointer to the panel bridge giving access to the panel using the
+ * DRM bridge API.
+ */
+ struct drm_bridge *bridge;
+
/**
* @backlight:
*
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 05/11] drm/bridge: panel: add a panel_bridge to every panel
2026-08-14 14:05 ` [PATCH 05/11] drm/bridge: panel: add a panel_bridge to every panel Luca Ceresoli
@ 2026-08-14 14:25 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 14:25 UTC (permalink / raw)
To: Luca Ceresoli; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Medium] Incorrect error check `WARN_ON(!panel->bridge)` masks bridge allocation failures, leading to invalid state.
Pre-existing issues:
- [High] Architectural Use-After-Free on panel memory when the panel unbinds.
--
--- Patch [5]: [PATCH 05/11] drm/bridge: panel: add a panel_bridge to every panel ---
commit fe7eeb215d441faca3cc4f0f4f95d9a96a4fb587
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
drm/bridge: panel: add a panel_bridge to every panel
Adding a panel does currently not add a panel_bridge wrapping it. Usually
the panel_bridge creation happens later, when some other driver (e.g. the
previous bridge or the encoder) calls *_of_get_bridge() and the following
element in the pipeline is a panel.
[ ... ]
Switch to the new approach: always create a panel_bridge with a drm_panel,
thus matching the lifetime of the drm_panel and the panel_bridge wrapping
it. This makes lifetime much more straightforward to understand and to
further develop on.
[Severity: High]
This is a pre-existing issue, but does the underlying bridge allocation
architecture have a use-after-free risk when panels unbind?
Looking at drm_panel_bridge_add_typed() in drivers/gpu/drm/bridge/panel.c:
struct drm_bridge *drm_panel_bridge_add_typed(...)
{
...
panel_bridge = devm_drm_bridge_alloc(panel->dev, struct panel_bridge,
bridge,
&panel_bridge_bridge_funcs);
...
}
Since devres automatically frees the bridge memory when the panel unbinds
(e.g., via rmmod), this completely ignores the bridge->refcount kref. For
child-parent topologies like DSI panels where fw_devlink cannot enforce
unbind ordering, couldn't the DSI host remain bound and retain a dangling
pointer to the freed bridge in its chain?
[ ... ]
> diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
> index f8f6082e637fd..95fc95d1b4b7a 100644
> --- a/drivers/gpu/drm/drm_panel.c
> +++ b/drivers/gpu/drm/drm_panel.c
> @@ -453,6 +453,9 @@ void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset,
>
> drm_panel_init(panel, dev, funcs, connector_type);
>
> + panel->bridge = devm_drm_panel_bridge_add(dev, panel);
> + WARN_ON(!panel->bridge);
[Severity: Medium]
Will this check actually catch failures? devm_drm_panel_bridge_add()
returns an ERR_PTR on failure, rather than NULL.
If the allocation fails due to memory pressure or an invalid connector
type, the warning will be silently bypassed since an ERR_PTR evaluates to
true.
The panel driver will then complete allocation successfully but with an
error pointer in panel->bridge, which can cause display controllers to
defer probing indefinitely without any error logs.
Should this use IS_ERR() instead of checking for NULL?
> +
> return container;
> }
> EXPORT_SYMBOL(__devm_drm_panel_alloc);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-drm-bridge-every-panel-v1-0-19cd5277cc8d@bootlin.com?part=5
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 06/11] drm/bridge: tc358767: don't create a panel_bridge
2026-08-14 14:05 [PATCH 00/11] drm/panel: add a panel_bridge to every panel Luca Ceresoli
` (4 preceding siblings ...)
2026-08-14 14:05 ` [PATCH 05/11] drm/bridge: panel: add a panel_bridge to every panel Luca Ceresoli
@ 2026-08-14 14:05 ` Luca Ceresoli
2026-08-14 14:17 ` sashiko-bot
2026-08-14 14:05 ` [PATCH 07/11] drm/bridge: waveshare-dsi: " Luca Ceresoli
` (5 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Luca Ceresoli @ 2026-08-14 14:05 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, Jessica Zhang,
Linus Walleij, Inki Dae, Jagan Teki, Marek Szyprowski
Cc: Dmitry Baryshkov, Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel,
linux-kernel, Luca Ceresoli
Now a panel_bridge is automatically created for every drm_panel, so this
panel-only call to drm_of_find_panel_or_bridge() would lead to creating a
second panel_bridge for the same panel.
Fix and simplify the code by just getting a reference to the
already-existing panel_bridge using of_drm_get_bridge_by_endpoint().
As of_drm_get_bridge_by_endpoint() returns a refcouncted bridge, take care
of putting the bridge reference on removal.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/tc358767.c | 22 +++++++---------------
1 file changed, 7 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
index ac83af7902cd..d282db507d59 100644
--- a/drivers/gpu/drm/bridge/tc358767.c
+++ b/drivers/gpu/drm/bridge/tc358767.c
@@ -2347,24 +2347,15 @@ static int tc_probe_dpi_bridge_endpoint(struct tc_data *tc)
static int tc_probe_edp_bridge_endpoint(struct tc_data *tc)
{
struct device *dev = tc->dev;
- struct drm_panel *panel;
- int ret;
+ struct drm_bridge *bridge;
/* port@2 is the output port */
- ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, NULL);
- if (ret && ret != -ENODEV)
- return dev_err_probe(dev, ret,
- "Could not find DSI panel or bridge\n");
+ bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 2, 0);
+ if (IS_ERR(bridge) && PTR_ERR(bridge) != -ENODEV)
+ return dev_err_probe(dev, PTR_ERR(bridge), "Could not find DSI bridge\n");
- if (panel) {
- struct drm_bridge *panel_bridge;
-
- panel_bridge = devm_drm_panel_bridge_add(dev, panel);
- drm_panel_put(panel);
- if (IS_ERR(panel_bridge))
- return PTR_ERR(panel_bridge);
-
- tc->panel_bridge = panel_bridge;
+ if (!IS_ERR(bridge)) {
+ tc->panel_bridge = bridge;
tc->bridge.type = DRM_MODE_CONNECTOR_eDP;
} else {
tc->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
@@ -2607,6 +2598,7 @@ static void tc_remove(struct i2c_client *client)
{
struct tc_data *tc = i2c_get_clientdata(client);
+ drm_bridge_put(tc->panel_bridge);
drm_bridge_remove(&tc->bridge);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 06/11] drm/bridge: tc358767: don't create a panel_bridge
2026-08-14 14:05 ` [PATCH 06/11] drm/bridge: tc358767: don't create a panel_bridge Luca Ceresoli
@ 2026-08-14 14:17 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 14:17 UTC (permalink / raw)
To: Luca Ceresoli; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [Critical] Refcount underflow and use-after-free for bridges acquired in DPI mode.
- [High] Resource leak of the bridge reference on probe error paths.
Pre-existing issues:
- [High] Undefined behavior due to a negative bitshift when evaluating `tc->hpd_pin`.
--
--- Patch [6]: [PATCH 06/11] drm/bridge: tc358767: don't create a panel_bridge ---
commit ae8aef2f483ffa927af25a2f1ac42b0420349ca8
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
drm/bridge: tc358767: don't create a panel_bridge
This commit prevents creating a second panel_bridge by retrieving the
already-existing one using of_drm_get_bridge_by_endpoint() for eDP
endpoints. It updates tc_remove() to release the reference.
> diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
> index e3a57f8228dae..2fef5d9fa361d 100644
> --- a/drivers/gpu/drm/bridge/tc358767.c
> +++ b/drivers/gpu/drm/bridge/tc358767.c
> @@ -2347,24 +2347,15 @@ static int tc_probe_dpi_bridge_endpoint(struct tc_data *tc)
> static int tc_probe_edp_bridge_endpoint(struct tc_data *tc)
> {
> struct device *dev = tc->dev;
> - struct drm_panel *panel;
> - int ret;
> + struct drm_bridge *bridge;
>
> /* port@2 is the output port */
> - ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, NULL);
> - if (ret && ret != -ENODEV)
> - return dev_err_probe(dev, ret,
> - "Could not find DSI panel or bridge\n");
> + bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 2, 0);
> + if (IS_ERR(bridge) && PTR_ERR(bridge) != -ENODEV)
> + return dev_err_probe(dev, PTR_ERR(bridge), "Could not find DSI bridge\n");
>
> - if (panel) {
> - struct drm_bridge *panel_bridge;
> -
> - panel_bridge = devm_drm_panel_bridge_add(dev, panel);
> - drm_panel_put(panel);
> - if (IS_ERR(panel_bridge))
> - return PTR_ERR(panel_bridge);
> -
> - tc->panel_bridge = panel_bridge;
> + if (!IS_ERR(bridge)) {
> + tc->panel_bridge = bridge;
> tc->bridge.type = DRM_MODE_CONNECTOR_eDP;
> } else {
> tc->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
> }
[Severity: High]
If of_drm_get_bridge_by_endpoint() successfully returns a refcounted bridge
pointer, it is stored in tc->panel_bridge. If any subsequent initialization
step in tc_probe() fails, it appears to return directly without cleaning up
the bridge reference:
tc_probe() {
...
tc_probe_edp_bridge_endpoint(tc);
...
tc->refclk = devm_clk_get_enabled(dev, "ref");
if (IS_ERR(tc->refclk))
return dev_err_probe(dev, PTR_ERR(tc->refclk),
"Failed to get and enable the ref clk\n");
...
}
Could this cause a resource leak on probe error paths?
[Severity: High]
This is a pre-existing issue, but does evaluating tc->hpd_pin here lead to a
negative bitshift later?
Due to initialization order, tc_data is zero-allocated, meaning tc->hpd_pin
is 0 when checked in tc_probe_edp_bridge_endpoint():
if (tc->hpd_pin >= 0)
tc->bridge.ops |= DRM_BRIDGE_OP_DETECT;
Later in tc_probe(), if the DT property is absent, tc->hpd_pin is assigned
-ENODEV (-19).
When the DRM core periodically polls the connector, it calls
tc_bridge_detect(), which executes:
conn = val & BIT(tc->hpd_pin);
Does this lead to undefined behavior by evaluating BIT(-19)?
[ ... ]
> @@ -2609,6 +2600,7 @@ static void tc_remove(struct i2c_client *client)
> {
> struct tc_data *tc = i2c_get_clientdata(client);
>
> + drm_bridge_put(tc->panel_bridge);
> drm_bridge_remove(&tc->bridge);
> }
[Severity: Critical]
Does this unconditional drm_bridge_put() cause a double-free for bridges
acquired in DPI mode?
When operating in DPI mode, tc_probe_dpi_bridge_endpoint() is called instead,
which assigns tc->panel_bridge to a devm-managed bridge:
tc_probe_dpi_bridge_endpoint() {
...
bridge = devm_drm_panel_bridge_add(dev, panel);
...
tc->panel_bridge = bridge;
}
When the device unbinds, tc_remove() drops a reference it does not own. For
the devm-managed bridge, this prematurely drops its refcount to 0, freeing
the bridge immediately. Later, the devres framework will attempt to clean up
the same bridge, causing a double-free.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-drm-bridge-every-panel-v1-0-19cd5277cc8d@bootlin.com?part=6
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 07/11] drm/bridge: waveshare-dsi: don't create a panel_bridge
2026-08-14 14:05 [PATCH 00/11] drm/panel: add a panel_bridge to every panel Luca Ceresoli
` (5 preceding siblings ...)
2026-08-14 14:05 ` [PATCH 06/11] drm/bridge: tc358767: don't create a panel_bridge Luca Ceresoli
@ 2026-08-14 14:05 ` Luca Ceresoli
2026-08-14 14:05 ` [PATCH 08/11] drm/mcde: dsi: simplify device_node management using scoped for_each variant Luca Ceresoli
` (4 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Luca Ceresoli @ 2026-08-14 14:05 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, Jessica Zhang,
Linus Walleij, Inki Dae, Jagan Teki, Marek Szyprowski
Cc: Dmitry Baryshkov, Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel,
linux-kernel, Luca Ceresoli
Now a panel_bridge is automatically created for every drm_panel, so this
panel-only call to drm_of_find_panel_or_bridge() would lead to creating a
second panel_bridge for the same panel.
Fix and simplify the code by just getting a reference to the
already-existing panel_bridge using of_drm_get_bridge_by_endpoint().
As of_drm_get_bridge_by_endpoint() returns a refcouncted bridge, take care
of putting the bridge reference by using bridge->next_bridge.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/waveshare-dsi.c | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/bridge/waveshare-dsi.c b/drivers/gpu/drm/bridge/waveshare-dsi.c
index 420f3b870a74..b79a1d32e679 100644
--- a/drivers/gpu/drm/bridge/waveshare-dsi.c
+++ b/drivers/gpu/drm/bridge/waveshare-dsi.c
@@ -20,7 +20,6 @@
struct ws_bridge {
struct drm_bridge bridge;
- struct drm_bridge *next_bridge;
struct backlight_device *backlight;
struct device *dev;
struct regmap *reg_map;
@@ -89,7 +88,7 @@ static int ws_bridge_bridge_attach(struct drm_bridge *bridge,
{
struct ws_bridge *ws = bridge_to_ws_bridge(bridge);
- return drm_bridge_attach(encoder, ws->next_bridge,
+ return drm_bridge_attach(encoder, ws->bridge.next_bridge,
&ws->bridge, flags);
}
@@ -150,7 +149,6 @@ static struct backlight_device *ws_bridge_create_backlight(struct ws_bridge *ws)
static int ws_bridge_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
- struct drm_panel *panel;
struct ws_bridge *ws;
int ret;
@@ -164,14 +162,10 @@ static int ws_bridge_probe(struct i2c_client *i2c)
if (IS_ERR(ws->reg_map))
return dev_err_probe(dev, PTR_ERR(ws->reg_map), "Failed to allocate regmap\n");
- ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, &panel, NULL);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to find remote panel\n");
-
- ws->next_bridge = devm_drm_panel_bridge_add(dev, panel);
- drm_panel_put(panel);
- if (IS_ERR(ws->next_bridge))
- return PTR_ERR(ws->next_bridge);
+ ws->bridge.next_bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 1, -1);
+ if (IS_ERR(ws->bridge.next_bridge))
+ return dev_err_probe(dev, PTR_ERR(ws->bridge.next_bridge),
+ "Failed to find remote panel\n");
ws->backlight = ws_bridge_create_backlight(ws);
if (IS_ERR(ws->backlight)) {
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH 08/11] drm/mcde: dsi: simplify device_node management using scoped for_each variant
2026-08-14 14:05 [PATCH 00/11] drm/panel: add a panel_bridge to every panel Luca Ceresoli
` (6 preceding siblings ...)
2026-08-14 14:05 ` [PATCH 07/11] drm/bridge: waveshare-dsi: " Luca Ceresoli
@ 2026-08-14 14:05 ` Luca Ceresoli
2026-08-14 14:19 ` sashiko-bot
2026-08-14 14:05 ` [PATCH 09/11] drm/mcde: dsi: don't create a panel_bridge Luca Ceresoli
` (3 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Luca Ceresoli @ 2026-08-14 14:05 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, Jessica Zhang,
Linus Walleij, Inki Dae, Jagan Teki, Marek Szyprowski
Cc: Dmitry Baryshkov, Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel,
linux-kernel, Luca Ceresoli
Before further modifying this function, simpify the struct device_node
management using the scoped variat of for_each_available_child_of_node().
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/mcde/mcde_dsi.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index 694372581840..ce078aa3910f 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -1076,7 +1076,6 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct mcde *mcde = to_mcde(drm);
struct mcde_dsi *d = dev_get_drvdata(dev);
- struct device_node *child;
struct drm_panel *panel = NULL;
struct drm_bridge *bridge __free(drm_bridge_put) = NULL;
@@ -1104,7 +1103,7 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
}
/* Look for a panel as a child to this node */
- for_each_available_child_of_node(dev->of_node, child) {
+ for_each_available_child_of_node_scoped(dev->of_node, child) {
panel = of_drm_find_panel(child);
if (IS_ERR(panel)) {
dev_err(dev, "failed to find panel try bridge (%ld)\n",
@@ -1114,15 +1113,12 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
bridge = of_drm_find_and_get_bridge(child);
if (!bridge) {
dev_err(dev, "failed to find bridge\n");
- of_node_put(child);
return -EINVAL;
}
}
- if (panel || bridge) {
- of_node_put(child);
+ if (panel || bridge)
break;
- }
}
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 08/11] drm/mcde: dsi: simplify device_node management using scoped for_each variant
2026-08-14 14:05 ` [PATCH 08/11] drm/mcde: dsi: simplify device_node management using scoped for_each variant Luca Ceresoli
@ 2026-08-14 14:19 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 14:19 UTC (permalink / raw)
To: Luca Ceresoli; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Missing `drm_bridge_remove()` for `d->bridge` during unbind causes a memory leak and use-after-free risk.
- [High] Deferred probing for panels is broken by mapping `-EPROBE_DEFER` to `-EINVAL`.
--
--- Patch [8]: [PATCH 08/11] drm/mcde: dsi: simplify device_node management using scoped for_each variant ---
commit 6cf52153aa2a545f24c563456ea7265e0b307010
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
drm/mcde: dsi: simplify device_node management using scoped for_each variant
Before further modifying this function, simpify the struct device_node
management using the scoped variat of for_each_available_child_of_node().
> diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
> index 6943725818407..ce078aa3910f2 100644
> --- a/drivers/gpu/drm/mcde/mcde_dsi.c
> +++ b/drivers/gpu/drm/mcde/mcde_dsi.c
[ ... ]
> @@ -1104,7 +1103,7 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
> }
>
> /* Look for a panel as a child to this node */
> - for_each_available_child_of_node(dev->of_node, child) {
> + for_each_available_child_of_node_scoped(dev->of_node, child) {
> panel = of_drm_find_panel(child);
> if (IS_ERR(panel)) {
> dev_err(dev, "failed to find panel try bridge (%ld)\n",
> @@ -1114,15 +1113,12 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
> bridge = of_drm_find_and_get_bridge(child);
> if (!bridge) {
> dev_err(dev, "failed to find bridge\n");
> - of_node_put(child);
> return -EINVAL;
[Severity: High]
This is a pre-existing issue, but does returning -EINVAL here break
deferred probing?
If the panel driver is not yet probed, of_drm_find_panel() returns
ERR_PTR(-EPROBE_DEFER). This error path intercepts that by checking
IS_ERR(panel), setting panel = NULL, and falling back to
of_drm_find_and_get_bridge(). Since the panel's bridge is also not
registered yet, this returns NULL.
Finding no bridge, the driver then unconditionally returns -EINVAL instead
of propagating the -EPROBE_DEFER error. This causes the component bind to
fail permanently rather than deferring, which can lead to a loss of
display output if the MCDE driver probes before the panel driver.
> }
> }
>
> - if (panel || bridge) {
> - of_node_put(child);
> + if (panel || bridge)
> break;
> - }
> }
[Severity: High]
This isn't a bug introduced by this patch, but is there a missing
drm_bridge_remove() in mcde_dsi_unbind() for the bridge registered in
mcde_dsi_bind()?
The driver registers d->bridge using drm_bridge_add() further down in
mcde_dsi_bind(), which acquires a reference and links it into the global
bridge_list.
However, in mcde_dsi_unbind(), the driver fails to call
drm_bridge_remove(), meaning the bridge is never unlinked and its reference
count never drops to zero.
Because d->bridge remains in the global bridge_list even after the device
is unbound and its devres-managed resources are freed, subsequent
iterations of the bridge_list by other drivers could dereference a stale
pointer. Can this cause a use-after-free and potential kernel panic?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-drm-bridge-every-panel-v1-0-19cd5277cc8d@bootlin.com?part=8
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 09/11] drm/mcde: dsi: don't create a panel_bridge
2026-08-14 14:05 [PATCH 00/11] drm/panel: add a panel_bridge to every panel Luca Ceresoli
` (7 preceding siblings ...)
2026-08-14 14:05 ` [PATCH 08/11] drm/mcde: dsi: simplify device_node management using scoped for_each variant Luca Ceresoli
@ 2026-08-14 14:05 ` Luca Ceresoli
2026-08-14 14:24 ` sashiko-bot
2026-08-14 14:05 ` [PATCH 10/11] drm/bridge: fsl-ldb: " Luca Ceresoli
` (2 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Luca Ceresoli @ 2026-08-14 14:05 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, Jessica Zhang,
Linus Walleij, Inki Dae, Jagan Teki, Marek Szyprowski
Cc: Dmitry Baryshkov, Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel,
linux-kernel, Luca Ceresoli
Now a panel_bridge is automatically created for every drm_panel, so this
code (similar to an open-coded variant of drm_of_find_panel_or_bridge())
would lead to creating a second panel_bridge for the same panel.
Fix and simplify the code by just getting a reference to the
already-existing bridge.
Also keep the "connected to non-panel bridge (unsupported)\n" error logic
by using the drm_bridge_is_panel() function.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/mcde/mcde_dsi.c | 41 +++++++++--------------------------------
1 file changed, 9 insertions(+), 32 deletions(-)
diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index ce078aa3910f..ec45440d20d7 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -39,7 +39,6 @@ struct mcde_dsi {
struct device *dev;
struct mcde *mcde;
struct drm_bridge bridge;
- struct drm_panel *panel;
struct mipi_dsi_host dsi_host;
struct mipi_dsi_device *mdsi;
const struct drm_display_mode *mode;
@@ -1076,7 +1075,6 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct mcde *mcde = to_mcde(drm);
struct mcde_dsi *d = dev_get_drvdata(dev);
- struct drm_panel *panel = NULL;
struct drm_bridge *bridge __free(drm_bridge_put) = NULL;
if (!of_get_available_child_count(dev->of_node)) {
@@ -1104,40 +1102,21 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
/* Look for a panel as a child to this node */
for_each_available_child_of_node_scoped(dev->of_node, child) {
- panel = of_drm_find_panel(child);
- if (IS_ERR(panel)) {
- dev_err(dev, "failed to find panel try bridge (%ld)\n",
- PTR_ERR(panel));
- panel = NULL;
-
- bridge = of_drm_find_and_get_bridge(child);
- if (!bridge) {
- dev_err(dev, "failed to find bridge\n");
- return -EINVAL;
- }
+ bridge = of_drm_find_and_get_bridge(child);
+ if (!bridge) {
+ dev_err(dev, "failed to find bridge\n");
+ return -EINVAL;
}
- if (panel || bridge)
- break;
+ break;
}
- if (panel) {
- bridge = drm_panel_bridge_add_typed(panel,
- DRM_MODE_CONNECTOR_DSI);
- drm_panel_put(panel);
- if (IS_ERR(bridge)) {
- dev_err(dev, "error adding panel bridge\n");
- return PTR_ERR(bridge);
- }
- drm_bridge_get(bridge);
- dev_info(dev, "connected to panel\n");
- d->panel = panel;
- } else if (bridge) {
+ if (!bridge) {
+ dev_err(dev, "no panel or bridge\n");
+ return -ENODEV;
+ } else if (!drm_bridge_is_panel(bridge)) {
/* TODO: AV8100 HDMI encoder goes here for example */
dev_info(dev, "connected to non-panel bridge (unsupported)\n");
return -ENODEV;
- } else {
- dev_err(dev, "no panel or bridge\n");
- return -ENODEV;
}
d->bridge.next_bridge = drm_bridge_get(bridge);
@@ -1159,8 +1138,6 @@ static void mcde_dsi_unbind(struct device *dev, struct device *master,
{
struct mcde_dsi *d = dev_get_drvdata(dev);
- if (d->panel)
- drm_panel_bridge_remove(d->bridge.next_bridge);
regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 09/11] drm/mcde: dsi: don't create a panel_bridge
2026-08-14 14:05 ` [PATCH 09/11] drm/mcde: dsi: don't create a panel_bridge Luca Ceresoli
@ 2026-08-14 14:24 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 14:24 UTC (permalink / raw)
To: Luca Ceresoli; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] The reference to the remote bridge (next_bridge) acquired during bind is permanently leaked on unbind.
Pre-existing issues:
- [High] Component unbind is missing drm_bridge_remove(), leading to list corruption and memory leaks upon rebind or module unload.
- [High] The driver returns -EINVAL instead of -EPROBE_DEFER when the downstream bridge is not found, breaking probe deferral.
- [High] Calling devm_clk_get() inside a repeatable component bind function causes a devres leak.
--
--- Patch [9]: [PATCH 09/11] drm/mcde: dsi: don't create a panel_bridge ---
commit 62f9247edbd74ae414416ce89cd9ec90e6811731
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
drm/mcde: dsi: don't create a panel_bridge
Now a panel_bridge is automatically created for every drm_panel, so this
code (similar to an open-coded variant of drm_of_find_panel_or_bridge())
would lead to creating a second panel_bridge for the same panel.
> diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
> index ce078aa3910f2..ec45440d20d70 100644
> --- a/drivers/gpu/drm/mcde/mcde_dsi.c
> +++ b/drivers/gpu/drm/mcde/mcde_dsi.c
[ ... ]
> @@ -1076,7 +1075,6 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
> struct drm_device *drm = data;
> struct mcde *mcde = to_mcde(drm);
> struct mcde_dsi *d = dev_get_drvdata(dev);
> - struct drm_panel *panel = NULL;
[Severity: High]
This is a pre-existing issue, but does calling devm_clk_get() inside
mcde_dsi_bind() cause a devres memory leak?
Since component drivers can be unbound and rebound multiple times (such as
via sysfs or master probe deferral), devres allocations in the bind
path will accumulate every time the component binds, because devres chunks
are bound to the platform device's lifetime, not the component bind/unbind
lifecycle.
[ ... ]
> @@ -1104,40 +1102,21 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
>
> /* Look for a panel as a child to this node */
> for_each_available_child_of_node_scoped(dev->of_node, child) {
> - panel = of_drm_find_panel(child);
> - if (IS_ERR(panel)) {
> - dev_err(dev, "failed to find panel try bridge (%ld)\n",
> - PTR_ERR(panel));
> - panel = NULL;
> -
> - bridge = of_drm_find_and_get_bridge(child);
> - if (!bridge) {
> - dev_err(dev, "failed to find bridge\n");
> - return -EINVAL;
> - }
> + bridge = of_drm_find_and_get_bridge(child);
> + if (!bridge) {
> + dev_err(dev, "failed to find bridge\n");
> + return -EINVAL;
[Severity: High]
This isn't a bug introduced by this patch, but should this return
-EPROBE_DEFER instead of -EINVAL when the downstream bridge is not found?
Returning a hard -EINVAL when of_drm_find_and_get_bridge() returns NULL
aborts the display probe irrevocably if the downstream panel/bridge probes
after the MCDE DSI host, rather than deferring the probe until it is ready.
[ ... ]
> @@ -1159,8 +1138,6 @@ static void mcde_dsi_unbind(struct device *dev, struct device *master,
> {
> struct mcde_dsi *d = dev_get_drvdata(dev);
>
> - if (d->panel)
> - drm_panel_bridge_remove(d->bridge.next_bridge);
[Severity: High]
Since this patch removes drm_panel_bridge_remove(), is a call to
drm_bridge_put(d->bridge.next_bridge) needed here to balance the reference
acquired in mcde_dsi_bind()?
In mcde_dsi_bind(), a reference to the downstream bridge is acquired and
stored via:
d->bridge.next_bridge = drm_bridge_get(bridge);
Without a corresponding drm_bridge_put() here, the reference is stranded.
Upon the next rebind, a new reference is acquired and overwrites the old
pointer, permanently leaking the downstream bridge reference.
[Severity: High]
This is a pre-existing issue, but is a call to drm_bridge_remove(&d->bridge)
missing from mcde_dsi_unbind()?
mcde_dsi_bind() registers the bridge globally via
drm_bridge_add(&d->bridge). Without drm_bridge_remove() in the unbind path,
a rebind will call drm_bridge_add() on the same bridge object.
This invokes list_del_init(&bridge->list) without holding the bridge_lock,
introducing a data race that can corrupt the global bridge_list if traversed
concurrently. Additionally, it prevents the bridge's refcount from ever
reaching zero.
> regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
> PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-drm-bridge-every-panel-v1-0-19cd5277cc8d@bootlin.com?part=9
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 10/11] drm/bridge: fsl-ldb: don't create a panel_bridge
2026-08-14 14:05 [PATCH 00/11] drm/panel: add a panel_bridge to every panel Luca Ceresoli
` (8 preceding siblings ...)
2026-08-14 14:05 ` [PATCH 09/11] drm/mcde: dsi: don't create a panel_bridge Luca Ceresoli
@ 2026-08-14 14:05 ` Luca Ceresoli
2026-08-14 14:18 ` sashiko-bot
2026-08-14 14:05 ` [PATCH 11/11] drm/bridge: samsung-dsim: " Luca Ceresoli
2026-08-14 14:13 ` [PATCH 00/11] drm/panel: add a panel_bridge to every panel Luca Ceresoli
11 siblings, 1 reply; 21+ messages in thread
From: Luca Ceresoli @ 2026-08-14 14:05 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, Jessica Zhang,
Linus Walleij, Inki Dae, Jagan Teki, Marek Szyprowski
Cc: Dmitry Baryshkov, Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel,
linux-kernel, Luca Ceresoli
Now a panel_bridge is automatically created for every drm_panel, so this
code, looking only for a panel and unconditionally creating a panel_bridge
for it, would lead to creating a second panel_bridge for the same panel.
Fix and simplify the code by just getting a reference to the
already-existing bridge from of_drm_find_and_get_bridge().
And since of_drm_find_and_get_bridge() returns a refcounted bridge, take
care of putting the bridge reference by using bridge->next_bridge.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/fsl-ldb.c | 18 +++++-------------
1 file changed, 5 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c
index 26cc72948f31..c289139b9273 100644
--- a/drivers/gpu/drm/bridge/fsl-ldb.c
+++ b/drivers/gpu/drm/bridge/fsl-ldb.c
@@ -86,7 +86,6 @@ static const struct fsl_ldb_devdata fsl_ldb_devdata[] = {
struct fsl_ldb {
struct device *dev;
struct drm_bridge bridge;
- struct drm_bridge *panel_bridge;
struct clk *clk;
struct regmap *regmap;
const struct fsl_ldb_devdata *devdata;
@@ -119,7 +118,7 @@ static int fsl_ldb_attach(struct drm_bridge *bridge,
{
struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
- return drm_bridge_attach(encoder, fsl_ldb->panel_bridge,
+ return drm_bridge_attach(encoder, fsl_ldb->bridge.next_bridge,
bridge, flags);
}
@@ -298,7 +297,6 @@ static int fsl_ldb_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *panel_node;
struct device_node *remote1, *remote2;
- struct drm_panel *panel;
struct fsl_ldb *fsl_ldb;
int dual_link;
@@ -339,19 +337,13 @@ static int fsl_ldb_probe(struct platform_device *pdev)
fsl_ldb_is_dual(fsl_ldb) ? "dual-link mode" :
fsl_ldb->ch0_enabled ? "channel 0" : "channel 1");
- panel = of_drm_find_panel(panel_node);
- of_node_put(panel_node);
- if (IS_ERR(panel))
- return PTR_ERR(panel);
-
if (of_property_present(dev->of_node, "nxp,enable-termination-resistor"))
fsl_ldb->use_termination_resistor = true;
- fsl_ldb->panel_bridge = devm_drm_panel_bridge_add(dev, panel);
- drm_panel_put(panel);
- if (IS_ERR(fsl_ldb->panel_bridge))
- return PTR_ERR(fsl_ldb->panel_bridge);
-
+ fsl_ldb->bridge.next_bridge = of_drm_find_and_get_bridge(panel_node);
+ of_node_put(panel_node);
+ if (!fsl_ldb->bridge.next_bridge)
+ return -ENODEV;
if (fsl_ldb_is_dual(fsl_ldb)) {
struct device_node *port1, *port2;
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 10/11] drm/bridge: fsl-ldb: don't create a panel_bridge
2026-08-14 14:05 ` [PATCH 10/11] drm/bridge: fsl-ldb: " Luca Ceresoli
@ 2026-08-14 14:18 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 14:18 UTC (permalink / raw)
To: Luca Ceresoli; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Returning `-ENODEV` instead of `-EPROBE_DEFER` when the downstream bridge is not found permanently breaks probe deferral.
--
--- Patch [10]: [PATCH 10/11] drm/bridge: fsl-ldb: don't create a panel_bridge ---
commit cea029e41728150133bf916d81ee3178f44b5540
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
drm/bridge: fsl-ldb: don't create a panel_bridge
This commit simplifies fsl_ldb_probe() by retrieving an existing bridge via
of_drm_find_and_get_bridge() rather than unconditionally creating a second
panel_bridge for the same panel. It also ensures the refcounted bridge is
properly managed.
> diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c
> --- a/drivers/gpu/drm/bridge/fsl-ldb.c
> +++ b/drivers/gpu/drm/bridge/fsl-ldb.c
[ ... ]
> @@ -298,7 +297,6 @@ static int fsl_ldb_probe(struct platform_device *pdev)
[ ... ]
> - panel = of_drm_find_panel(panel_node);
> - of_node_put(panel_node);
> - if (IS_ERR(panel))
> - return PTR_ERR(panel);
> -
> if (of_property_present(dev->of_node, "nxp,enable-termination-resistor"))
> fsl_ldb->use_termination_resistor = true;
>
> - fsl_ldb->panel_bridge = devm_drm_panel_bridge_add(dev, panel);
> - drm_panel_put(panel);
> - if (IS_ERR(fsl_ldb->panel_bridge))
> - return PTR_ERR(fsl_ldb->panel_bridge);
> -
> + fsl_ldb->bridge.next_bridge = of_drm_find_and_get_bridge(panel_node);
> + of_node_put(panel_node);
> + if (!fsl_ldb->bridge.next_bridge)
> + return -ENODEV;
[Severity: High]
Does this change break probe deferral when the downstream driver loads later?
Previously, if the downstream panel driver hadn't loaded yet,
of_drm_find_panel() returned -EPROBE_DEFER, which was propagated back to
allow fsl_ldb_probe() to try again later.
With the updated code, if of_drm_find_and_get_bridge() returns NULL because
the downstream bridge driver hasn't successfully probed yet, this returns a
hard -ENODEV error. Could this permanently fail the display pipeline
initialization depending on module load order? Should this return
-EPROBE_DEFER instead?
>
> if (fsl_ldb_is_dual(fsl_ldb)) {
> struct device_node *port1, *port2;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-drm-bridge-every-panel-v1-0-19cd5277cc8d@bootlin.com?part=10
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 11/11] drm/bridge: samsung-dsim: don't create a panel_bridge
2026-08-14 14:05 [PATCH 00/11] drm/panel: add a panel_bridge to every panel Luca Ceresoli
` (9 preceding siblings ...)
2026-08-14 14:05 ` [PATCH 10/11] drm/bridge: fsl-ldb: " Luca Ceresoli
@ 2026-08-14 14:05 ` Luca Ceresoli
2026-08-14 14:23 ` sashiko-bot
2026-08-14 14:13 ` [PATCH 00/11] drm/panel: add a panel_bridge to every panel Luca Ceresoli
11 siblings, 1 reply; 21+ messages in thread
From: Luca Ceresoli @ 2026-08-14 14:05 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, Jessica Zhang,
Linus Walleij, Inki Dae, Jagan Teki, Marek Szyprowski
Cc: Dmitry Baryshkov, Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel,
linux-kernel, Luca Ceresoli
Now a panel_bridge is automatically created for every drm_panel, so this
code (similar to an open-coded variant of drm_of_find_panel_or_bridge())
would lead to creating a second panel_bridge for the same panel.
Fix and simplify the code by just getting a reference to the
already-existing bridge.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/samsung-dsim.c | 19 +++----------------
1 file changed, 3 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
index 5457a7b02a33..55d4e3aedb3d 100644
--- a/drivers/gpu/drm/bridge/samsung-dsim.c
+++ b/drivers/gpu/drm/bridge/samsung-dsim.c
@@ -1898,7 +1898,6 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
struct device *dev = dsi->dev;
struct device_node *np = dev->of_node;
struct device_node *remote;
- struct drm_panel *panel;
int ret = 0;
/*
@@ -1931,21 +1930,9 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
if (!remote)
return -ENODEV;
- panel = of_drm_find_panel(remote);
- if (!IS_ERR(panel)) {
- next_bridge = devm_drm_panel_bridge_add(dev, panel);
- drm_panel_put(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);
- }
- } else {
- next_bridge = of_drm_find_and_get_bridge(remote);
- if (!next_bridge)
- ret = -EINVAL;
- }
+ next_bridge = of_drm_find_and_get_bridge(remote);
+ if (!next_bridge)
+ ret = -EINVAL;
of_node_put(remote);
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 11/11] drm/bridge: samsung-dsim: don't create a panel_bridge
2026-08-14 14:05 ` [PATCH 11/11] drm/bridge: samsung-dsim: " Luca Ceresoli
@ 2026-08-14 14:23 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 14:23 UTC (permalink / raw)
To: Luca Ceresoli; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Manual gpiod_put() on a devres-managed GPIO and failure to clear the dsi->te_gpio pointer leads to UAF and double free.
--
--- Patch [11]: [PATCH 11/11] drm/bridge: samsung-dsim: don't create a panel_bridge ---
commit 3269367ff66fe6dd842314973873ed18b84437ca
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
drm/bridge: samsung-dsim: don't create a panel_bridge
Now a panel_bridge is automatically created for every drm_panel, so this
code would lead to creating a second panel_bridge for the same panel. This
commit simplifies the code by just getting a reference to the already-existing
bridge.
> diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
> index e2fc69fc51b60..ca419edb1c1a6 100644
> --- a/drivers/gpu/drm/bridge/samsung-dsim.c
> +++ b/drivers/gpu/drm/bridge/samsung-dsim.c
[ ... ]
> @@ -1931,21 +1930,9 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
> if (!remote)
> return -ENODEV;
>
> - panel = of_drm_find_panel(remote);
> - if (!IS_ERR(panel)) {
[ ... ]
> - } else {
> - next_bridge = of_drm_find_and_get_bridge(remote);
> - if (!next_bridge)
> - ret = -EINVAL;
> - }
> + next_bridge = of_drm_find_and_get_bridge(remote);
> + if (!next_bridge)
> + ret = -EINVAL;
>
> of_node_put(remote);
>
[Severity: Critical]
This isn't a bug introduced by this patch, but there is a pre-existing double
free and use-after-free issue in this driver related to the TE IRQ handling,
which is called from the error path of samsung_dsim_host_attach().
When a CMD mode device attaches, samsung_dsim_register_te_irq() allocates
dsi->te_gpio using devm_gpiod_get_optional(). If it fails later, or when
samsung_dsim_unregister_te_irq() is called on detach, the code manually calls
gpiod_put(dsi->te_gpio) without clearing the pointer.
Because the GPIO was allocated with devres, won't this manual gpiod_put()
cause a double free when the device unbinds and devres cleans up?
drivers/gpu/drm/bridge/samsung-dsim.c:samsung_dsim_unregister_te_irq() {
if (dsi->te_gpio) {
free_irq(gpiod_to_irq(dsi->te_gpio), dsi);
gpiod_put(dsi->te_gpio);
}
}
This manual cleanup is also present in the error path of
samsung_dsim_register_te_irq():
drivers/gpu/drm/bridge/samsung-dsim.c:samsung_dsim_register_te_irq() {
...
if (ret) {
dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
gpiod_put(dsi->te_gpio);
return ret;
}
...
}
Additionally, since samsung_dsim_unregister_te_irq() leaves a stale pointer
in dsi->te_gpio, could this lead to a use-after-free?
If a VIDEO mode device subsequently attaches, it skips registering the TE IRQ
and leaves the stale pointer in place. When it detaches,
samsung_dsim_host_detach() unconditionally calls
samsung_dsim_unregister_te_irq(), which would call gpiod_to_irq() on the
already freed pointer.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-drm-bridge-every-panel-v1-0-19cd5277cc8d@bootlin.com?part=11
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 00/11] drm/panel: add a panel_bridge to every panel
2026-08-14 14:05 [PATCH 00/11] drm/panel: add a panel_bridge to every panel Luca Ceresoli
` (10 preceding siblings ...)
2026-08-14 14:05 ` [PATCH 11/11] drm/bridge: samsung-dsim: " Luca Ceresoli
@ 2026-08-14 14:13 ` Luca Ceresoli
11 siblings, 0 replies; 21+ messages in thread
From: Luca Ceresoli @ 2026-08-14 14:13 UTC (permalink / raw)
To: Luca Ceresoli, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Jessica Zhang, Linus Walleij, Inki Dae,
Jagan Teki, Marek Szyprowski
Cc: Dmitry Baryshkov, Hui Pu, Ian Ray, Thomas Petazzoni, dri-devel,
linux-kernel
Hello,
On Fri Aug 14, 2026 at 4:05 PM CEST, Luca Ceresoli wrote:
> This series makes every drm_panel create a drm_panel_bridge wrapping it, as
> previously discussed [1][2].
I'm afraid I forgot adding an [RFC] tag to this series before
sending. Sorry about that.
> == Series outline
[...]
> 2. Address circular module dependency that would arise in the following
> commit [RFC!]
>
> drm/panel: merge the drm_kms_helper module into the drm module
>
> 3. The main commit
>
> drm/bridge: panel: add a panel_bridge to every panel
FYI, these two commits are those in the biggest need for comments.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 21+ messages in thread