* [PATCH 0/5] drm/panel: refcounting panel lookups and references
@ 2026-06-26 12:03 Albert Esteve
2026-06-26 12:03 ` [PATCH 1/5] drm/panel: have drm_panel_add/remove manage a list reference Albert Esteve
` (4 more replies)
0 siblings, 5 replies; 14+ messages in thread
From: Albert Esteve @ 2026-06-26 12:03 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Inki Dae, Jagan Teki, Marek Szyprowski,
Laurentiu Palcu, Lucas Stach, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Paul Cercueil,
Linus Walleij, Marek Vasut, Stefan Agner, Tomi Valkeinen,
Laurent Pinchart, Kieran Bingham, Geert Uytterhoeven, Magnus Damm,
Biju Das, Sandy Huang, Heiko Stübner, Andy Yan,
Yannick Fertre, Raphael Gallais-Pou, Philippe Cornu,
Maxime Coquelin, Alexandre Torgue, Chen-Yu Tsai, Samuel Holland,
Jyri Sarha, Jingoo Han, Seung-Woo Kim, Kyungmin Park,
Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Alison Wang,
Paul Kocialkowski, Alain Volmat, Raphael Gallais-Pou,
Thierry Reding, Mikko Perttunen, Jonathan Hunter
Cc: dri-devel, linux-kernel, imx, linux-arm-kernel, linux-mips,
linux-renesas-soc, linux-rockchip, linux-stm32, linux-sunxi,
linux-samsung-soc, linux-tegra, Albert Esteve
The drm_panel subsystem provides kref-based reference counting [1]
(drm_panel_get/put) but almost nothing in the tree actually uses it.
This results in a systemic use-after-free pattern throughout the codebase.
This series aims to close all those issues.
Patches 1-2: fix the infrastructure. drm_panel_add/remove now keep
a counted reference for the list entry. drm_panel_bridge_add_typed()
now holds a counted reference for the lifetime of the panel_bridge.
Patch 3: change the semantics of of_drm_find_panel() and
find_panel_by_fwnode(). They now acquire a reference before returning,
under panel_lock. Callers are responsible for calling drm_panel_put()
when they no longer need the pointer.
Patches 4-5: update all in-tree callers of of_drm_find_panel() and
drm_of_find_panel_or_bridge() to release the reference at the
appropriate point. Two patterns are repeated in these fixes:
- Bridge-wrapping: the panel is passed to devm_drm_panel_bridge_add()
or equivalent, which acquires its own reference. The caller (including
devm_drm_of_get_bridge() and drmm_of_get_bridge()) releases its lookup
reference immediately after.
- Store-and-use: the panel pointer is kept in a driver struct and
used directly for the device lifetime. The reference is released in the
remove/unbind path, or via devm_add_action_or_reset() where no explicit
teardown function exists.
In order to catch all places in the tree that required releasing the
reference, the search was assisted by an AI model. Specifically, a
Coccinelle script was designed to address the trivial changes (not
included in the series). Although a few required manual fixes, with goto
labels or bracket additions. Additionally, the model helped to discern implicit
teardown paths that were addressed with devm_add_action_or_reset() calls.
Thus, these commits have the Assisted-by label following the project guidelines.
No functional change is intended for any driver. The reference
counting only affects object lifetime; panel operations are unaffected.
[1] https://lore.kernel.org/all/20250331-b4-panel-refcounting-v4-0-dad50c60c6c9@redhat.com/
Signed-off-by: Albert Esteve <aesteve@redhat.com>
---
Albert Esteve (5):
drm/panel: have drm_panel_add/remove manage a list reference
drm/bridge/panel: hold a reference to the wrapped panel
drm/panel: make *find_panel*() return a counted reference
drm/bridge: release panel reference on all lookup exit paths
drm: release panel reference after panel bridge creation
drivers/gpu/drm/bridge/analogix/analogix-anx6345.c | 3 +++
drivers/gpu/drm/bridge/panel.c | 18 ++++++++++++----
drivers/gpu/drm/drm_panel.c | 24 +++++++++++++++++-----
drivers/gpu/drm/exynos/exynos_dp.c | 10 +++++++++
drivers/gpu/drm/exynos/exynos_drm_dpi.c | 3 +++
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c | 18 ++++++++++++++++
drivers/gpu/drm/imx/dcss/dcss-kms.c | 3 +++
drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 4 +++-
drivers/gpu/drm/logicvc/logicvc_interface.c | 12 +++++++++++
drivers/gpu/drm/mcde/mcde_drv.c | 1 +
drivers/gpu/drm/mcde/mcde_dsi.c | 1 +
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 1 +
drivers/gpu/drm/omapdrm/dss/output.c | 1 +
drivers/gpu/drm/pl111/pl111_drv.c | 1 +
drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c | 1 +
drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c | 1 +
drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c | 1 +
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 11 ++++++++++
drivers/gpu/drm/rockchip/rockchip_lvds.c | 1 +
drivers/gpu/drm/rockchip/rockchip_rgb.c | 1 +
drivers/gpu/drm/sti/sti_dvo.c | 3 +++
drivers/gpu/drm/stm/ltdc.c | 1 +
drivers/gpu/drm/stm/lvds.c | 3 +++
drivers/gpu/drm/sun4i/sun4i_lvds.c | 13 ++++++++++++
drivers/gpu/drm/sun4i/sun4i_rgb.c | 13 ++++++++++++
drivers/gpu/drm/sun4i/sun4i_tcon.c | 2 ++
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 2 ++
drivers/gpu/drm/tegra/dsi.c | 1 +
drivers/gpu/drm/tegra/output.c | 3 +++
drivers/gpu/drm/tidss/tidss_kms.c | 16 ++++++++++-----
drivers/gpu/drm/tve200/tve200_drv.c | 1 +
31 files changed, 159 insertions(+), 15 deletions(-)
---
base-commit: 502d801f0ab03e4f32f9a33d203154ce84887921
change-id: 20260513-drm_refcount_wiring-4e5e757e9047
Best regards,
--
Albert Esteve <aesteve@redhat.com>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 1/5] drm/panel: have drm_panel_add/remove manage a list reference
2026-06-26 12:03 [PATCH 0/5] drm/panel: refcounting panel lookups and references Albert Esteve
@ 2026-06-26 12:03 ` Albert Esteve
2026-06-26 12:47 ` Maxime Ripard
2026-06-26 12:03 ` [PATCH 2/5] drm/bridge/panel: hold a reference to the wrapped panel Albert Esteve
` (3 subsequent siblings)
4 siblings, 1 reply; 14+ messages in thread
From: Albert Esteve @ 2026-06-26 12:03 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Inki Dae, Jagan Teki, Marek Szyprowski,
Laurentiu Palcu, Lucas Stach, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Paul Cercueil,
Linus Walleij, Marek Vasut, Stefan Agner, Tomi Valkeinen,
Laurent Pinchart, Kieran Bingham, Geert Uytterhoeven, Magnus Damm,
Biju Das, Sandy Huang, Heiko Stübner, Andy Yan,
Yannick Fertre, Raphael Gallais-Pou, Philippe Cornu,
Maxime Coquelin, Alexandre Torgue, Chen-Yu Tsai, Samuel Holland,
Jyri Sarha, Jingoo Han, Seung-Woo Kim, Kyungmin Park,
Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Alison Wang,
Paul Kocialkowski, Alain Volmat, Raphael Gallais-Pou,
Thierry Reding, Mikko Perttunen, Jonathan Hunter
Cc: dri-devel, linux-kernel, imx, linux-arm-kernel, linux-mips,
linux-renesas-soc, linux-rockchip, linux-stm32, linux-sunxi,
linux-samsung-soc, linux-tegra, Albert Esteve
The global panel_list holds raw pointers to drm_panel objects.
Nothing prevents a panel from being freed while it is still linked
in the list: if a driver's probe calls drm_panel_add() and then
fails at a later step, panel->list remains in panel_list. Any
subsequent call to of_drm_find_panel() that iterates the list will
dereference freed memory.
Have drm_panel_add() acquire a reference via drm_panel_get() before
inserting the panel into the list, and have drm_panel_remove() drop
it via drm_panel_put() after removing the panel from the list. The
global registry now holds a counted reference for as long as the
panel is listed, ensuring the object outlives any concurrent lookup.
Signed-off-by: Albert Esteve <aesteve@redhat.com>
---
drivers/gpu/drm/drm_panel.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
index 2c5649e433dfb..545fe93dc28fe 100644
--- a/drivers/gpu/drm/drm_panel.c
+++ b/drivers/gpu/drm/drm_panel.c
@@ -81,6 +81,7 @@ static void drm_panel_init(struct drm_panel *panel, struct device *dev,
*/
void drm_panel_add(struct drm_panel *panel)
{
+ drm_panel_get(panel);
mutex_lock(&panel_lock);
list_add_tail(&panel->list, &panel_list);
mutex_unlock(&panel_lock);
@@ -98,6 +99,7 @@ void drm_panel_remove(struct drm_panel *panel)
mutex_lock(&panel_lock);
list_del_init(&panel->list);
mutex_unlock(&panel_lock);
+ drm_panel_put(panel);
}
EXPORT_SYMBOL(drm_panel_remove);
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 2/5] drm/bridge/panel: hold a reference to the wrapped panel
2026-06-26 12:03 [PATCH 0/5] drm/panel: refcounting panel lookups and references Albert Esteve
2026-06-26 12:03 ` [PATCH 1/5] drm/panel: have drm_panel_add/remove manage a list reference Albert Esteve
@ 2026-06-26 12:03 ` Albert Esteve
2026-06-26 12:48 ` Maxime Ripard
2026-06-26 12:03 ` [PATCH 3/5] drm/panel: make *find_panel*() return a counted reference Albert Esteve
` (2 subsequent siblings)
4 siblings, 1 reply; 14+ messages in thread
From: Albert Esteve @ 2026-06-26 12:03 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Inki Dae, Jagan Teki, Marek Szyprowski,
Laurentiu Palcu, Lucas Stach, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Paul Cercueil,
Linus Walleij, Marek Vasut, Stefan Agner, Tomi Valkeinen,
Laurent Pinchart, Kieran Bingham, Geert Uytterhoeven, Magnus Damm,
Biju Das, Sandy Huang, Heiko Stübner, Andy Yan,
Yannick Fertre, Raphael Gallais-Pou, Philippe Cornu,
Maxime Coquelin, Alexandre Torgue, Chen-Yu Tsai, Samuel Holland,
Jyri Sarha, Jingoo Han, Seung-Woo Kim, Kyungmin Park,
Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Alison Wang,
Paul Kocialkowski, Alain Volmat, Raphael Gallais-Pou,
Thierry Reding, Mikko Perttunen, Jonathan Hunter
Cc: dri-devel, linux-kernel, imx, linux-arm-kernel, linux-mips,
linux-renesas-soc, linux-rockchip, linux-stm32, linux-sunxi,
linux-samsung-soc, linux-tegra, Albert Esteve
drm_panel_bridge_add_typed() stores a pointer to the drm_panel it
wraps, but never acquires a reference to it. If the panel device
goes away while a panel_bridge still exists, the dangling pointer can
be dereferenced through panel_bridge->panel.
Acquire a reference in drm_panel_bridge_add_typed() with drm_panel_get()
and release it in each teardown path.
Signed-off-by: Albert Esteve <aesteve@redhat.com>
---
drivers/gpu/drm/bridge/panel.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 4978ec98a0828..6b98ad19508df 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -294,7 +294,7 @@ struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
return (void *)panel_bridge;
panel_bridge->connector_type = connector_type;
- panel_bridge->panel = panel;
+ panel_bridge->panel = drm_panel_get(panel);
panel_bridge->bridge.of_node = panel->dev->of_node;
panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES;
@@ -316,6 +316,7 @@ EXPORT_SYMBOL(drm_panel_bridge_add_typed);
void drm_panel_bridge_remove(struct drm_bridge *bridge)
{
struct panel_bridge *panel_bridge;
+ struct drm_panel *panel;
if (!bridge)
return;
@@ -326,10 +327,12 @@ void drm_panel_bridge_remove(struct drm_bridge *bridge)
}
panel_bridge = drm_bridge_to_panel_bridge(bridge);
+ panel = panel_bridge->panel;
drm_bridge_remove(bridge);
/* TODO remove this after reworking panel_bridge lifetime */
- devm_drm_put_bridge(panel_bridge->panel->dev, bridge);
+ devm_drm_put_bridge(panel->dev, bridge);
+ drm_panel_put(panel);
}
EXPORT_SYMBOL(drm_panel_bridge_remove);
@@ -357,11 +360,14 @@ EXPORT_SYMBOL(drm_panel_bridge_set_orientation);
static void devm_drm_panel_bridge_release(struct device *dev, void *res)
{
struct drm_bridge *bridge = *(struct drm_bridge **)res;
+ struct panel_bridge *panel_bridge;
if (!bridge)
return;
+ panel_bridge = drm_bridge_to_panel_bridge(bridge);
drm_bridge_remove(bridge);
+ drm_panel_put(panel_bridge->panel);
}
/**
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 3/5] drm/panel: make *find_panel*() return a counted reference
2026-06-26 12:03 [PATCH 0/5] drm/panel: refcounting panel lookups and references Albert Esteve
2026-06-26 12:03 ` [PATCH 1/5] drm/panel: have drm_panel_add/remove manage a list reference Albert Esteve
2026-06-26 12:03 ` [PATCH 2/5] drm/bridge/panel: hold a reference to the wrapped panel Albert Esteve
@ 2026-06-26 12:03 ` Albert Esteve
2026-06-26 12:50 ` Maxime Ripard
2026-06-26 12:03 ` [PATCH 4/5] drm/bridge: release panel reference on all lookup exit paths Albert Esteve
2026-06-26 12:03 ` [PATCH 5/5] drm: release panel reference after panel bridge creation Albert Esteve
4 siblings, 1 reply; 14+ messages in thread
From: Albert Esteve @ 2026-06-26 12:03 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Inki Dae, Jagan Teki, Marek Szyprowski,
Laurentiu Palcu, Lucas Stach, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Paul Cercueil,
Linus Walleij, Marek Vasut, Stefan Agner, Tomi Valkeinen,
Laurent Pinchart, Kieran Bingham, Geert Uytterhoeven, Magnus Damm,
Biju Das, Sandy Huang, Heiko Stübner, Andy Yan,
Yannick Fertre, Raphael Gallais-Pou, Philippe Cornu,
Maxime Coquelin, Alexandre Torgue, Chen-Yu Tsai, Samuel Holland,
Jyri Sarha, Jingoo Han, Seung-Woo Kim, Kyungmin Park,
Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Alison Wang,
Paul Kocialkowski, Alain Volmat, Raphael Gallais-Pou,
Thierry Reding, Mikko Perttunen, Jonathan Hunter
Cc: dri-devel, linux-kernel, imx, linux-arm-kernel, linux-mips,
linux-renesas-soc, linux-rockchip, linux-stm32, linux-sunxi,
linux-samsung-soc, linux-tegra, Albert Esteve
Callers of of_drm_find_panel() receive a pointer with no reference
held, creating a window where the panel device can be unregistered
and freed between the lookup and first use (e.g., drm_panel_prepare()).
find_panel_by_fwnode() is the fwnode counterpart of of_drm_find_panel().
drm_panel_add_follower() worked around the missing panel kref by calling
get_device() on the panel's underlying struct device. However, get_device()
only prevents the device kobject from being freed. It does not prevent the
panel's kzalloc()'d container memory from being released when the kref
reaches zero.
Fix both lookup functions by acquiring a reference with drm_panel_get()
before returning, under panel_lock. Callers are now responsible for calling
drm_panel_put() when they no longer need the pointer.
Signed-off-by: Albert Esteve <aesteve@redhat.com>
---
drivers/gpu/drm/drm_panel.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
index 545fe93dc28fe..a00ae98ed0956 100644
--- a/drivers/gpu/drm/drm_panel.c
+++ b/drivers/gpu/drm/drm_panel.c
@@ -458,14 +458,17 @@ EXPORT_SYMBOL(__devm_drm_panel_alloc);
#ifdef CONFIG_OF
/**
- * of_drm_find_panel - look up a panel using a device tree node
+ * of_drm_find_panel - look up and reference a panel by device tree node
* @np: device tree node of the panel
*
* Searches the set of registered panels for one that matches the given device
- * tree node. If a matching panel is found, return a pointer to it.
+ * tree node. If a matching panel is found, the panel's reference count is
+ * incremented before returning a pointer to it. The caller must call
+ * drm_panel_put() when it no longer needs the panel pointer.
*
- * Return: A pointer to the panel registered for the specified device tree
- * node or an ERR_PTR() if no panel matching the device tree node can be found.
+ * Return: A reference-counted pointer to the panel registered for the specified
+ * device tree node or an ERR_PTR() if no panel matching the device tree node
+ * can be found.
*
* Possible error codes returned by this function:
*
@@ -484,6 +487,7 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np)
list_for_each_entry(panel, &panel_list, list) {
if (panel->dev->of_node == np) {
+ drm_panel_get(panel);
mutex_unlock(&panel_lock);
return panel;
}
@@ -538,7 +542,13 @@ int of_drm_get_panel_orientation(const struct device_node *np,
EXPORT_SYMBOL(of_drm_get_panel_orientation);
#endif
-/* Find panel by fwnode. This should be identical to of_drm_find_panel(). */
+/*
+ * Find panel by fwnode, returning a counted reference.
+ *
+ * Behaves identically to of_drm_find_panel(). On success the returned
+ * pointer has been passed through drm_panel_get(); the caller must call
+ * drm_panel_put() when done with it.
+ */
static struct drm_panel *find_panel_by_fwnode(const struct fwnode_handle *fwnode)
{
struct drm_panel *panel;
@@ -550,6 +560,7 @@ static struct drm_panel *find_panel_by_fwnode(const struct fwnode_handle *fwnode
list_for_each_entry(panel, &panel_list, list) {
if (dev_fwnode(panel->dev) == fwnode) {
+ drm_panel_get(panel);
mutex_unlock(&panel_lock);
return panel;
}
@@ -686,6 +697,7 @@ void drm_panel_remove_follower(struct drm_panel_follower *follower)
mutex_unlock(&panel->follower_lock);
put_device(panel->dev);
+ drm_panel_put(panel);
}
EXPORT_SYMBOL(drm_panel_remove_follower);
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 4/5] drm/bridge: release panel reference on all lookup exit paths
2026-06-26 12:03 [PATCH 0/5] drm/panel: refcounting panel lookups and references Albert Esteve
` (2 preceding siblings ...)
2026-06-26 12:03 ` [PATCH 3/5] drm/panel: make *find_panel*() return a counted reference Albert Esteve
@ 2026-06-26 12:03 ` Albert Esteve
2026-06-26 12:53 ` Maxime Ripard
2026-06-26 12:03 ` [PATCH 5/5] drm: release panel reference after panel bridge creation Albert Esteve
4 siblings, 1 reply; 14+ messages in thread
From: Albert Esteve @ 2026-06-26 12:03 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Inki Dae, Jagan Teki, Marek Szyprowski,
Laurentiu Palcu, Lucas Stach, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Paul Cercueil,
Linus Walleij, Marek Vasut, Stefan Agner, Tomi Valkeinen,
Laurent Pinchart, Kieran Bingham, Geert Uytterhoeven, Magnus Damm,
Biju Das, Sandy Huang, Heiko Stübner, Andy Yan,
Yannick Fertre, Raphael Gallais-Pou, Philippe Cornu,
Maxime Coquelin, Alexandre Torgue, Chen-Yu Tsai, Samuel Holland,
Jyri Sarha, Jingoo Han, Seung-Woo Kim, Kyungmin Park,
Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Alison Wang,
Paul Kocialkowski, Alain Volmat, Raphael Gallais-Pou,
Thierry Reding, Mikko Perttunen, Jonathan Hunter
Cc: dri-devel, linux-kernel, imx, linux-arm-kernel, linux-mips,
linux-renesas-soc, linux-rockchip, linux-stm32, linux-sunxi,
linux-samsung-soc, linux-tegra, Albert Esteve
of_drm_find_panel() and drm_of_find_panel_or_bridge() now return a
counted reference that the caller must release with drm_panel_put().
For bridge drivers that immediately wrap the panel in a panel_bridge
(which acquires its own reference), release the lookup reference right
after the bridge creation call.
For analogix-anx6345, which stores the panel for direct use, release
the reference in the i2c remove path.
For platform drivers using analogix_dp_core with a component lifecycle
(exynos_dp, rockchip analogix_dp), release the lookup reference in the
platform remove() function. The panel_bridge created during bind() holds
a separate reference that devm cleanup releases after remove() returns.
Also fix devm_drm_of_get_bridge() and drmm_of_get_bridge() in
bridge/panel.c itself: both call drm_of_find_panel_or_bridge() and
then pass the panel to devm/drmm_panel_bridge_add(), which acquires
its own reference via drm_panel_bridge_add_typed(). The lookup
reference was never released; add drm_panel_put() after each bridge
creation call.
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Albert Esteve <aesteve@redhat.com>
---
drivers/gpu/drm/bridge/analogix/analogix-anx6345.c | 3 +++
drivers/gpu/drm/bridge/panel.c | 8 ++++++--
drivers/gpu/drm/exynos/exynos_dp.c | 10 ++++++++++
drivers/gpu/drm/exynos/exynos_drm_dpi.c | 3 +++
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c | 18 ++++++++++++++++++
drivers/gpu/drm/logicvc/logicvc_interface.c | 12 ++++++++++++
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 11 +++++++++++
drivers/gpu/drm/sti/sti_dvo.c | 3 +++
drivers/gpu/drm/stm/lvds.c | 3 +++
drivers/gpu/drm/sun4i/sun4i_lvds.c | 13 +++++++++++++
drivers/gpu/drm/sun4i/sun4i_rgb.c | 13 +++++++++++++
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 2 ++
drivers/gpu/drm/tegra/dsi.c | 1 +
drivers/gpu/drm/tegra/output.c | 3 +++
14 files changed, 101 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
index f3fe47b12edca..1fe11b075f860 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
@@ -756,6 +756,9 @@ static void anx6345_i2c_remove(struct i2c_client *client)
{
struct anx6345 *anx6345 = i2c_get_clientdata(client);
+ if (anx6345->panel)
+ drm_panel_put(anx6345->panel);
+
drm_bridge_remove(&anx6345->bridge);
unregister_i2c_dummy_clients(anx6345);
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 6b98ad19508df..04b53ae698e5b 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -513,8 +513,10 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
if (ret)
return ERR_PTR(ret);
- if (panel)
+ if (panel) {
bridge = devm_drm_panel_bridge_add(dev, panel);
+ drm_panel_put(panel);
+ }
return bridge;
}
@@ -547,8 +549,10 @@ struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
if (ret)
return ERR_PTR(ret);
- if (panel)
+ if (panel) {
bridge = drmm_panel_bridge_add(drm, panel);
+ drm_panel_put(panel);
+ }
return bridge;
}
diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c
index b805403281504..14f5b1b452506 100644
--- a/drivers/gpu/drm/exynos/exynos_dp.c
+++ b/drivers/gpu/drm/exynos/exynos_dp.c
@@ -193,6 +193,16 @@ static int exynos_dp_probe(struct platform_device *pdev)
static void exynos_dp_remove(struct platform_device *pdev)
{
+ struct exynos_dp_device *dp = platform_get_drvdata(pdev);
+
+ /*
+ * Release the probe-time reference from of_drm_find_panel(). If bind
+ * ran, the panel_bridge holds a second reference that devm cleanup
+ * will release when the bridge is destroyed after remove() returns.
+ */
+ if (dp->plat_data.panel)
+ drm_panel_put(dp->plat_data.panel);
+
component_del(&pdev->dev, &exynos_dp_ops);
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
index 0dc36df6ada34..9d15a0035ea99 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
@@ -245,5 +245,8 @@ int exynos_dpi_remove(struct drm_encoder *encoder)
exynos_dpi_disable(&ctx->encoder);
+ if (ctx->panel)
+ drm_panel_put(ctx->panel);
+
return 0;
}
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
index 84eff7519e322..ec71fbbb0eb89 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
@@ -109,6 +109,13 @@ static int fsl_dcu_attach_panel(struct fsl_dcu_drm_device *fsl_dev,
return ret;
}
+static void fsl_dcu_panel_put_action(void *data)
+{
+ struct drm_panel *panel = data;
+
+ drm_panel_put(panel);
+}
+
int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev)
{
struct device_node *panel_node;
@@ -124,6 +131,12 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev)
if (IS_ERR(fsl_dev->connector.panel))
return PTR_ERR(fsl_dev->connector.panel);
+ ret = devm_add_action_or_reset(fsl_dev->dev,
+ fsl_dcu_panel_put_action,
+ fsl_dev->connector.panel);
+ if (ret)
+ return ret;
+
return fsl_dcu_attach_panel(fsl_dev, fsl_dev->connector.panel);
}
@@ -132,6 +145,11 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev)
return ret;
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);
}
diff --git a/drivers/gpu/drm/logicvc/logicvc_interface.c b/drivers/gpu/drm/logicvc/logicvc_interface.c
index 689049d395c0d..81f760dc07f8d 100644
--- a/drivers/gpu/drm/logicvc/logicvc_interface.c
+++ b/drivers/gpu/drm/logicvc/logicvc_interface.c
@@ -28,6 +28,11 @@
#define logicvc_interface_from_drm_connector(c) \
container_of(c, struct logicvc_interface, drm_connector)
+static void logicvc_panel_put_action(void *data)
+{
+ drm_panel_put(data);
+}
+
static void logicvc_encoder_enable(struct drm_encoder *drm_encoder)
{
struct logicvc_drm *logicvc = logicvc_drm(drm_encoder->dev);
@@ -160,6 +165,13 @@ int logicvc_interface_init(struct logicvc_drm *logicvc)
if (ret == -EPROBE_DEFER)
goto error_early;
+ if (interface->drm_panel) {
+ ret = devm_add_action_or_reset(dev, logicvc_panel_put_action,
+ interface->drm_panel);
+ if (ret)
+ goto error_early;
+ }
+
ret = drm_encoder_init(drm_dev, &interface->drm_encoder,
&logicvc_encoder_funcs, encoder_type, NULL);
if (ret) {
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 06072efd7fca3..4b2795a6caf8c 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -27,6 +27,7 @@
#include <drm/drm_bridge_connector.h>
#include <drm/bridge/analogix_dp.h>
#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
@@ -472,6 +473,16 @@ static int rockchip_dp_probe(struct platform_device *pdev)
static void rockchip_dp_remove(struct platform_device *pdev)
{
+ struct rockchip_dp_device *dp = platform_get_drvdata(pdev);
+
+ /*
+ * Release the probe-time reference from of_drm_find_panel(). If bind
+ * ran, the panel_bridge holds a second reference that devm cleanup
+ * will release when the bridge is destroyed after remove() returns.
+ */
+ if (dp->plat_data.panel)
+ drm_panel_put(dp->plat_data.panel);
+
component_del(&pdev->dev, &rockchip_dp_component_ops);
}
diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c
index 7484d3c3f4ed5..64a9da0362fb8 100644
--- a/drivers/gpu/drm/sti/sti_dvo.c
+++ b/drivers/gpu/drm/sti/sti_dvo.c
@@ -492,6 +492,9 @@ static void sti_dvo_unbind(struct device *dev,
{
struct sti_dvo *dvo = dev_get_drvdata(dev);
+ if (dvo->panel)
+ drm_panel_put(dvo->panel);
+
drm_bridge_remove(&dvo->bridge);
}
diff --git a/drivers/gpu/drm/stm/lvds.c b/drivers/gpu/drm/stm/lvds.c
index 50a878688e477..77735e26c56e3 100644
--- a/drivers/gpu/drm/stm/lvds.c
+++ b/drivers/gpu/drm/stm/lvds.c
@@ -1189,6 +1189,9 @@ static void lvds_remove(struct platform_device *pdev)
{
struct stm_lvds *lvds = platform_get_drvdata(pdev);
+ if (lvds->panel)
+ drm_panel_put(lvds->panel);
+
lvds_pixel_clk_unregister(lvds);
drm_bridge_remove(&lvds->lvds_bridge);
diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c
index 6716e895ae8a4..e1b342c922224 100644
--- a/drivers/gpu/drm/sun4i/sun4i_lvds.c
+++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
@@ -18,6 +18,11 @@
#include "sun4i_tcon.h"
#include "sun4i_lvds.h"
+static void sun4i_panel_put_action(void *data)
+{
+ drm_panel_put(data);
+}
+
struct sun4i_lvds {
struct drm_connector connector;
struct drm_encoder encoder;
@@ -113,6 +118,14 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
return 0;
}
+ if (lvds->panel) {
+ ret = devm_add_action_or_reset(tcon->dev,
+ sun4i_panel_put_action,
+ lvds->panel);
+ if (ret)
+ return ret;
+ }
+
drm_encoder_helper_add(&lvds->encoder,
&sun4i_lvds_enc_helper_funcs);
ret = drm_simple_encoder_init(drm, &lvds->encoder,
diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c
index dfb6acc42f02e..0066bec5a9e5a 100644
--- a/drivers/gpu/drm/sun4i/sun4i_rgb.c
+++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c
@@ -43,6 +43,11 @@ drm_encoder_to_sun4i_rgb(struct drm_encoder *encoder)
encoder);
}
+static void sun4i_panel_put_action(void *data)
+{
+ drm_panel_put(data);
+}
+
static int sun4i_rgb_get_modes(struct drm_connector *connector)
{
struct sun4i_rgb *rgb =
@@ -205,6 +210,14 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
return 0;
}
+ if (rgb->panel) {
+ ret = devm_add_action_or_reset(tcon->dev,
+ sun4i_panel_put_action,
+ rgb->panel);
+ if (ret)
+ return ret;
+ }
+
drm_encoder_helper_add(&rgb->encoder,
&sun4i_rgb_enc_helper_funcs);
ret = drm_simple_encoder_init(drm, &rgb->encoder,
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index c35b70d83e53b..1e8bc12fb6d04 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -985,6 +985,8 @@ static int sun6i_dsi_detach(struct mipi_dsi_host *host,
{
struct sun6i_dsi *dsi = host_to_sun6i_dsi(host);
+ if (dsi->panel)
+ drm_panel_put(dsi->panel);
dsi->panel = NULL;
dsi->device = NULL;
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 7f25c50621c94..57a016f47434d 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -1516,6 +1516,7 @@ static int tegra_dsi_host_detach(struct mipi_dsi_host *host,
struct tegra_output *output = &dsi->output;
if (output->panel && &device->dev == output->panel->dev) {
+ drm_panel_put(output->panel);
output->panel = NULL;
if (output->connector.dev)
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 49e4f63a5550d..90db39dbdd332 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -195,6 +195,9 @@ int tegra_output_probe(struct tegra_output *output)
void tegra_output_remove(struct tegra_output *output)
{
+ if (output->panel)
+ drm_panel_put(output->panel);
+
if (output->hpd_gpio)
free_irq(output->hpd_irq, output);
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 5/5] drm: release panel reference after panel bridge creation
2026-06-26 12:03 [PATCH 0/5] drm/panel: refcounting panel lookups and references Albert Esteve
` (3 preceding siblings ...)
2026-06-26 12:03 ` [PATCH 4/5] drm/bridge: release panel reference on all lookup exit paths Albert Esteve
@ 2026-06-26 12:03 ` Albert Esteve
2026-06-26 12:59 ` Maxime Ripard
4 siblings, 1 reply; 14+ messages in thread
From: Albert Esteve @ 2026-06-26 12:03 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Inki Dae, Jagan Teki, Marek Szyprowski,
Laurentiu Palcu, Lucas Stach, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Paul Cercueil,
Linus Walleij, Marek Vasut, Stefan Agner, Tomi Valkeinen,
Laurent Pinchart, Kieran Bingham, Geert Uytterhoeven, Magnus Damm,
Biju Das, Sandy Huang, Heiko Stübner, Andy Yan,
Yannick Fertre, Raphael Gallais-Pou, Philippe Cornu,
Maxime Coquelin, Alexandre Torgue, Chen-Yu Tsai, Samuel Holland,
Jyri Sarha, Jingoo Han, Seung-Woo Kim, Kyungmin Park,
Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Alison Wang,
Paul Kocialkowski, Alain Volmat, Raphael Gallais-Pou,
Thierry Reding, Mikko Perttunen, Jonathan Hunter
Cc: dri-devel, linux-kernel, imx, linux-arm-kernel, linux-mips,
linux-renesas-soc, linux-rockchip, linux-stm32, linux-sunxi,
linux-samsung-soc, linux-tegra, Albert Esteve
of_drm_find_panel() and drm_of_find_panel_or_bridge() now return a
counted reference. In drivers that immediately wrap the panel in a
bridge via devm_drm_panel_bridge_add() or equivalent, the bridge
acquires its own reference, so the caller's lookup reference must be
released right afterwards.
Also handle the cases where a panel is found but cannot be used,
dropping the reference immediately in those paths.
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Albert Esteve <aesteve@redhat.com>
---
drivers/gpu/drm/imx/dcss/dcss-kms.c | 3 +++
drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 4 +++-
drivers/gpu/drm/mcde/mcde_drv.c | 1 +
drivers/gpu/drm/mcde/mcde_dsi.c | 1 +
drivers/gpu/drm/mxsfb/mxsfb_drv.c | 1 +
drivers/gpu/drm/omapdrm/dss/output.c | 1 +
drivers/gpu/drm/pl111/pl111_drv.c | 1 +
drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c | 1 +
drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c | 1 +
drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c | 1 +
drivers/gpu/drm/rockchip/rockchip_lvds.c | 1 +
drivers/gpu/drm/rockchip/rockchip_rgb.c | 1 +
drivers/gpu/drm/stm/ltdc.c | 1 +
drivers/gpu/drm/sun4i/sun4i_tcon.c | 2 ++
drivers/gpu/drm/tidss/tidss_kms.c | 16 +++++++++++-----
drivers/gpu/drm/tve200/tve200_drv.c | 1 +
16 files changed, 31 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.c b/drivers/gpu/drm/imx/dcss/dcss-kms.c
index 50bd7f36d36dd..01e0c10b6ea1a 100644
--- a/drivers/gpu/drm/imx/dcss/dcss-kms.c
+++ b/drivers/gpu/drm/imx/dcss/dcss-kms.c
@@ -77,6 +77,9 @@ static int dcss_kms_bridge_connector_init(struct dcss_kms_dev *kms)
if (ret)
return ret;
+ if (panel)
+ drm_panel_put(panel);
+
if (!bridge) {
dev_err(ddev->dev, "No bridge found %d.\n", ret);
return -ENODEV;
diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index 42c86f195c66b..1887e01d29701 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -1297,9 +1297,11 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
goto err_drvdata;
}
- if (panel)
+ if (panel) {
bridge = devm_drm_panel_bridge_add_typed(dev, panel,
DRM_MODE_CONNECTOR_DPI);
+ drm_panel_put(panel);
+ }
ib = drmm_encoder_alloc(drm, struct ingenic_drm_bridge, encoder,
NULL, DRM_MODE_ENCODER_DPI, NULL);
diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c
index 5f2c462bad7e1..53275b575f0cb 100644
--- a/drivers/gpu/drm/mcde/mcde_drv.c
+++ b/drivers/gpu/drm/mcde/mcde_drv.c
@@ -153,6 +153,7 @@ static int mcde_modeset_init(struct drm_device *drm)
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_DPI);
+ drm_panel_put(panel);
if (IS_ERR(bridge)) {
dev_err(drm->dev,
"Could not connect panel bridge\n");
diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index 47d45897ed069..d9a454f226f79 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -1124,6 +1124,7 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
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);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
index 0b756da2fec22..bfcdc0c237ee1 100644
--- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c
+++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
@@ -128,6 +128,7 @@ static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb)
if (panel) {
bridge = devm_drm_panel_bridge_add_typed(drm->dev, panel,
DRM_MODE_CONNECTOR_DPI);
+ drm_panel_put(panel);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
}
diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c
index ca891aba38209..6e9bc605ee22f 100644
--- a/drivers/gpu/drm/omapdrm/dss/output.c
+++ b/drivers/gpu/drm/omapdrm/dss/output.c
@@ -43,6 +43,7 @@ int omapdss_device_init_output(struct omap_dss_device *out,
struct drm_bridge *bridge;
bridge = drm_panel_bridge_add(out->panel);
+ drm_panel_put(out->panel);
if (IS_ERR(bridge)) {
dev_err(out->dev,
"unable to create panel bridge (%ld)\n",
diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c
index ac7b1d12a0f59..8ec659b3c08eb 100644
--- a/drivers/gpu/drm/pl111/pl111_drv.c
+++ b/drivers/gpu/drm/pl111/pl111_drv.c
@@ -145,6 +145,7 @@ static int pl111_modeset_init(struct drm_device *dev)
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_Unknown);
+ drm_panel_put(panel);
if (IS_ERR(bridge)) {
ret = PTR_ERR(bridge);
goto finish;
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c
index db2088529b480..d8e7e9877ba86 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c
@@ -69,6 +69,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel,
DRM_MODE_CONNECTOR_DPI);
+ drm_panel_put(panel);
if (IS_ERR(bridge))
return PTR_ERR(no_free_ptr(bridge));
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c
index 154410745a74b..cc2996f044721 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c
@@ -791,6 +791,7 @@ static int rcar_lvds_parse_dt(struct rcar_lvds *lvds)
if (lvds->panel) {
lvds->next_bridge = devm_drm_panel_bridge_add(lvds->dev,
lvds->panel);
+ drm_panel_put(lvds->panel);
if (IS_ERR_OR_NULL(lvds->next_bridge)) {
ret = -EINVAL;
goto done;
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
index f50d166b764f5..3d0999e4fcfdf 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
@@ -90,6 +90,7 @@ int rzg2l_du_encoder_init(struct rzg2l_du_device *rcdu,
bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel,
DRM_MODE_CONNECTOR_DPI);
+ drm_panel_put(panel);
if (IS_ERR(bridge))
return PTR_ERR(no_free_ptr(bridge));
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index 7a0c4fa29f2f0..f754445d2631b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -605,6 +605,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
if (lvds->panel) {
lvds->bridge = drm_panel_bridge_add_typed(lvds->panel, DRM_MODE_CONNECTOR_LVDS);
+ drm_panel_put(lvds->panel);
if (IS_ERR(lvds->bridge)) {
ret = PTR_ERR(lvds->bridge);
goto err_free_encoder;
diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c
index add3123e5ce70..ea66c70013787 100644
--- a/drivers/gpu/drm/rockchip/rockchip_rgb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c
@@ -139,6 +139,7 @@ struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_LVDS);
+ drm_panel_put(panel);
if (IS_ERR(bridge))
return ERR_CAST(bridge);
}
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
index 95fcfa48d8be3..daf198edb42f5 100644
--- a/drivers/gpu/drm/stm/ltdc.c
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -1982,6 +1982,7 @@ int ltdc_load(struct drm_device *ddev)
if (panel) {
bridge = drmm_panel_bridge_add(ddev, panel);
+ drm_panel_put(panel);
if (IS_ERR(bridge)) {
drm_err(ddev, "panel-bridge endpoint %d\n", i);
ret = PTR_ERR(bridge);
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 960e83c8291da..d4c1723c5e3d8 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -1326,6 +1326,8 @@ static int sun4i_tcon_probe(struct platform_device *pdev)
ret = drm_of_find_panel_or_bridge(node, 1, 0, &panel, &bridge);
if (ret == -EPROBE_DEFER)
return ret;
+ if (panel)
+ drm_panel_put(panel);
}
return component_add(&pdev->dev, &sun4i_tcon_ops);
diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c
index 1512ee2574b66..70c14c3be10d5 100644
--- a/drivers/gpu/drm/tidss/tidss_kms.c
+++ b/drivers/gpu/drm/tidss/tidss_kms.c
@@ -162,6 +162,7 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss)
if (panel) {
u32 conn_type;
+ int ret;
dev_dbg(dev, "Setting up panel for port %d\n", i);
@@ -176,7 +177,8 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss)
break;
default:
WARN_ON(1);
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_panel;
}
if (panel->connector_type != conn_type) {
@@ -184,16 +186,20 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss)
"%s: Panel %s has incompatible connector type for vp%d (%d != %d)\n",
__func__, dev_name(panel->dev), i,
panel->connector_type, conn_type);
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_panel;
}
bridge = devm_drm_panel_bridge_add(dev, panel);
- if (IS_ERR(bridge)) {
+ ret = PTR_ERR_OR_ZERO(bridge);
+ if (ret)
dev_err(dev,
"failed to set up panel bridge for port %d\n",
i);
- return PTR_ERR(bridge);
- }
+put_panel:
+ drm_panel_put(panel);
+ if (ret)
+ return ret;
}
pipes[num_pipes].hw_videoport = i;
diff --git a/drivers/gpu/drm/tve200/tve200_drv.c b/drivers/gpu/drm/tve200/tve200_drv.c
index 562f3f11812a3..f858c58ccb994 100644
--- a/drivers/gpu/drm/tve200/tve200_drv.c
+++ b/drivers/gpu/drm/tve200/tve200_drv.c
@@ -84,6 +84,7 @@ static int tve200_modeset_init(struct drm_device *dev)
if (panel) {
bridge = drm_panel_bridge_add_typed(panel,
DRM_MODE_CONNECTOR_Unknown);
+ drm_panel_put(panel);
if (IS_ERR(bridge)) {
ret = PTR_ERR(bridge);
goto out_bridge;
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 1/5] drm/panel: have drm_panel_add/remove manage a list reference
2026-06-26 12:03 ` [PATCH 1/5] drm/panel: have drm_panel_add/remove manage a list reference Albert Esteve
@ 2026-06-26 12:47 ` Maxime Ripard
0 siblings, 0 replies; 14+ messages in thread
From: Maxime Ripard @ 2026-06-26 12:47 UTC (permalink / raw)
To: Albert Esteve
Cc: dri-devel, imx, linux-arm-kernel, linux-kernel, linux-mips,
linux-renesas-soc, linux-rockchip, linux-samsung-soc, linux-stm32,
linux-sunxi, linux-tegra, Alain Volmat, Alexandre Torgue,
Alim Akhtar, Alison Wang, Andrzej Hajda, Andy Yan, Biju Das,
Chen-Yu Tsai, David Airlie, Fabio Estevam, Frank Li,
Geert Uytterhoeven, Heiko Stübner, Inki Dae, Jagan Teki,
Jernej Skrabec, Jessica Zhang, Jingoo Han, Jonas Karlman,
Jonathan Hunter, Jyri Sarha, Kieran Bingham, Krzysztof Kozlowski,
Kyungmin Park, Laurent Pinchart, Laurent Pinchart,
Laurentiu Palcu, Linus Walleij, Luca Ceresoli, Lucas Stach,
Maarten Lankhorst, Magnus Damm, Marek Szyprowski, Marek Vasut,
Maxime Coquelin, Maxime Ripard, Mikko Perttunen, Neil Armstrong,
Paul Cercueil, Paul Kocialkowski, Pengutronix Kernel Team,
Peter Griffin, Philippe Cornu, Raphael Gallais-Pou,
Raphael Gallais-Pou, Robert Foss, Samuel Holland, Sandy Huang,
Sascha Hauer, Seung-Woo Kim, Simona Vetter, Stefan Agner,
Thierry Reding, Thomas Zimmermann, Tomi Valkeinen, Yannick Fertre
On Fri, 26 Jun 2026 14:03:23 +0200, Albert Esteve wrote:
> The global panel_list holds raw pointers to drm_panel objects.
> Nothing prevents a panel from being freed while it is still linked
> in the list: if a driver's probe calls drm_panel_add() and then
> fails at a later step, panel->list remains in panel_list. Any
> subsequent call to of_drm_find_panel() that iterates the list will
>
> [ ... ]
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Thanks!
Maxime
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/5] drm/bridge/panel: hold a reference to the wrapped panel
2026-06-26 12:03 ` [PATCH 2/5] drm/bridge/panel: hold a reference to the wrapped panel Albert Esteve
@ 2026-06-26 12:48 ` Maxime Ripard
0 siblings, 0 replies; 14+ messages in thread
From: Maxime Ripard @ 2026-06-26 12:48 UTC (permalink / raw)
To: Albert Esteve
Cc: dri-devel, imx, linux-arm-kernel, linux-kernel, linux-mips,
linux-renesas-soc, linux-rockchip, linux-samsung-soc, linux-stm32,
linux-sunxi, linux-tegra, Alain Volmat, Alexandre Torgue,
Alim Akhtar, Alison Wang, Andrzej Hajda, Andy Yan, Biju Das,
Chen-Yu Tsai, David Airlie, Fabio Estevam, Frank Li,
Geert Uytterhoeven, Heiko Stübner, Inki Dae, Jagan Teki,
Jernej Skrabec, Jessica Zhang, Jingoo Han, Jonas Karlman,
Jonathan Hunter, Jyri Sarha, Kieran Bingham, Krzysztof Kozlowski,
Kyungmin Park, Laurent Pinchart, Laurent Pinchart,
Laurentiu Palcu, Linus Walleij, Luca Ceresoli, Lucas Stach,
Maarten Lankhorst, Magnus Damm, Marek Szyprowski, Marek Vasut,
Maxime Coquelin, Maxime Ripard, Mikko Perttunen, Neil Armstrong,
Paul Cercueil, Paul Kocialkowski, Pengutronix Kernel Team,
Peter Griffin, Philippe Cornu, Raphael Gallais-Pou,
Raphael Gallais-Pou, Robert Foss, Samuel Holland, Sandy Huang,
Sascha Hauer, Seung-Woo Kim, Simona Vetter, Stefan Agner,
Thierry Reding, Thomas Zimmermann, Tomi Valkeinen, Yannick Fertre
On Fri, 26 Jun 2026 14:03:24 +0200, Albert Esteve wrote:
> drm_panel_bridge_add_typed() stores a pointer to the drm_panel it
> wraps, but never acquires a reference to it. If the panel device
> goes away while a panel_bridge still exists, the dangling pointer can
> be dereferenced through panel_bridge->panel.
>
>
> [ ... ]
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Thanks!
Maxime
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 3/5] drm/panel: make *find_panel*() return a counted reference
2026-06-26 12:03 ` [PATCH 3/5] drm/panel: make *find_panel*() return a counted reference Albert Esteve
@ 2026-06-26 12:50 ` Maxime Ripard
2026-06-26 15:11 ` Albert Esteve
0 siblings, 1 reply; 14+ messages in thread
From: Maxime Ripard @ 2026-06-26 12:50 UTC (permalink / raw)
To: Albert Esteve
Cc: Neil Armstrong, Jessica Zhang, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Inki Dae, Jagan Teki, Marek Szyprowski,
Laurentiu Palcu, Lucas Stach, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Paul Cercueil,
Linus Walleij, Marek Vasut, Stefan Agner, Tomi Valkeinen,
Laurent Pinchart, Kieran Bingham, Geert Uytterhoeven, Magnus Damm,
Biju Das, Sandy Huang, Heiko Stübner, Andy Yan,
Yannick Fertre, Raphael Gallais-Pou, Philippe Cornu,
Maxime Coquelin, Alexandre Torgue, Chen-Yu Tsai, Samuel Holland,
Jyri Sarha, Jingoo Han, Seung-Woo Kim, Kyungmin Park,
Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Alison Wang,
Paul Kocialkowski, Alain Volmat, Raphael Gallais-Pou,
Thierry Reding, Mikko Perttunen, Jonathan Hunter, dri-devel,
linux-kernel, imx, linux-arm-kernel, linux-mips,
linux-renesas-soc, linux-rockchip, linux-stm32, linux-sunxi,
linux-samsung-soc, linux-tegra
[-- Attachment #1: Type: text/plain, Size: 3911 bytes --]
On Fri, Jun 26, 2026 at 02:03:25PM +0200, Albert Esteve wrote:
> Callers of of_drm_find_panel() receive a pointer with no reference
> held, creating a window where the panel device can be unregistered
> and freed between the lookup and first use (e.g., drm_panel_prepare()).
>
> find_panel_by_fwnode() is the fwnode counterpart of of_drm_find_panel().
> drm_panel_add_follower() worked around the missing panel kref by calling
> get_device() on the panel's underlying struct device. However, get_device()
> only prevents the device kobject from being freed. It does not prevent the
> panel's kzalloc()'d container memory from being released when the kref
> reaches zero.
>
> Fix both lookup functions by acquiring a reference with drm_panel_get()
> before returning, under panel_lock. Callers are now responsible for calling
> drm_panel_put() when they no longer need the pointer.
>
> Signed-off-by: Albert Esteve <aesteve@redhat.com>
> ---
> drivers/gpu/drm/drm_panel.c | 22 +++++++++++++++++-----
> 1 file changed, 17 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
> index 545fe93dc28fe..a00ae98ed0956 100644
> --- a/drivers/gpu/drm/drm_panel.c
> +++ b/drivers/gpu/drm/drm_panel.c
> @@ -458,14 +458,17 @@ EXPORT_SYMBOL(__devm_drm_panel_alloc);
>
> #ifdef CONFIG_OF
> /**
> - * of_drm_find_panel - look up a panel using a device tree node
> + * of_drm_find_panel - look up and reference a panel by device tree node
> * @np: device tree node of the panel
> *
> * Searches the set of registered panels for one that matches the given device
> - * tree node. If a matching panel is found, return a pointer to it.
> + * tree node. If a matching panel is found, the panel's reference count is
> + * incremented before returning a pointer to it. The caller must call
> + * drm_panel_put() when it no longer needs the panel pointer.
> *
> - * Return: A pointer to the panel registered for the specified device tree
> - * node or an ERR_PTR() if no panel matching the device tree node can be found.
> + * Return: A reference-counted pointer to the panel registered for the specified
> + * device tree node or an ERR_PTR() if no panel matching the device tree node
> + * can be found.
> *
> * Possible error codes returned by this function:
> *
> @@ -484,6 +487,7 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np)
>
> list_for_each_entry(panel, &panel_list, list) {
> if (panel->dev->of_node == np) {
> + drm_panel_get(panel);
> mutex_unlock(&panel_lock);
> return panel;
> }
> @@ -538,7 +542,13 @@ int of_drm_get_panel_orientation(const struct device_node *np,
> EXPORT_SYMBOL(of_drm_get_panel_orientation);
> #endif
>
> -/* Find panel by fwnode. This should be identical to of_drm_find_panel(). */
> +/*
> + * Find panel by fwnode, returning a counted reference.
> + *
> + * Behaves identically to of_drm_find_panel(). On success the returned
> + * pointer has been passed through drm_panel_get(); the caller must call
> + * drm_panel_put() when done with it.
> + */
> static struct drm_panel *find_panel_by_fwnode(const struct fwnode_handle *fwnode)
> {
> struct drm_panel *panel;
> @@ -550,6 +560,7 @@ static struct drm_panel *find_panel_by_fwnode(const struct fwnode_handle *fwnode
>
> list_for_each_entry(panel, &panel_list, list) {
> if (dev_fwnode(panel->dev) == fwnode) {
> + drm_panel_get(panel);
> mutex_unlock(&panel_lock);
> return panel;
> }
This part should probably be in a separate patch
> @@ -686,6 +697,7 @@ void drm_panel_remove_follower(struct drm_panel_follower *follower)
> mutex_unlock(&panel->follower_lock);
>
> put_device(panel->dev);
> + drm_panel_put(panel);
> }
> EXPORT_SYMBOL(drm_panel_remove_follower);
together with this one?
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 4/5] drm/bridge: release panel reference on all lookup exit paths
2026-06-26 12:03 ` [PATCH 4/5] drm/bridge: release panel reference on all lookup exit paths Albert Esteve
@ 2026-06-26 12:53 ` Maxime Ripard
2026-06-26 13:11 ` Albert Esteve
0 siblings, 1 reply; 14+ messages in thread
From: Maxime Ripard @ 2026-06-26 12:53 UTC (permalink / raw)
To: Albert Esteve
Cc: Neil Armstrong, Jessica Zhang, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Inki Dae, Jagan Teki, Marek Szyprowski,
Laurentiu Palcu, Lucas Stach, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Paul Cercueil,
Linus Walleij, Marek Vasut, Stefan Agner, Tomi Valkeinen,
Laurent Pinchart, Kieran Bingham, Geert Uytterhoeven, Magnus Damm,
Biju Das, Sandy Huang, Heiko Stübner, Andy Yan,
Yannick Fertre, Raphael Gallais-Pou, Philippe Cornu,
Maxime Coquelin, Alexandre Torgue, Chen-Yu Tsai, Samuel Holland,
Jyri Sarha, Jingoo Han, Seung-Woo Kim, Kyungmin Park,
Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Alison Wang,
Paul Kocialkowski, Alain Volmat, Raphael Gallais-Pou,
Thierry Reding, Mikko Perttunen, Jonathan Hunter, dri-devel,
linux-kernel, imx, linux-arm-kernel, linux-mips,
linux-renesas-soc, linux-rockchip, linux-stm32, linux-sunxi,
linux-samsung-soc, linux-tegra
[-- Attachment #1: Type: text/plain, Size: 1466 bytes --]
On Fri, Jun 26, 2026 at 02:03:26PM +0200, Albert Esteve wrote:
> of_drm_find_panel() and drm_of_find_panel_or_bridge() now return a
> counted reference that the caller must release with drm_panel_put().
>
> For bridge drivers that immediately wrap the panel in a panel_bridge
> (which acquires its own reference), release the lookup reference right
> after the bridge creation call.
>
> For analogix-anx6345, which stores the panel for direct use, release
> the reference in the i2c remove path.
>
> For platform drivers using analogix_dp_core with a component lifecycle
> (exynos_dp, rockchip analogix_dp), release the lookup reference in the
> platform remove() function. The panel_bridge created during bind() holds
> a separate reference that devm cleanup releases after remove() returns.
>
> Also fix devm_drm_of_get_bridge() and drmm_of_get_bridge() in
> bridge/panel.c itself: both call drm_of_find_panel_or_bridge() and
> then pass the panel to devm/drmm_panel_bridge_add(), which acquires
> its own reference via drm_panel_bridge_add_typed(). The lookup
> reference was never released; add drm_panel_put() after each bridge
> creation call.
>
> Assisted-by: Claude:claude-opus-4-6
> Signed-off-by: Albert Esteve <aesteve@redhat.com>
I think this one should be either split into one patch per driver, or
merged with the of_drm_find_panel patch. I'm still not quite sure which
would be the best, maybe the latter?
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 5/5] drm: release panel reference after panel bridge creation
2026-06-26 12:03 ` [PATCH 5/5] drm: release panel reference after panel bridge creation Albert Esteve
@ 2026-06-26 12:59 ` Maxime Ripard
2026-06-26 15:05 ` Albert Esteve
0 siblings, 1 reply; 14+ messages in thread
From: Maxime Ripard @ 2026-06-26 12:59 UTC (permalink / raw)
To: Albert Esteve
Cc: Neil Armstrong, Jessica Zhang, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Inki Dae, Jagan Teki, Marek Szyprowski,
Laurentiu Palcu, Lucas Stach, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Paul Cercueil,
Linus Walleij, Marek Vasut, Stefan Agner, Tomi Valkeinen,
Laurent Pinchart, Kieran Bingham, Geert Uytterhoeven, Magnus Damm,
Biju Das, Sandy Huang, Heiko Stübner, Andy Yan,
Yannick Fertre, Raphael Gallais-Pou, Philippe Cornu,
Maxime Coquelin, Alexandre Torgue, Chen-Yu Tsai, Samuel Holland,
Jyri Sarha, Jingoo Han, Seung-Woo Kim, Kyungmin Park,
Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Alison Wang,
Paul Kocialkowski, Alain Volmat, Raphael Gallais-Pou,
Thierry Reding, Mikko Perttunen, Jonathan Hunter, dri-devel,
linux-kernel, imx, linux-arm-kernel, linux-mips,
linux-renesas-soc, linux-rockchip, linux-stm32, linux-sunxi,
linux-samsung-soc, linux-tegra
[-- Attachment #1: Type: text/plain, Size: 1268 bytes --]
On Fri, Jun 26, 2026 at 02:03:27PM +0200, Albert Esteve wrote:
> of_drm_find_panel() and drm_of_find_panel_or_bridge() now return a
> counted reference. In drivers that immediately wrap the panel in a
> bridge via devm_drm_panel_bridge_add() or equivalent, the bridge
> acquires its own reference, so the caller's lookup reference must be
> released right afterwards.
>
> Also handle the cases where a panel is found but cannot be used,
> dropping the reference immediately in those paths.
>
> Assisted-by: Claude:claude-opus-4-6
> Signed-off-by: Albert Esteve <aesteve@redhat.com>
drm_of_find_panel_or_bridge() does indeed return a refcounted pointer
now, but afaik the doc wasn't updated to reflect that.
More importantly, I feel like with both of_drm_find_panel and
drm_of_find_panel_or_bridge we update a path that is considered legacy
anyway now, and we should rather focus on providing a safe alternative.
But none of the functions you updated are unsafe, so it won't be more
unsafe, or provide any illusion of safety to the caller. Idk.
Either way, this should all be on its way out if Luca creates a bridge
for every panel, and we'll consolidate on bridges only, so maybe it's
not such a big deal to merge this patch.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 4/5] drm/bridge: release panel reference on all lookup exit paths
2026-06-26 12:53 ` Maxime Ripard
@ 2026-06-26 13:11 ` Albert Esteve
0 siblings, 0 replies; 14+ messages in thread
From: Albert Esteve @ 2026-06-26 13:11 UTC (permalink / raw)
To: Maxime Ripard
Cc: Neil Armstrong, Jessica Zhang, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Inki Dae, Jagan Teki, Marek Szyprowski,
Laurentiu Palcu, Lucas Stach, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Paul Cercueil,
Linus Walleij, Marek Vasut, Stefan Agner, Tomi Valkeinen,
Laurent Pinchart, Kieran Bingham, Geert Uytterhoeven, Magnus Damm,
Biju Das, Sandy Huang, Heiko Stübner, Andy Yan,
Yannick Fertre, Raphael Gallais-Pou, Philippe Cornu,
Maxime Coquelin, Alexandre Torgue, Chen-Yu Tsai, Samuel Holland,
Jyri Sarha, Jingoo Han, Seung-Woo Kim, Kyungmin Park,
Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Alison Wang,
Paul Kocialkowski, Alain Volmat, Raphael Gallais-Pou,
Thierry Reding, Mikko Perttunen, Jonathan Hunter, dri-devel,
linux-kernel, imx, linux-arm-kernel, linux-mips,
linux-renesas-soc, linux-rockchip, linux-stm32, linux-sunxi,
linux-samsung-soc, linux-tegra
On Fri, Jun 26, 2026 at 2:53 PM Maxime Ripard <mripard@kernel.org> wrote:
>
> On Fri, Jun 26, 2026 at 02:03:26PM +0200, Albert Esteve wrote:
> > of_drm_find_panel() and drm_of_find_panel_or_bridge() now return a
> > counted reference that the caller must release with drm_panel_put().
> >
> > For bridge drivers that immediately wrap the panel in a panel_bridge
> > (which acquires its own reference), release the lookup reference right
> > after the bridge creation call.
> >
> > For analogix-anx6345, which stores the panel for direct use, release
> > the reference in the i2c remove path.
> >
> > For platform drivers using analogix_dp_core with a component lifecycle
> > (exynos_dp, rockchip analogix_dp), release the lookup reference in the
> > platform remove() function. The panel_bridge created during bind() holds
> > a separate reference that devm cleanup releases after remove() returns.
> >
> > Also fix devm_drm_of_get_bridge() and drmm_of_get_bridge() in
> > bridge/panel.c itself: both call drm_of_find_panel_or_bridge() and
> > then pass the panel to devm/drmm_panel_bridge_add(), which acquires
> > its own reference via drm_panel_bridge_add_typed(). The lookup
> > reference was never released; add drm_panel_put() after each bridge
> > creation call.
> >
> > Assisted-by: Claude:claude-opus-4-6
> > Signed-off-by: Albert Esteve <aesteve@redhat.com>
>
> I think this one should be either split into one patch per driver, or
> merged with the of_drm_find_panel patch. I'm still not quite sure which
> would be the best, maybe the latter?
I have spent some time myself thinking about how to approach this.
Initially I thought about doing one patch per driver as you suggested,
but since there are many similar fixes that are mostly one-liners, In
the end I decided that grouping them would make the review easier and
result in a less inflated series. Maybe merging with of_drm_find_panel
patch makes sense to avoid having one patch introducing a transient
reference leak (even if it is fixed right after and is bisectable).
But that will also create a giant patch (harder to review?). Another
option could be to merge patches 4 and 5, which basically update all
callers, and keep the API update separated. Either way, I am happy
with whatever you decide is better. I will take note of this for v2.
>
> Maxime
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 5/5] drm: release panel reference after panel bridge creation
2026-06-26 12:59 ` Maxime Ripard
@ 2026-06-26 15:05 ` Albert Esteve
0 siblings, 0 replies; 14+ messages in thread
From: Albert Esteve @ 2026-06-26 15:05 UTC (permalink / raw)
To: Maxime Ripard
Cc: Neil Armstrong, Jessica Zhang, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Inki Dae, Jagan Teki, Marek Szyprowski,
Laurentiu Palcu, Lucas Stach, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Paul Cercueil,
Linus Walleij, Marek Vasut, Stefan Agner, Tomi Valkeinen,
Laurent Pinchart, Kieran Bingham, Geert Uytterhoeven, Magnus Damm,
Biju Das, Sandy Huang, Heiko Stübner, Andy Yan,
Yannick Fertre, Raphael Gallais-Pou, Philippe Cornu,
Maxime Coquelin, Alexandre Torgue, Chen-Yu Tsai, Samuel Holland,
Jyri Sarha, Jingoo Han, Seung-Woo Kim, Kyungmin Park,
Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Alison Wang,
Paul Kocialkowski, Alain Volmat, Raphael Gallais-Pou,
Thierry Reding, Mikko Perttunen, Jonathan Hunter, dri-devel,
linux-kernel, imx, linux-arm-kernel, linux-mips,
linux-renesas-soc, linux-rockchip, linux-stm32, linux-sunxi,
linux-samsung-soc, linux-tegra
On Fri, Jun 26, 2026 at 3:00 PM Maxime Ripard <mripard@kernel.org> wrote:
>
> On Fri, Jun 26, 2026 at 02:03:27PM +0200, Albert Esteve wrote:
> > of_drm_find_panel() and drm_of_find_panel_or_bridge() now return a
> > counted reference. In drivers that immediately wrap the panel in a
> > bridge via devm_drm_panel_bridge_add() or equivalent, the bridge
> > acquires its own reference, so the caller's lookup reference must be
> > released right afterwards.
> >
> > Also handle the cases where a panel is found but cannot be used,
> > dropping the reference immediately in those paths.
> >
> > Assisted-by: Claude:claude-opus-4-6
> > Signed-off-by: Albert Esteve <aesteve@redhat.com>
>
> drm_of_find_panel_or_bridge() does indeed return a refcounted pointer
> now, but afaik the doc wasn't updated to reflect that.
True, I'll fix that in the next version.
>
> More importantly, I feel like with both of_drm_find_panel and
> drm_of_find_panel_or_bridge we update a path that is considered legacy
> anyway now, and we should rather focus on providing a safe alternative.
Oh, I missed that this code path is considered legacy.
>
> But none of the functions you updated are unsafe, so it won't be more
> unsafe, or provide any illusion of safety to the caller. Idk.
>
> Either way, this should all be on its way out if Luca creates a bridge
> for every panel, and we'll consolidate on bridges only, so maybe it's
> not such a big deal to merge this patch.
I see. Given what you wrote, I think it'd make sense to correct them
while this code isn't completely dead.
BR,
Albert.
>
> Maxime
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 3/5] drm/panel: make *find_panel*() return a counted reference
2026-06-26 12:50 ` Maxime Ripard
@ 2026-06-26 15:11 ` Albert Esteve
0 siblings, 0 replies; 14+ messages in thread
From: Albert Esteve @ 2026-06-26 15:11 UTC (permalink / raw)
To: Maxime Ripard
Cc: Neil Armstrong, Jessica Zhang, Maarten Lankhorst,
Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Inki Dae, Jagan Teki, Marek Szyprowski,
Laurentiu Palcu, Lucas Stach, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Paul Cercueil,
Linus Walleij, Marek Vasut, Stefan Agner, Tomi Valkeinen,
Laurent Pinchart, Kieran Bingham, Geert Uytterhoeven, Magnus Damm,
Biju Das, Sandy Huang, Heiko Stübner, Andy Yan,
Yannick Fertre, Raphael Gallais-Pou, Philippe Cornu,
Maxime Coquelin, Alexandre Torgue, Chen-Yu Tsai, Samuel Holland,
Jyri Sarha, Jingoo Han, Seung-Woo Kim, Kyungmin Park,
Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Alison Wang,
Paul Kocialkowski, Alain Volmat, Raphael Gallais-Pou,
Thierry Reding, Mikko Perttunen, Jonathan Hunter, dri-devel,
linux-kernel, imx, linux-arm-kernel, linux-mips,
linux-renesas-soc, linux-rockchip, linux-stm32, linux-sunxi,
linux-samsung-soc, linux-tegra
On Fri, Jun 26, 2026 at 2:50 PM Maxime Ripard <mripard@kernel.org> wrote:
>
> On Fri, Jun 26, 2026 at 02:03:25PM +0200, Albert Esteve wrote:
> > Callers of of_drm_find_panel() receive a pointer with no reference
> > held, creating a window where the panel device can be unregistered
> > and freed between the lookup and first use (e.g., drm_panel_prepare()).
> >
> > find_panel_by_fwnode() is the fwnode counterpart of of_drm_find_panel().
> > drm_panel_add_follower() worked around the missing panel kref by calling
> > get_device() on the panel's underlying struct device. However, get_device()
> > only prevents the device kobject from being freed. It does not prevent the
> > panel's kzalloc()'d container memory from being released when the kref
> > reaches zero.
> >
> > Fix both lookup functions by acquiring a reference with drm_panel_get()
> > before returning, under panel_lock. Callers are now responsible for calling
> > drm_panel_put() when they no longer need the pointer.
> >
> > Signed-off-by: Albert Esteve <aesteve@redhat.com>
> > ---
> > drivers/gpu/drm/drm_panel.c | 22 +++++++++++++++++-----
> > 1 file changed, 17 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
> > index 545fe93dc28fe..a00ae98ed0956 100644
> > --- a/drivers/gpu/drm/drm_panel.c
> > +++ b/drivers/gpu/drm/drm_panel.c
> > @@ -458,14 +458,17 @@ EXPORT_SYMBOL(__devm_drm_panel_alloc);
> >
> > #ifdef CONFIG_OF
> > /**
> > - * of_drm_find_panel - look up a panel using a device tree node
> > + * of_drm_find_panel - look up and reference a panel by device tree node
> > * @np: device tree node of the panel
> > *
> > * Searches the set of registered panels for one that matches the given device
> > - * tree node. If a matching panel is found, return a pointer to it.
> > + * tree node. If a matching panel is found, the panel's reference count is
> > + * incremented before returning a pointer to it. The caller must call
> > + * drm_panel_put() when it no longer needs the panel pointer.
> > *
> > - * Return: A pointer to the panel registered for the specified device tree
> > - * node or an ERR_PTR() if no panel matching the device tree node can be found.
> > + * Return: A reference-counted pointer to the panel registered for the specified
> > + * device tree node or an ERR_PTR() if no panel matching the device tree node
> > + * can be found.
> > *
> > * Possible error codes returned by this function:
> > *
> > @@ -484,6 +487,7 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np)
> >
> > list_for_each_entry(panel, &panel_list, list) {
> > if (panel->dev->of_node == np) {
> > + drm_panel_get(panel);
> > mutex_unlock(&panel_lock);
> > return panel;
> > }
> > @@ -538,7 +542,13 @@ int of_drm_get_panel_orientation(const struct device_node *np,
> > EXPORT_SYMBOL(of_drm_get_panel_orientation);
> > #endif
> >
> > -/* Find panel by fwnode. This should be identical to of_drm_find_panel(). */
> > +/*
> > + * Find panel by fwnode, returning a counted reference.
> > + *
> > + * Behaves identically to of_drm_find_panel(). On success the returned
> > + * pointer has been passed through drm_panel_get(); the caller must call
> > + * drm_panel_put() when done with it.
> > + */
> > static struct drm_panel *find_panel_by_fwnode(const struct fwnode_handle *fwnode)
> > {
> > struct drm_panel *panel;
> > @@ -550,6 +560,7 @@ static struct drm_panel *find_panel_by_fwnode(const struct fwnode_handle *fwnode
> >
> > list_for_each_entry(panel, &panel_list, list) {
> > if (dev_fwnode(panel->dev) == fwnode) {
> > + drm_panel_get(panel);
> > mutex_unlock(&panel_lock);
> > return panel;
> > }
>
> This part should probably be in a separate patch
Yes. This is another place where I hesitated on organization, as it is
very similar to of_drm_find_panel() fix. But find_panel_by_fwnode() is
much more self-contained (it is declared static to begin with). So it
makes sense to split them. I will do so in the next version.
>
> > @@ -686,6 +697,7 @@ void drm_panel_remove_follower(struct drm_panel_follower *follower)
> > mutex_unlock(&panel->follower_lock);
> >
> > put_device(panel->dev);
> > + drm_panel_put(panel);
> > }
> > EXPORT_SYMBOL(drm_panel_remove_follower);
>
> together with this one?
>
> Maxime
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-06-26 15:12 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-26 12:03 [PATCH 0/5] drm/panel: refcounting panel lookups and references Albert Esteve
2026-06-26 12:03 ` [PATCH 1/5] drm/panel: have drm_panel_add/remove manage a list reference Albert Esteve
2026-06-26 12:47 ` Maxime Ripard
2026-06-26 12:03 ` [PATCH 2/5] drm/bridge/panel: hold a reference to the wrapped panel Albert Esteve
2026-06-26 12:48 ` Maxime Ripard
2026-06-26 12:03 ` [PATCH 3/5] drm/panel: make *find_panel*() return a counted reference Albert Esteve
2026-06-26 12:50 ` Maxime Ripard
2026-06-26 15:11 ` Albert Esteve
2026-06-26 12:03 ` [PATCH 4/5] drm/bridge: release panel reference on all lookup exit paths Albert Esteve
2026-06-26 12:53 ` Maxime Ripard
2026-06-26 13:11 ` Albert Esteve
2026-06-26 12:03 ` [PATCH 5/5] drm: release panel reference after panel bridge creation Albert Esteve
2026-06-26 12:59 ` Maxime Ripard
2026-06-26 15:05 ` Albert Esteve
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox