* [PATCH v6 01/26] drm/debugfs: fix printk format for bridge index
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 2:06 ` Dmitry Baryshkov
2025-02-06 18:14 ` [PATCH v6 02/26] drm: of: drm_of_find_panel_or_bridge: move misplaced comment Luca Ceresoli
` (25 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
idx is an unsigned int, use %u for printk-style strings.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/drm_debugfs.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index 536409a35df406dae0dd7ade01b3f3d1e2c9e8f9..6b2178864c7ee12db9aa1f562e106b2f604439f8 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -748,7 +748,7 @@ static int bridges_show(struct seq_file *m, void *data)
unsigned int idx = 0;
drm_for_each_bridge_in_chain(encoder, bridge) {
- drm_printf(&p, "bridge[%d]: %ps\n", idx++, bridge->funcs);
+ drm_printf(&p, "bridge[%u]: %ps\n", idx++, bridge->funcs);
drm_printf(&p, "\ttype: [%d] %s\n",
bridge->type,
drm_get_connector_type_name(bridge->type));
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 01/26] drm/debugfs: fix printk format for bridge index
2025-02-06 18:14 ` [PATCH v6 01/26] drm/debugfs: fix printk format for bridge index Luca Ceresoli
@ 2025-02-07 2:06 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 2:06 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Thu, Feb 06, 2025 at 07:14:16PM +0100, Luca Ceresoli wrote:
> idx is an unsigned int, use %u for printk-style strings.
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> This patch was added in v6.
> ---
> drivers/gpu/drm/drm_debugfs.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 02/26] drm: of: drm_of_find_panel_or_bridge: move misplaced comment
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
2025-02-06 18:14 ` [PATCH v6 01/26] drm/debugfs: fix printk format for bridge index Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 2:20 ` Dmitry Baryshkov
2025-02-06 18:14 ` [PATCH v6 03/26] drm/bridge: panel: use drm_bridge_is_panel() instead of open code Luca Ceresoli
` (24 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
This comment is misleading as it refers to one of the inner if() branches
only, not the whole outer if(). Move it to the branch it refers to.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/drm_of.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
index 5530919e0ba05f7ce1806730b292319f36e905ed..d0183dea770308e77f05da364ffe087d53f3be36 100644
--- a/drivers/gpu/drm/drm_of.c
+++ b/drivers/gpu/drm/drm_of.c
@@ -268,9 +268,9 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
*panel = NULL;
}
- /* No panel found yet, check for a bridge next. */
if (bridge) {
if (ret) {
+ /* No panel found yet, check for a bridge next. */
*bridge = of_drm_find_bridge(remote);
if (*bridge)
ret = 0;
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 02/26] drm: of: drm_of_find_panel_or_bridge: move misplaced comment
2025-02-06 18:14 ` [PATCH v6 02/26] drm: of: drm_of_find_panel_or_bridge: move misplaced comment Luca Ceresoli
@ 2025-02-07 2:20 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 2:20 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Thu, Feb 06, 2025 at 07:14:17PM +0100, Luca Ceresoli wrote:
> This comment is misleading as it refers to one of the inner if() branches
> only, not the whole outer if(). Move it to the branch it refers to.
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 03/26] drm/bridge: panel: use drm_bridge_is_panel() instead of open code
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
2025-02-06 18:14 ` [PATCH v6 01/26] drm/debugfs: fix printk format for bridge index Luca Ceresoli
2025-02-06 18:14 ` [PATCH v6 02/26] drm: of: drm_of_find_panel_or_bridge: move misplaced comment Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 2:21 ` Dmitry Baryshkov
2025-02-06 18:14 ` [PATCH v6 04/26] drm/bridge: panel: drm_panel_bridge_remove: warn when called on non-panel bridge Luca Ceresoli
` (23 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
drm_panel_bridge_remove() reads bridge->funcs to find out whether this is a
panel bridge or another kind of bridge. drm_bridge_is_panel() is made
exactly for that, so use it.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/bridge/panel.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 6e88339dec0f5faee690b7c53e8dcd0f1ee2281c..0c5db13b11dcb90ee88b9932b91aa05fc48d59bd 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -322,7 +322,7 @@ void drm_panel_bridge_remove(struct drm_bridge *bridge)
if (!bridge)
return;
- if (bridge->funcs != &panel_bridge_bridge_funcs)
+ if (!drm_bridge_is_panel(bridge))
return;
panel_bridge = drm_bridge_to_panel_bridge(bridge);
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 03/26] drm/bridge: panel: use drm_bridge_is_panel() instead of open code
2025-02-06 18:14 ` [PATCH v6 03/26] drm/bridge: panel: use drm_bridge_is_panel() instead of open code Luca Ceresoli
@ 2025-02-07 2:21 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 2:21 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Thu, Feb 06, 2025 at 07:14:18PM +0100, Luca Ceresoli wrote:
> drm_panel_bridge_remove() reads bridge->funcs to find out whether this is a
> panel bridge or another kind of bridge. drm_bridge_is_panel() is made
> exactly for that, so use it.
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> This patch was added in v6.
> ---
> drivers/gpu/drm/bridge/panel.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 04/26] drm/bridge: panel: drm_panel_bridge_remove: warn when called on non-panel bridge
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (2 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 03/26] drm/bridge: panel: use drm_bridge_is_panel() instead of open code Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 2:22 ` Dmitry Baryshkov
2025-02-07 7:25 ` Maxime Ripard
2025-02-06 18:14 ` [PATCH v6 05/26] drm/debugfs: add top-level 'bridges' file showing all added bridges Luca Ceresoli
` (22 subsequent siblings)
26 siblings, 2 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
This function is for panel_bridge instances only. The silent return when
invoked on other bridges might hide actual errors, so avoid them to go
unnoticed.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/bridge/panel.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 0c5db13b11dcb90ee88b9932b91aa05fc48d59bd..c57036b06493a6922e2cae38bcd1733930ff0073 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -322,8 +322,10 @@ void drm_panel_bridge_remove(struct drm_bridge *bridge)
if (!bridge)
return;
- if (!drm_bridge_is_panel(bridge))
+ if (!drm_bridge_is_panel(bridge)) {
+ drm_warn(bridge->dev, "%s: called on non-panel bridge!\n", __func__);
return;
+ }
panel_bridge = drm_bridge_to_panel_bridge(bridge);
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 04/26] drm/bridge: panel: drm_panel_bridge_remove: warn when called on non-panel bridge
2025-02-06 18:14 ` [PATCH v6 04/26] drm/bridge: panel: drm_panel_bridge_remove: warn when called on non-panel bridge Luca Ceresoli
@ 2025-02-07 2:22 ` Dmitry Baryshkov
2025-02-07 8:59 ` Luca Ceresoli
2025-02-07 7:25 ` Maxime Ripard
1 sibling, 1 reply; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 2:22 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Thu, Feb 06, 2025 at 07:14:19PM +0100, Luca Ceresoli wrote:
> This function is for panel_bridge instances only. The silent return when
> invoked on other bridges might hide actual errors, so avoid them to go
> unnoticed.
Is there a real case of something using this function in a wrong way?
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> This patch was added in v6.
> ---
> drivers/gpu/drm/bridge/panel.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
> index 0c5db13b11dcb90ee88b9932b91aa05fc48d59bd..c57036b06493a6922e2cae38bcd1733930ff0073 100644
> --- a/drivers/gpu/drm/bridge/panel.c
> +++ b/drivers/gpu/drm/bridge/panel.c
> @@ -322,8 +322,10 @@ void drm_panel_bridge_remove(struct drm_bridge *bridge)
> if (!bridge)
> return;
>
> - if (!drm_bridge_is_panel(bridge))
> + if (!drm_bridge_is_panel(bridge)) {
> + drm_warn(bridge->dev, "%s: called on non-panel bridge!\n", __func__);
> return;
> + }
>
> panel_bridge = drm_bridge_to_panel_bridge(bridge);
>
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 04/26] drm/bridge: panel: drm_panel_bridge_remove: warn when called on non-panel bridge
2025-02-07 2:22 ` Dmitry Baryshkov
@ 2025-02-07 8:59 ` Luca Ceresoli
0 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-07 8:59 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Fri, 7 Feb 2025 04:22:25 +0200
Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> On Thu, Feb 06, 2025 at 07:14:19PM +0100, Luca Ceresoli wrote:
> > This function is for panel_bridge instances only. The silent return when
> > invoked on other bridges might hide actual errors, so avoid them to go
> > unnoticed.
>
> Is there a real case of something using this function in a wrong way?
I don't know, but there was one in my v5 code. Having this warning
would have saved me a lot of debugging, so it looked useful for future
developers.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 04/26] drm/bridge: panel: drm_panel_bridge_remove: warn when called on non-panel bridge
2025-02-06 18:14 ` [PATCH v6 04/26] drm/bridge: panel: drm_panel_bridge_remove: warn when called on non-panel bridge Luca Ceresoli
2025-02-07 2:22 ` Dmitry Baryshkov
@ 2025-02-07 7:25 ` Maxime Ripard
1 sibling, 0 replies; 81+ messages in thread
From: Maxime Ripard @ 2025-02-07 7:25 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni,
Sam Ravnborg, linux-doc, Catalin Marinas, Paul Kocialkowski,
dri-devel, Claudiu Beznea, Laurent Pinchart, Andrzej Hajda,
Fabio Estevam, Marek Szyprowski, Simona Vetter, Robert Foss,
Jonathan Corbet, David Airlie, Jernej Skrabec, Daniel Thompson,
Jagan Teki, Jessica Zhang, Thomas Zimmermann, Will Deacon,
Jonas Karlman, Sascha Hauer, Maarten Lankhorst, Maxime Ripard,
Inki Dae, linux-arm-kernel, Neil Armstrong, Boris Brezillon,
linux-kernel, Paul Kocialkowski, Pengutronix Kernel Team,
Dmitry Baryshkov, Shawn Guo
On Thu, 6 Feb 2025 19:14:19 +0100, Luca Ceresoli wrote:
> This function is for panel_bridge instances only. The silent return when
> invoked on other bridges might hide actual errors, so avoid them to go
> unnoticed.
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> [ ... ]
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Thanks!
Maxime
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 05/26] drm/debugfs: add top-level 'bridges' file showing all added bridges
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (3 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 04/26] drm/bridge: panel: drm_panel_bridge_remove: warn when called on non-panel bridge Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 2:41 ` Dmitry Baryshkov
2025-02-06 18:14 ` [PATCH v6 06/26] drm/panel: move all code into bridge/panel.c Luca Ceresoli
` (21 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
The global bridges_list holding all the bridges between drm_bridge_add()
and drm_bridge_remove() cannot be inspected via debugfs. Add a file showing
it.
To avoid code duplication, move the code printing a bridge info to a common
function.
Note: this change requires exporting bridge_list and the mutex protecting
it.
Also add a comment about bridge_lock to make checkpatch happy.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/drm_bridge.c | 5 +--
drivers/gpu/drm/drm_debugfs.c | 70 +++++++++++++++++++++++++++++-------------
drivers/gpu/drm/drm_drv.c | 1 +
drivers/gpu/drm/drm_internal.h | 9 ++++++
4 files changed, 61 insertions(+), 24 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 241a384ebce39b4a3db58c208af27960904fc662..87cebec2de806781cee22da54d666eee9bde3648 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -195,8 +195,9 @@
* driver.
*/
-static DEFINE_MUTEX(bridge_lock);
-static LIST_HEAD(bridge_list);
+/* Protect bridge_list */
+DEFINE_MUTEX(bridge_lock);
+LIST_HEAD(bridge_list);
/**
* drm_bridge_add - add the given bridge to the global bridge list
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index 6b2178864c7ee12db9aa1f562e106b2f604439f8..7424d5237e7615d63de6bba572ee6050da6709d0 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -740,6 +740,30 @@ void drm_debugfs_crtc_remove(struct drm_crtc *crtc)
crtc->debugfs_entry = NULL;
}
+static void bridge_print(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx)
+{
+ drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs);
+ drm_printf(p, "\ttype: [%d] %s\n",
+ bridge->type,
+ drm_get_connector_type_name(bridge->type));
+
+ if (bridge->of_node)
+ drm_printf(p, "\tOF: %pOFfc\n", bridge->of_node);
+
+ drm_printf(p, "\tops: [0x%x]", bridge->ops);
+ if (bridge->ops & DRM_BRIDGE_OP_DETECT)
+ drm_puts(p, " detect");
+ if (bridge->ops & DRM_BRIDGE_OP_EDID)
+ drm_puts(p, " edid");
+ if (bridge->ops & DRM_BRIDGE_OP_HPD)
+ drm_puts(p, " hpd");
+ if (bridge->ops & DRM_BRIDGE_OP_MODES)
+ drm_puts(p, " modes");
+ if (bridge->ops & DRM_BRIDGE_OP_HDMI)
+ drm_puts(p, " hdmi");
+ drm_puts(p, "\n");
+}
+
static int bridges_show(struct seq_file *m, void *data)
{
struct drm_encoder *encoder = m->private;
@@ -747,28 +771,8 @@ static int bridges_show(struct seq_file *m, void *data)
struct drm_bridge *bridge;
unsigned int idx = 0;
- drm_for_each_bridge_in_chain(encoder, bridge) {
- drm_printf(&p, "bridge[%u]: %ps\n", idx++, bridge->funcs);
- drm_printf(&p, "\ttype: [%d] %s\n",
- bridge->type,
- drm_get_connector_type_name(bridge->type));
-
- if (bridge->of_node)
- drm_printf(&p, "\tOF: %pOFfc\n", bridge->of_node);
-
- drm_printf(&p, "\tops: [0x%x]", bridge->ops);
- if (bridge->ops & DRM_BRIDGE_OP_DETECT)
- drm_puts(&p, " detect");
- if (bridge->ops & DRM_BRIDGE_OP_EDID)
- drm_puts(&p, " edid");
- if (bridge->ops & DRM_BRIDGE_OP_HPD)
- drm_puts(&p, " hpd");
- if (bridge->ops & DRM_BRIDGE_OP_MODES)
- drm_puts(&p, " modes");
- if (bridge->ops & DRM_BRIDGE_OP_HDMI)
- drm_puts(&p, " hdmi");
- drm_puts(&p, "\n");
- }
+ drm_for_each_bridge_in_chain(encoder, bridge)
+ bridge_print(&p, bridge, idx++);
return 0;
}
@@ -802,3 +806,25 @@ void drm_debugfs_encoder_remove(struct drm_encoder *encoder)
debugfs_remove_recursive(encoder->debugfs_entry);
encoder->debugfs_entry = NULL;
}
+
+static int allbridges_show(struct seq_file *m, void *data)
+{
+ struct drm_printer p = drm_seq_file_printer(m);
+ struct drm_bridge *bridge;
+ unsigned int idx = 0;
+
+ mutex_lock(&bridge_lock);
+
+ list_for_each_entry(bridge, &bridge_list, list)
+ bridge_print(&p, bridge, idx++);
+
+ mutex_unlock(&bridge_lock);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(allbridges);
+
+void drm_debugfs_global_add(struct dentry *root)
+{
+ debugfs_create_file("bridges", 0444, root, NULL, &allbridges_fops);
+}
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 3cf440eee8a2ab3de134d925db8f1d2ce68062b7..9b6d7bd16ba409b6a9155a9fecbec6bfdd5ea0c2 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -1120,6 +1120,7 @@ static int __init drm_core_init(void)
}
drm_debugfs_root = debugfs_create_dir("dri", NULL);
+ drm_debugfs_global_add(drm_debugfs_root);
ret = register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops);
if (ret < 0)
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index b2b6a8e49dda46f1cb3b048ef7b28356dd3aaa4e..b6e875d4b25faae6bb0bb952c3c12bd4819698ec 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -48,6 +48,10 @@ struct drm_prime_file_private;
struct drm_printer;
struct drm_vblank_crtc;
+// for drm_debugfs.c
+extern struct mutex bridge_lock;
+extern struct list_head bridge_list;
+
/* drm_client_event.c */
#if defined(CONFIG_DRM_CLIENT)
void drm_client_debugfs_init(struct drm_device *dev);
@@ -196,6 +200,7 @@ void drm_debugfs_crtc_remove(struct drm_crtc *crtc);
void drm_debugfs_crtc_crc_add(struct drm_crtc *crtc);
void drm_debugfs_encoder_add(struct drm_encoder *encoder);
void drm_debugfs_encoder_remove(struct drm_encoder *encoder);
+void drm_debugfs_global_add(struct dentry *drm_debugfs_root);
#else
static inline void drm_debugfs_dev_fini(struct drm_device *dev)
{
@@ -241,6 +246,10 @@ static inline void drm_debugfs_encoder_remove(struct drm_encoder *encoder)
{
}
+static inline void drm_debugfs_global_add(struct dentry *drm_debugfs_root)
+{
+}
+
#endif
drm_ioctl_t drm_version;
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 05/26] drm/debugfs: add top-level 'bridges' file showing all added bridges
2025-02-06 18:14 ` [PATCH v6 05/26] drm/debugfs: add top-level 'bridges' file showing all added bridges Luca Ceresoli
@ 2025-02-07 2:41 ` Dmitry Baryshkov
2025-02-07 8:54 ` Luca Ceresoli
0 siblings, 1 reply; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 2:41 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Thu, Feb 06, 2025 at 07:14:20PM +0100, Luca Ceresoli wrote:
> The global bridges_list holding all the bridges between drm_bridge_add()
> and drm_bridge_remove() cannot be inspected via debugfs. Add a file showing
> it.
>
> To avoid code duplication, move the code printing a bridge info to a common
> function.
>
> Note: this change requires exporting bridge_list and the mutex protecting
> it.
>
> Also add a comment about bridge_lock to make checkpatch happy.
I think, exporting mutex _and_ a list is a bad idea (especially since
they don't have a proper prefix). It might be better to make the
bridge_print() function a more public one (and name it
drm_bridge_print()) and move allbridges attribute definition to
drm_bridge.c
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> This patch was added in v6.
> ---
> drivers/gpu/drm/drm_bridge.c | 5 +--
> drivers/gpu/drm/drm_debugfs.c | 70 +++++++++++++++++++++++++++++-------------
> drivers/gpu/drm/drm_drv.c | 1 +
> drivers/gpu/drm/drm_internal.h | 9 ++++++
> 4 files changed, 61 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index 241a384ebce39b4a3db58c208af27960904fc662..87cebec2de806781cee22da54d666eee9bde3648 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -195,8 +195,9 @@
> * driver.
> */
>
> -static DEFINE_MUTEX(bridge_lock);
> -static LIST_HEAD(bridge_list);
> +/* Protect bridge_list */
> +DEFINE_MUTEX(bridge_lock);
> +LIST_HEAD(bridge_list);
>
> /**
> * drm_bridge_add - add the given bridge to the global bridge list
> diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
> index 6b2178864c7ee12db9aa1f562e106b2f604439f8..7424d5237e7615d63de6bba572ee6050da6709d0 100644
> --- a/drivers/gpu/drm/drm_debugfs.c
> +++ b/drivers/gpu/drm/drm_debugfs.c
> @@ -740,6 +740,30 @@ void drm_debugfs_crtc_remove(struct drm_crtc *crtc)
> crtc->debugfs_entry = NULL;
> }
>
> +static void bridge_print(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx)
> +{
> + drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs);
> + drm_printf(p, "\ttype: [%d] %s\n",
> + bridge->type,
> + drm_get_connector_type_name(bridge->type));
> +
> + if (bridge->of_node)
> + drm_printf(p, "\tOF: %pOFfc\n", bridge->of_node);
> +
> + drm_printf(p, "\tops: [0x%x]", bridge->ops);
> + if (bridge->ops & DRM_BRIDGE_OP_DETECT)
> + drm_puts(p, " detect");
> + if (bridge->ops & DRM_BRIDGE_OP_EDID)
> + drm_puts(p, " edid");
> + if (bridge->ops & DRM_BRIDGE_OP_HPD)
> + drm_puts(p, " hpd");
> + if (bridge->ops & DRM_BRIDGE_OP_MODES)
> + drm_puts(p, " modes");
> + if (bridge->ops & DRM_BRIDGE_OP_HDMI)
> + drm_puts(p, " hdmi");
> + drm_puts(p, "\n");
> +}
> +
> static int bridges_show(struct seq_file *m, void *data)
> {
> struct drm_encoder *encoder = m->private;
> @@ -747,28 +771,8 @@ static int bridges_show(struct seq_file *m, void *data)
> struct drm_bridge *bridge;
> unsigned int idx = 0;
>
> - drm_for_each_bridge_in_chain(encoder, bridge) {
> - drm_printf(&p, "bridge[%u]: %ps\n", idx++, bridge->funcs);
> - drm_printf(&p, "\ttype: [%d] %s\n",
> - bridge->type,
> - drm_get_connector_type_name(bridge->type));
> -
> - if (bridge->of_node)
> - drm_printf(&p, "\tOF: %pOFfc\n", bridge->of_node);
> -
> - drm_printf(&p, "\tops: [0x%x]", bridge->ops);
> - if (bridge->ops & DRM_BRIDGE_OP_DETECT)
> - drm_puts(&p, " detect");
> - if (bridge->ops & DRM_BRIDGE_OP_EDID)
> - drm_puts(&p, " edid");
> - if (bridge->ops & DRM_BRIDGE_OP_HPD)
> - drm_puts(&p, " hpd");
> - if (bridge->ops & DRM_BRIDGE_OP_MODES)
> - drm_puts(&p, " modes");
> - if (bridge->ops & DRM_BRIDGE_OP_HDMI)
> - drm_puts(&p, " hdmi");
> - drm_puts(&p, "\n");
> - }
> + drm_for_each_bridge_in_chain(encoder, bridge)
> + bridge_print(&p, bridge, idx++);
>
> return 0;
> }
> @@ -802,3 +806,25 @@ void drm_debugfs_encoder_remove(struct drm_encoder *encoder)
> debugfs_remove_recursive(encoder->debugfs_entry);
> encoder->debugfs_entry = NULL;
> }
> +
> +static int allbridges_show(struct seq_file *m, void *data)
> +{
> + struct drm_printer p = drm_seq_file_printer(m);
> + struct drm_bridge *bridge;
> + unsigned int idx = 0;
> +
> + mutex_lock(&bridge_lock);
> +
> + list_for_each_entry(bridge, &bridge_list, list)
> + bridge_print(&p, bridge, idx++);
> +
> + mutex_unlock(&bridge_lock);
> +
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(allbridges);
Should it be DEFINE_DEBUGFS_ATTRIBUTE instead?
> +
> +void drm_debugfs_global_add(struct dentry *root)
> +{
> + debugfs_create_file("bridges", 0444, root, NULL, &allbridges_fops);
> +}
> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> index 3cf440eee8a2ab3de134d925db8f1d2ce68062b7..9b6d7bd16ba409b6a9155a9fecbec6bfdd5ea0c2 100644
> --- a/drivers/gpu/drm/drm_drv.c
> +++ b/drivers/gpu/drm/drm_drv.c
> @@ -1120,6 +1120,7 @@ static int __init drm_core_init(void)
> }
>
> drm_debugfs_root = debugfs_create_dir("dri", NULL);
> + drm_debugfs_global_add(drm_debugfs_root);
>
> ret = register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops);
> if (ret < 0)
> diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
> index b2b6a8e49dda46f1cb3b048ef7b28356dd3aaa4e..b6e875d4b25faae6bb0bb952c3c12bd4819698ec 100644
> --- a/drivers/gpu/drm/drm_internal.h
> +++ b/drivers/gpu/drm/drm_internal.h
> @@ -48,6 +48,10 @@ struct drm_prime_file_private;
> struct drm_printer;
> struct drm_vblank_crtc;
>
> +// for drm_debugfs.c
> +extern struct mutex bridge_lock;
> +extern struct list_head bridge_list;
> +
> /* drm_client_event.c */
> #if defined(CONFIG_DRM_CLIENT)
> void drm_client_debugfs_init(struct drm_device *dev);
> @@ -196,6 +200,7 @@ void drm_debugfs_crtc_remove(struct drm_crtc *crtc);
> void drm_debugfs_crtc_crc_add(struct drm_crtc *crtc);
> void drm_debugfs_encoder_add(struct drm_encoder *encoder);
> void drm_debugfs_encoder_remove(struct drm_encoder *encoder);
> +void drm_debugfs_global_add(struct dentry *drm_debugfs_root);
> #else
> static inline void drm_debugfs_dev_fini(struct drm_device *dev)
> {
> @@ -241,6 +246,10 @@ static inline void drm_debugfs_encoder_remove(struct drm_encoder *encoder)
> {
> }
>
> +static inline void drm_debugfs_global_add(struct dentry *drm_debugfs_root)
> +{
> +}
> +
> #endif
>
> drm_ioctl_t drm_version;
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 05/26] drm/debugfs: add top-level 'bridges' file showing all added bridges
2025-02-07 2:41 ` Dmitry Baryshkov
@ 2025-02-07 8:54 ` Luca Ceresoli
0 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-07 8:54 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
Hi Dmitry,
On Fri, 7 Feb 2025 04:41:12 +0200
Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> On Thu, Feb 06, 2025 at 07:14:20PM +0100, Luca Ceresoli wrote:
> > The global bridges_list holding all the bridges between drm_bridge_add()
> > and drm_bridge_remove() cannot be inspected via debugfs. Add a file showing
> > it.
> >
> > To avoid code duplication, move the code printing a bridge info to a common
> > function.
> >
> > Note: this change requires exporting bridge_list and the mutex protecting
> > it.
> >
> > Also add a comment about bridge_lock to make checkpatch happy.
>
> I think, exporting mutex _and_ a list is a bad idea (especially since
> they don't have a proper prefix). It might be better to make the
> bridge_print() function a more public one (and name it
> drm_bridge_print()) and move allbridges attribute definition to
> drm_bridge.c
I was also not happy with exporting these two symbols, and agree
exporting a print function from drm_bridge.c is cleaner. I'll rearrange
this for v7.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 06/26] drm/panel: move all code into bridge/panel.c
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (4 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 05/26] drm/debugfs: add top-level 'bridges' file showing all added bridges Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-06 18:14 ` [PATCH v6 07/26] drm/bridge: panel: forbid initializing a panel with unknown connector type Luca Ceresoli
` (20 subsequent siblings)
26 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
In preparation to let panels always create a panel bridge, we need the
drm_panel.c code to call bridge/panel.c code. However this would create a
cyclic dependency between .c files and between modules (drm ->
drm_kms_helper -> drm), indeed now the two components would depend on each
other.
In the short term, resolve this by moving all code to a single file so both
will end up in the drm_kms_helper module. As a beneficial side effect, this
will slightly reduce code size for configurations without bridges and
panels.
This step also requires moving drm_of_find_panel_or_bridge() from drm_of.c
because it is referenced by devm_drm_of_get_bridge() and
drmm_of_get_bridge().
In the long term, a good plan is probably that the panel becomes just "a
bridge", embedding a struct drm_bridge instead of allocating it separately.
In addition to moving around code without changing it, other changes are:
* update Makefile
* update #includes as needed
* update bridge/Kconfig to select DRM_PANEL_BRIDGE instead of DRM_PANEL
* update MAINTAINERS
* fix a trivial checkpatch issue
Link: https://lore.kernel.org/all/emuj2innmp6zmzd7pyakqzjqpdzhly6qfhakya3ydwmd63pl26@5jwxaidpikjw/
Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
Documentation/gpu/drm-kms-helpers.rst | 5 +-
MAINTAINERS | 1 -
drivers/gpu/drm/Makefile | 1 -
drivers/gpu/drm/atmel-hlcdc/Kconfig | 2 +-
drivers/gpu/drm/bridge/Kconfig | 25 +-
drivers/gpu/drm/bridge/panel.c | 610 ++++++++++++++++++++++++++++++++
drivers/gpu/drm/drm_kms_helper_common.c | 1 +
drivers/gpu/drm/drm_of.c | 68 ----
drivers/gpu/drm/drm_panel.c | 575 ------------------------------
9 files changed, 626 insertions(+), 662 deletions(-)
diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst
index b4ee25af1702b0019e0de5f9ee66d2dbdac2c664..79c8d3e63f7e06136440ed38972697b5f057d5d1 100644
--- a/Documentation/gpu/drm-kms-helpers.rst
+++ b/Documentation/gpu/drm-kms-helpers.rst
@@ -209,15 +209,12 @@ Panel-Bridge Helper Reference
Panel Helper Reference
======================
-.. kernel-doc:: drivers/gpu/drm/drm_panel.c
+.. kernel-doc:: drivers/gpu/drm/bridge/panel.c
:doc: drm panel
.. kernel-doc:: include/drm/drm_panel.h
:internal:
-.. kernel-doc:: drivers/gpu/drm/drm_panel.c
- :export:
-
.. kernel-doc:: drivers/gpu/drm/drm_panel_orientation_quirks.c
:export:
diff --git a/MAINTAINERS b/MAINTAINERS
index f789e9e54110914dc266fb7d1937a92277c507bb..090153a4f40aa3ec4982c85c809a97dca0396ab8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7996,7 +7996,6 @@ L: dri-devel@lists.freedesktop.org
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/panel/
-F: drivers/gpu/drm/drm_panel.c
F: drivers/gpu/drm/panel/
F: include/drm/drm_panel.h
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 19fb370fbc56772077973c864df71e4b8e0bf99b..98a42805b529ccf307e3a78857be47b544a601d8 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -79,7 +79,6 @@ drm-$(CONFIG_DRM_CLIENT) += \
drm_client_modeset.o
drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
-drm-$(CONFIG_DRM_PANEL) += drm_panel.o
drm-$(CONFIG_OF) += drm_of.o
drm-$(CONFIG_PCI) += drm_pci.o
drm-$(CONFIG_DEBUG_FS) += \
diff --git a/drivers/gpu/drm/atmel-hlcdc/Kconfig b/drivers/gpu/drm/atmel-hlcdc/Kconfig
index f8b9c91907d8ec697d072269090da8deaa54a6d9..7360991307ceaf7a4a594b1586114b2b7d6c1253 100644
--- a/drivers/gpu/drm/atmel-hlcdc/Kconfig
+++ b/drivers/gpu/drm/atmel-hlcdc/Kconfig
@@ -5,7 +5,7 @@ config DRM_ATMEL_HLCDC
select DRM_CLIENT_SELECTION
select DRM_GEM_DMA_HELPER
select DRM_KMS_HELPER
- select DRM_PANEL
+ select DRM_PANEL_BRIDGE
help
Choose this option if you have an ATMEL SoC with an HLCDC display
controller (i.e. at91sam9n12, at91sam9x5 family or sama5d3 family).
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 6b4664d91faa80f096ac6a0548ed342e802ae68b..393214e6ed6656674d2ffbfc36d45541253bc31d 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -10,7 +10,7 @@ config DRM_PANEL_BRIDGE
depends on DRM_BRIDGE
select DRM_PANEL
help
- DRM bridge wrapper of DRM panels
+ DRM panels (including DRM bridge wrapper of such panels)
config DRM_AUX_BRIDGE
tristate
@@ -195,7 +195,7 @@ config DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW
tristate "MegaChips stdp4028-ge-b850v3-fw and stdp2690-ge-b850v3-fw"
depends on OF
select DRM_KMS_HELPER
- select DRM_PANEL
+ select DRM_PANEL_BRIDGE
help
This is a driver for the display bridges of
GE B850v3 that convert dual channel LVDS
@@ -230,14 +230,14 @@ config DRM_NXP_PTN3460
tristate "NXP PTN3460 DP/LVDS bridge"
depends on OF
select DRM_KMS_HELPER
- select DRM_PANEL
+ select DRM_PANEL_BRIDGE
help
NXP PTN3460 eDP-LVDS bridge chip driver.
config DRM_PARADE_PS8622
tristate "Parade eDP/LVDS bridge"
depends on OF
- select DRM_PANEL
+ select DRM_PANEL_BRIDGE
select DRM_KMS_HELPER
select BACKLIGHT_CLASS_DEVICE
help
@@ -251,7 +251,7 @@ config DRM_PARADE_PS8640
select DRM_DISPLAY_DP_AUX_BUS
select DRM_KMS_HELPER
select DRM_MIPI_DSI
- select DRM_PANEL
+ select DRM_PANEL_BRIDGE
help
Choose this option if you have PS8640 for display
The PS8640 is a high-performance and low-power
@@ -326,7 +326,7 @@ config DRM_TOSHIBA_TC358764
depends on OF
select DRM_MIPI_DSI
select DRM_KMS_HELPER
- select DRM_PANEL
+ select DRM_PANEL_BRIDGE
help
Toshiba TC358764 DSI/LVDS bridge driver.
@@ -338,7 +338,7 @@ config DRM_TOSHIBA_TC358767
select DRM_KMS_HELPER
select REGMAP_I2C
select DRM_MIPI_DSI
- select DRM_PANEL
+ select DRM_PANEL_BRIDGE
help
Toshiba TC358767 eDP bridge chip driver.
@@ -347,7 +347,7 @@ config DRM_TOSHIBA_TC358768
depends on OF
select DRM_KMS_HELPER
select REGMAP_I2C
- select DRM_PANEL
+ select DRM_PANEL_BRIDGE
select DRM_MIPI_DSI
select VIDEOMODE_HELPERS
help
@@ -360,15 +360,16 @@ config DRM_TOSHIBA_TC358775
select DRM_DISPLAY_HELPER
select DRM_KMS_HELPER
select REGMAP_I2C
- select DRM_PANEL
+ select DRM_PANEL_BRIDGE
select DRM_MIPI_DSI
help
Toshiba TC358775 DSI/LVDS bridge chip driver.
config DRM_TI_DLPC3433
tristate "TI DLPC3433 Display controller"
- depends on DRM && DRM_PANEL
+ depends on DRM
depends on OF
+ select DRM_PANEL_BRIDGE
select DRM_MIPI_DSI
help
TI DLPC3433 is a MIPI DSI based display controller bridge
@@ -400,7 +401,7 @@ config DRM_TI_SN65DSI83
depends on OF
select DRM_KMS_HELPER
select REGMAP_I2C
- select DRM_PANEL
+ select DRM_PANEL_BRIDGE
select DRM_MIPI_DSI
help
Texas Instruments SN65DSI83 and SN65DSI84 DSI to LVDS Bridge driver
@@ -413,7 +414,7 @@ config DRM_TI_SN65DSI86
select DRM_BRIDGE_CONNECTOR
select DRM_KMS_HELPER
select REGMAP_I2C
- select DRM_PANEL
+ select DRM_PANEL_BRIDGE
select DRM_MIPI_DSI
select AUXILIARY_BUS
select DRM_DISPLAY_DP_AUX_BUS
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index c57036b06493a6922e2cae38bcd1733930ff0073..bd61e57e1a2dd3d1eb034da4f9213a6bcb3e6dc5 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -2,9 +2,12 @@
/*
* Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
* Copyright (C) 2017 Broadcom
+ * Copyright (C) 2013 NVIDIA Corporation
*/
+#include <linux/backlight.h>
#include <linux/debugfs.h>
+#include <linux/of.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
@@ -17,6 +20,613 @@
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
+static DEFINE_MUTEX(panel_lock);
+static LIST_HEAD(panel_list);
+
+/**
+ * DOC: drm panel
+ *
+ * The DRM panel helpers allow drivers to register panel objects with a
+ * central registry and provide functions to retrieve those panels in display
+ * drivers.
+ *
+ * For easy integration into drivers using the &drm_bridge infrastructure please
+ * take look at drm_panel_bridge_add() and devm_drm_panel_bridge_add().
+ */
+
+/**
+ * drm_panel_init - initialize a panel
+ * @panel: DRM panel
+ * @dev: parent device of the panel
+ * @funcs: panel operations
+ * @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to
+ * the panel interface
+ *
+ * Initialize the panel structure for subsequent registration with
+ * drm_panel_add().
+ */
+void drm_panel_init(struct drm_panel *panel, struct device *dev,
+ const struct drm_panel_funcs *funcs, int connector_type)
+{
+ INIT_LIST_HEAD(&panel->list);
+ INIT_LIST_HEAD(&panel->followers);
+ mutex_init(&panel->follower_lock);
+ panel->dev = dev;
+ panel->funcs = funcs;
+ panel->connector_type = connector_type;
+}
+EXPORT_SYMBOL(drm_panel_init);
+
+/**
+ * drm_panel_add - add a panel to the global registry
+ * @panel: panel to add
+ *
+ * Add a panel to the global registry so that it can be looked up by display
+ * drivers.
+ */
+void drm_panel_add(struct drm_panel *panel)
+{
+ mutex_lock(&panel_lock);
+ list_add_tail(&panel->list, &panel_list);
+ mutex_unlock(&panel_lock);
+}
+EXPORT_SYMBOL(drm_panel_add);
+
+/**
+ * drm_panel_remove - remove a panel from the global registry
+ * @panel: DRM panel
+ *
+ * Removes a panel from the global registry.
+ */
+void drm_panel_remove(struct drm_panel *panel)
+{
+ mutex_lock(&panel_lock);
+ list_del_init(&panel->list);
+ mutex_unlock(&panel_lock);
+}
+EXPORT_SYMBOL(drm_panel_remove);
+
+/**
+ * drm_panel_prepare - power on a panel
+ * @panel: DRM panel
+ *
+ * Calling this function will enable power and deassert any reset signals to
+ * the panel. After this has completed it is possible to communicate with any
+ * integrated circuitry via a command bus.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_panel_prepare(struct drm_panel *panel)
+{
+ struct drm_panel_follower *follower;
+ int ret;
+
+ if (!panel)
+ return -EINVAL;
+
+ if (panel->prepared) {
+ dev_warn(panel->dev, "Skipping prepare of already prepared panel\n");
+ return 0;
+ }
+
+ mutex_lock(&panel->follower_lock);
+
+ if (panel->funcs && panel->funcs->prepare) {
+ ret = panel->funcs->prepare(panel);
+ if (ret < 0)
+ goto exit;
+ }
+ panel->prepared = true;
+
+ list_for_each_entry(follower, &panel->followers, list) {
+ ret = follower->funcs->panel_prepared(follower);
+ if (ret < 0)
+ dev_info(panel->dev, "%ps failed: %d\n",
+ follower->funcs->panel_prepared, ret);
+ }
+
+ ret = 0;
+exit:
+ mutex_unlock(&panel->follower_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(drm_panel_prepare);
+
+/**
+ * drm_panel_unprepare - power off a panel
+ * @panel: DRM panel
+ *
+ * Calling this function will completely power off a panel (assert the panel's
+ * reset, turn off power supplies, ...). After this function has completed, it
+ * is usually no longer possible to communicate with the panel until another
+ * call to drm_panel_prepare().
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_panel_unprepare(struct drm_panel *panel)
+{
+ struct drm_panel_follower *follower;
+ int ret;
+
+ if (!panel)
+ return -EINVAL;
+
+ /*
+ * If you are seeing the warning below it likely means one of two things:
+ * - Your panel driver incorrectly calls drm_panel_unprepare() in its
+ * shutdown routine. You should delete this.
+ * - You are using panel-edp or panel-simple and your DRM modeset
+ * driver's shutdown() callback happened after the panel's shutdown().
+ * In this case the warning is harmless though ideally you should
+ * figure out how to reverse the order of the shutdown() callbacks.
+ */
+ if (!panel->prepared) {
+ dev_warn(panel->dev, "Skipping unprepare of already unprepared panel\n");
+ return 0;
+ }
+
+ mutex_lock(&panel->follower_lock);
+
+ list_for_each_entry(follower, &panel->followers, list) {
+ ret = follower->funcs->panel_unpreparing(follower);
+ if (ret < 0)
+ dev_info(panel->dev, "%ps failed: %d\n",
+ follower->funcs->panel_unpreparing, ret);
+ }
+
+ if (panel->funcs && panel->funcs->unprepare) {
+ ret = panel->funcs->unprepare(panel);
+ if (ret < 0)
+ goto exit;
+ }
+ panel->prepared = false;
+
+ ret = 0;
+exit:
+ mutex_unlock(&panel->follower_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(drm_panel_unprepare);
+
+/**
+ * drm_panel_enable - enable a panel
+ * @panel: DRM panel
+ *
+ * Calling this function will cause the panel display drivers to be turned on
+ * and the backlight to be enabled. Content will be visible on screen after
+ * this call completes.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_panel_enable(struct drm_panel *panel)
+{
+ int ret;
+
+ if (!panel)
+ return -EINVAL;
+
+ if (panel->enabled) {
+ dev_warn(panel->dev, "Skipping enable of already enabled panel\n");
+ return 0;
+ }
+
+ if (panel->funcs && panel->funcs->enable) {
+ ret = panel->funcs->enable(panel);
+ if (ret < 0)
+ return ret;
+ }
+ panel->enabled = true;
+
+ ret = backlight_enable(panel->backlight);
+ if (ret < 0)
+ DRM_DEV_INFO(panel->dev, "failed to enable backlight: %d\n",
+ ret);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_panel_enable);
+
+/**
+ * drm_panel_disable - disable a panel
+ * @panel: DRM panel
+ *
+ * This will typically turn off the panel's backlight or disable the display
+ * drivers. For smart panels it should still be possible to communicate with
+ * the integrated circuitry via any command bus after this call.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_panel_disable(struct drm_panel *panel)
+{
+ int ret;
+
+ if (!panel)
+ return -EINVAL;
+
+ /*
+ * If you are seeing the warning below it likely means one of two things:
+ * - Your panel driver incorrectly calls drm_panel_disable() in its
+ * shutdown routine. You should delete this.
+ * - You are using panel-edp or panel-simple and your DRM modeset
+ * driver's shutdown() callback happened after the panel's shutdown().
+ * In this case the warning is harmless though ideally you should
+ * figure out how to reverse the order of the shutdown() callbacks.
+ */
+ if (!panel->enabled) {
+ dev_warn(panel->dev, "Skipping disable of already disabled panel\n");
+ return 0;
+ }
+
+ ret = backlight_disable(panel->backlight);
+ if (ret < 0)
+ DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n",
+ ret);
+
+ if (panel->funcs && panel->funcs->disable) {
+ ret = panel->funcs->disable(panel);
+ if (ret < 0)
+ return ret;
+ }
+ panel->enabled = false;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_panel_disable);
+
+/**
+ * drm_panel_get_modes - probe the available display modes of a panel
+ * @panel: DRM panel
+ * @connector: DRM connector
+ *
+ * The modes probed from the panel are automatically added to the connector
+ * that the panel is attached to.
+ *
+ * Return: The number of modes available from the panel on success, or 0 on
+ * failure (no modes).
+ */
+int drm_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ if (!panel)
+ return 0;
+
+ if (panel->funcs && panel->funcs->get_modes) {
+ int num;
+
+ num = panel->funcs->get_modes(panel, connector);
+ if (num > 0)
+ return num;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_panel_get_modes);
+
+#ifdef CONFIG_OF
+/**
+ * of_drm_find_panel - look up a panel using a 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.
+ *
+ * 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.
+ *
+ * Possible error codes returned by this function:
+ *
+ * - EPROBE_DEFER: the panel device has not been probed yet, and the caller
+ * should retry later
+ * - ENODEV: the device is not available (status != "okay" or "ok")
+ */
+struct drm_panel *of_drm_find_panel(const struct device_node *np)
+{
+ struct drm_panel *panel;
+
+ if (!of_device_is_available(np))
+ return ERR_PTR(-ENODEV);
+
+ mutex_lock(&panel_lock);
+
+ list_for_each_entry(panel, &panel_list, list) {
+ if (panel->dev->of_node == np) {
+ mutex_unlock(&panel_lock);
+ return panel;
+ }
+ }
+
+ mutex_unlock(&panel_lock);
+ return ERR_PTR(-EPROBE_DEFER);
+}
+EXPORT_SYMBOL(of_drm_find_panel);
+
+/**
+ * drm_of_find_panel_or_bridge - return connected panel or bridge device
+ * @np: device tree node containing encoder output ports
+ * @port: port in the device tree node
+ * @endpoint: endpoint in the device tree node
+ * @panel: pointer to hold returned drm_panel
+ * @bridge: pointer to hold returned drm_bridge
+ *
+ * Given a DT node's port and endpoint number, find the connected node and
+ * return either the associated struct drm_panel or drm_bridge device. Either
+ * @panel or @bridge must not be NULL.
+ *
+ * This function is deprecated and should not be used in new drivers. Use
+ * devm_drm_of_get_bridge() instead.
+ *
+ * Returns zero if successful, or one of the standard error codes if it fails.
+ */
+int drm_of_find_panel_or_bridge(const struct device_node *np,
+ int port, int endpoint,
+ struct drm_panel **panel,
+ struct drm_bridge **bridge)
+{
+ int ret = -EPROBE_DEFER;
+ struct device_node *remote;
+
+ if (!panel && !bridge)
+ return -EINVAL;
+ if (panel)
+ *panel = NULL;
+
+ /*
+ * of_graph_get_remote_node() produces a noisy error message if port
+ * node isn't found and the absence of the port is a legit case here,
+ * so at first we silently check whether graph presents in the
+ * device-tree node.
+ */
+ if (!of_graph_is_present(np))
+ return -ENODEV;
+
+ remote = of_graph_get_remote_node(np, port, endpoint);
+ if (!remote)
+ return -ENODEV;
+
+ if (panel) {
+ *panel = of_drm_find_panel(remote);
+ if (!IS_ERR(*panel))
+ ret = 0;
+ else
+ *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;
+ }
+ }
+
+ of_node_put(remote);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
+
+/**
+ * of_drm_get_panel_orientation - look up the orientation of the panel through
+ * the "rotation" binding from a device tree node
+ * @np: device tree node of the panel
+ * @orientation: orientation enum to be filled in
+ *
+ * Looks up the rotation of a panel in the device tree. The orientation of the
+ * panel is expressed as a property name "rotation" in the device tree. The
+ * rotation in the device tree is counter clockwise.
+ *
+ * Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the
+ * rotation property doesn't exist. Return a negative error code on failure.
+ */
+int of_drm_get_panel_orientation(const struct device_node *np,
+ enum drm_panel_orientation *orientation)
+{
+ int rotation, ret;
+
+ ret = of_property_read_u32(np, "rotation", &rotation);
+ if (ret == -EINVAL) {
+ /* Don't return an error if there's no rotation property. */
+ *orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
+ return 0;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ if (rotation == 0)
+ *orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
+ else if (rotation == 90)
+ *orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
+ else if (rotation == 180)
+ *orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
+ else if (rotation == 270)
+ *orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL(of_drm_get_panel_orientation);
+#endif
+
+/**
+ * drm_is_panel_follower() - Check if the device is a panel follower
+ * @dev: The 'struct device' to check
+ *
+ * This checks to see if a device needs to be power sequenced together with
+ * a panel using the panel follower API.
+ * At the moment panels can only be followed on device tree enabled systems.
+ * The "panel" property of the follower points to the panel to be followed.
+ *
+ * Return: true if we should be power sequenced with a panel; false otherwise.
+ */
+bool drm_is_panel_follower(struct device *dev)
+{
+ /*
+ * The "panel" property is actually a phandle, but for simplicity we
+ * don't bother trying to parse it here. We just need to know if the
+ * property is there.
+ */
+ return of_property_present(dev->of_node, "panel");
+}
+EXPORT_SYMBOL(drm_is_panel_follower);
+
+/**
+ * drm_panel_add_follower() - Register something to follow panel state.
+ * @follower_dev: The 'struct device' for the follower.
+ * @follower: The panel follower descriptor for the follower.
+ *
+ * A panel follower is called right after preparing the panel and right before
+ * unpreparing the panel. It's primary intention is to power on an associated
+ * touchscreen, though it could be used for any similar devices. Multiple
+ * devices are allowed the follow the same panel.
+ *
+ * If a follower is added to a panel that's already been turned on, the
+ * follower's prepare callback is called right away.
+ *
+ * At the moment panels can only be followed on device tree enabled systems.
+ * The "panel" property of the follower points to the panel to be followed.
+ *
+ * Return: 0 or an error code. Note that -ENODEV means that we detected that
+ * follower_dev is not actually following a panel. The caller may
+ * choose to ignore this return value if following a panel is optional.
+ */
+int drm_panel_add_follower(struct device *follower_dev,
+ struct drm_panel_follower *follower)
+{
+ struct device_node *panel_np;
+ struct drm_panel *panel;
+ int ret;
+
+ panel_np = of_parse_phandle(follower_dev->of_node, "panel", 0);
+ if (!panel_np)
+ return -ENODEV;
+
+ panel = of_drm_find_panel(panel_np);
+ of_node_put(panel_np);
+ if (IS_ERR(panel))
+ return PTR_ERR(panel);
+
+ get_device(panel->dev);
+ follower->panel = panel;
+
+ mutex_lock(&panel->follower_lock);
+
+ list_add_tail(&follower->list, &panel->followers);
+ if (panel->prepared) {
+ ret = follower->funcs->panel_prepared(follower);
+ if (ret < 0)
+ dev_info(panel->dev, "%ps failed: %d\n",
+ follower->funcs->panel_prepared, ret);
+ }
+
+ mutex_unlock(&panel->follower_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_panel_add_follower);
+
+/**
+ * drm_panel_remove_follower() - Reverse drm_panel_add_follower().
+ * @follower: The panel follower descriptor for the follower.
+ *
+ * Undo drm_panel_add_follower(). This includes calling the follower's
+ * unprepare function if we're removed from a panel that's currently prepared.
+ *
+ * Return: 0 or an error code.
+ */
+void drm_panel_remove_follower(struct drm_panel_follower *follower)
+{
+ struct drm_panel *panel = follower->panel;
+ int ret;
+
+ mutex_lock(&panel->follower_lock);
+
+ if (panel->prepared) {
+ ret = follower->funcs->panel_unpreparing(follower);
+ if (ret < 0)
+ dev_info(panel->dev, "%ps failed: %d\n",
+ follower->funcs->panel_unpreparing, ret);
+ }
+ list_del_init(&follower->list);
+
+ mutex_unlock(&panel->follower_lock);
+
+ put_device(panel->dev);
+}
+EXPORT_SYMBOL(drm_panel_remove_follower);
+
+static void drm_panel_remove_follower_void(void *follower)
+{
+ drm_panel_remove_follower(follower);
+}
+
+/**
+ * devm_drm_panel_add_follower() - devm version of drm_panel_add_follower()
+ * @follower_dev: The 'struct device' for the follower.
+ * @follower: The panel follower descriptor for the follower.
+ *
+ * Handles calling drm_panel_remove_follower() using devm on the follower_dev.
+ *
+ * Return: 0 or an error code.
+ */
+int devm_drm_panel_add_follower(struct device *follower_dev,
+ struct drm_panel_follower *follower)
+{
+ int ret;
+
+ ret = drm_panel_add_follower(follower_dev, follower);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(follower_dev,
+ drm_panel_remove_follower_void, follower);
+}
+EXPORT_SYMBOL(devm_drm_panel_add_follower);
+
+#if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE)
+/**
+ * drm_panel_of_backlight - use backlight device node for backlight
+ * @panel: DRM panel
+ *
+ * Use this function to enable backlight handling if your panel
+ * uses device tree and has a backlight phandle.
+ *
+ * When the panel is enabled backlight will be enabled after a
+ * successful call to &drm_panel_funcs.enable()
+ *
+ * When the panel is disabled backlight will be disabled before the
+ * call to &drm_panel_funcs.disable().
+ *
+ * A typical implementation for a panel driver supporting device tree
+ * will call this function at probe time. Backlight will then be handled
+ * transparently without requiring any intervention from the driver.
+ * drm_panel_of_backlight() must be called after the call to drm_panel_init().
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_panel_of_backlight(struct drm_panel *panel)
+{
+ struct backlight_device *backlight;
+
+ if (!panel || !panel->dev)
+ return -EINVAL;
+
+ backlight = devm_of_find_backlight(panel->dev);
+
+ if (IS_ERR(backlight))
+ return PTR_ERR(backlight);
+
+ panel->backlight = backlight;
+ return 0;
+}
+EXPORT_SYMBOL(drm_panel_of_backlight);
+#endif
+
struct panel_bridge {
struct drm_bridge bridge;
struct drm_connector connector;
diff --git a/drivers/gpu/drm/drm_kms_helper_common.c b/drivers/gpu/drm/drm_kms_helper_common.c
index 0c7550c0462b5f2ae7084ae70633f501e02dac78..47660db72958a2937ac3344180b59c52110bebe7 100644
--- a/drivers/gpu/drm/drm_kms_helper_common.c
+++ b/drivers/gpu/drm/drm_kms_helper_common.c
@@ -28,5 +28,6 @@
#include <linux/module.h>
MODULE_AUTHOR("David Airlie, Jesse Barnes");
+MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_DESCRIPTION("DRM KMS helper");
MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
index d0183dea770308e77f05da364ffe087d53f3be36..9ba36ad0bac0e74af139d703c305f5814eb64ca8 100644
--- a/drivers/gpu/drm/drm_of.c
+++ b/drivers/gpu/drm/drm_of.c
@@ -217,74 +217,6 @@ int drm_of_encoder_active_endpoint(struct device_node *node,
}
EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint);
-/**
- * drm_of_find_panel_or_bridge - return connected panel or bridge device
- * @np: device tree node containing encoder output ports
- * @port: port in the device tree node
- * @endpoint: endpoint in the device tree node
- * @panel: pointer to hold returned drm_panel
- * @bridge: pointer to hold returned drm_bridge
- *
- * Given a DT node's port and endpoint number, find the connected node and
- * return either the associated struct drm_panel or drm_bridge device. Either
- * @panel or @bridge must not be NULL.
- *
- * This function is deprecated and should not be used in new drivers. Use
- * devm_drm_of_get_bridge() instead.
- *
- * Returns zero if successful, or one of the standard error codes if it fails.
- */
-int drm_of_find_panel_or_bridge(const struct device_node *np,
- int port, int endpoint,
- struct drm_panel **panel,
- struct drm_bridge **bridge)
-{
- int ret = -EPROBE_DEFER;
- struct device_node *remote;
-
- if (!panel && !bridge)
- return -EINVAL;
- if (panel)
- *panel = NULL;
-
- /*
- * of_graph_get_remote_node() produces a noisy error message if port
- * node isn't found and the absence of the port is a legit case here,
- * so at first we silently check whether graph presents in the
- * device-tree node.
- */
- if (!of_graph_is_present(np))
- return -ENODEV;
-
- remote = of_graph_get_remote_node(np, port, endpoint);
- if (!remote)
- return -ENODEV;
-
- if (panel) {
- *panel = of_drm_find_panel(remote);
- if (!IS_ERR(*panel))
- ret = 0;
- else
- *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;
- }
-
- }
-
- of_node_put(remote);
- return ret;
-}
-EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
-
enum drm_of_lvds_pixels {
DRM_OF_LVDS_EVEN = BIT(0),
DRM_OF_LVDS_ODD = BIT(1),
diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
deleted file mode 100644
index 9940e96d35e302080c32b49154bbf19a51c0665e..0000000000000000000000000000000000000000
--- a/drivers/gpu/drm/drm_panel.c
+++ /dev/null
@@ -1,575 +0,0 @@
-/*
- * Copyright (C) 2013, NVIDIA Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sub license,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the
- * next paragraph) shall be included in all copies or substantial portions
- * of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-#include <linux/backlight.h>
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/of.h>
-
-#include <drm/drm_crtc.h>
-#include <drm/drm_panel.h>
-#include <drm/drm_print.h>
-
-static DEFINE_MUTEX(panel_lock);
-static LIST_HEAD(panel_list);
-
-/**
- * DOC: drm panel
- *
- * The DRM panel helpers allow drivers to register panel objects with a
- * central registry and provide functions to retrieve those panels in display
- * drivers.
- *
- * For easy integration into drivers using the &drm_bridge infrastructure please
- * take look at drm_panel_bridge_add() and devm_drm_panel_bridge_add().
- */
-
-/**
- * drm_panel_init - initialize a panel
- * @panel: DRM panel
- * @dev: parent device of the panel
- * @funcs: panel operations
- * @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to
- * the panel interface
- *
- * Initialize the panel structure for subsequent registration with
- * drm_panel_add().
- */
-void drm_panel_init(struct drm_panel *panel, struct device *dev,
- const struct drm_panel_funcs *funcs, int connector_type)
-{
- INIT_LIST_HEAD(&panel->list);
- INIT_LIST_HEAD(&panel->followers);
- mutex_init(&panel->follower_lock);
- panel->dev = dev;
- panel->funcs = funcs;
- panel->connector_type = connector_type;
-}
-EXPORT_SYMBOL(drm_panel_init);
-
-/**
- * drm_panel_add - add a panel to the global registry
- * @panel: panel to add
- *
- * Add a panel to the global registry so that it can be looked up by display
- * drivers.
- */
-void drm_panel_add(struct drm_panel *panel)
-{
- mutex_lock(&panel_lock);
- list_add_tail(&panel->list, &panel_list);
- mutex_unlock(&panel_lock);
-}
-EXPORT_SYMBOL(drm_panel_add);
-
-/**
- * drm_panel_remove - remove a panel from the global registry
- * @panel: DRM panel
- *
- * Removes a panel from the global registry.
- */
-void drm_panel_remove(struct drm_panel *panel)
-{
- mutex_lock(&panel_lock);
- list_del_init(&panel->list);
- mutex_unlock(&panel_lock);
-}
-EXPORT_SYMBOL(drm_panel_remove);
-
-/**
- * drm_panel_prepare - power on a panel
- * @panel: DRM panel
- *
- * Calling this function will enable power and deassert any reset signals to
- * the panel. After this has completed it is possible to communicate with any
- * integrated circuitry via a command bus.
- *
- * Return: 0 on success or a negative error code on failure.
- */
-int drm_panel_prepare(struct drm_panel *panel)
-{
- struct drm_panel_follower *follower;
- int ret;
-
- if (!panel)
- return -EINVAL;
-
- if (panel->prepared) {
- dev_warn(panel->dev, "Skipping prepare of already prepared panel\n");
- return 0;
- }
-
- mutex_lock(&panel->follower_lock);
-
- if (panel->funcs && panel->funcs->prepare) {
- ret = panel->funcs->prepare(panel);
- if (ret < 0)
- goto exit;
- }
- panel->prepared = true;
-
- list_for_each_entry(follower, &panel->followers, list) {
- ret = follower->funcs->panel_prepared(follower);
- if (ret < 0)
- dev_info(panel->dev, "%ps failed: %d\n",
- follower->funcs->panel_prepared, ret);
- }
-
- ret = 0;
-exit:
- mutex_unlock(&panel->follower_lock);
-
- return ret;
-}
-EXPORT_SYMBOL(drm_panel_prepare);
-
-/**
- * drm_panel_unprepare - power off a panel
- * @panel: DRM panel
- *
- * Calling this function will completely power off a panel (assert the panel's
- * reset, turn off power supplies, ...). After this function has completed, it
- * is usually no longer possible to communicate with the panel until another
- * call to drm_panel_prepare().
- *
- * Return: 0 on success or a negative error code on failure.
- */
-int drm_panel_unprepare(struct drm_panel *panel)
-{
- struct drm_panel_follower *follower;
- int ret;
-
- if (!panel)
- return -EINVAL;
-
- /*
- * If you are seeing the warning below it likely means one of two things:
- * - Your panel driver incorrectly calls drm_panel_unprepare() in its
- * shutdown routine. You should delete this.
- * - You are using panel-edp or panel-simple and your DRM modeset
- * driver's shutdown() callback happened after the panel's shutdown().
- * In this case the warning is harmless though ideally you should
- * figure out how to reverse the order of the shutdown() callbacks.
- */
- if (!panel->prepared) {
- dev_warn(panel->dev, "Skipping unprepare of already unprepared panel\n");
- return 0;
- }
-
- mutex_lock(&panel->follower_lock);
-
- list_for_each_entry(follower, &panel->followers, list) {
- ret = follower->funcs->panel_unpreparing(follower);
- if (ret < 0)
- dev_info(panel->dev, "%ps failed: %d\n",
- follower->funcs->panel_unpreparing, ret);
- }
-
- if (panel->funcs && panel->funcs->unprepare) {
- ret = panel->funcs->unprepare(panel);
- if (ret < 0)
- goto exit;
- }
- panel->prepared = false;
-
- ret = 0;
-exit:
- mutex_unlock(&panel->follower_lock);
-
- return ret;
-}
-EXPORT_SYMBOL(drm_panel_unprepare);
-
-/**
- * drm_panel_enable - enable a panel
- * @panel: DRM panel
- *
- * Calling this function will cause the panel display drivers to be turned on
- * and the backlight to be enabled. Content will be visible on screen after
- * this call completes.
- *
- * Return: 0 on success or a negative error code on failure.
- */
-int drm_panel_enable(struct drm_panel *panel)
-{
- int ret;
-
- if (!panel)
- return -EINVAL;
-
- if (panel->enabled) {
- dev_warn(panel->dev, "Skipping enable of already enabled panel\n");
- return 0;
- }
-
- if (panel->funcs && panel->funcs->enable) {
- ret = panel->funcs->enable(panel);
- if (ret < 0)
- return ret;
- }
- panel->enabled = true;
-
- ret = backlight_enable(panel->backlight);
- if (ret < 0)
- DRM_DEV_INFO(panel->dev, "failed to enable backlight: %d\n",
- ret);
-
- return 0;
-}
-EXPORT_SYMBOL(drm_panel_enable);
-
-/**
- * drm_panel_disable - disable a panel
- * @panel: DRM panel
- *
- * This will typically turn off the panel's backlight or disable the display
- * drivers. For smart panels it should still be possible to communicate with
- * the integrated circuitry via any command bus after this call.
- *
- * Return: 0 on success or a negative error code on failure.
- */
-int drm_panel_disable(struct drm_panel *panel)
-{
- int ret;
-
- if (!panel)
- return -EINVAL;
-
- /*
- * If you are seeing the warning below it likely means one of two things:
- * - Your panel driver incorrectly calls drm_panel_disable() in its
- * shutdown routine. You should delete this.
- * - You are using panel-edp or panel-simple and your DRM modeset
- * driver's shutdown() callback happened after the panel's shutdown().
- * In this case the warning is harmless though ideally you should
- * figure out how to reverse the order of the shutdown() callbacks.
- */
- if (!panel->enabled) {
- dev_warn(panel->dev, "Skipping disable of already disabled panel\n");
- return 0;
- }
-
- ret = backlight_disable(panel->backlight);
- if (ret < 0)
- DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n",
- ret);
-
- if (panel->funcs && panel->funcs->disable) {
- ret = panel->funcs->disable(panel);
- if (ret < 0)
- return ret;
- }
- panel->enabled = false;
-
- return 0;
-}
-EXPORT_SYMBOL(drm_panel_disable);
-
-/**
- * drm_panel_get_modes - probe the available display modes of a panel
- * @panel: DRM panel
- * @connector: DRM connector
- *
- * The modes probed from the panel are automatically added to the connector
- * that the panel is attached to.
- *
- * Return: The number of modes available from the panel on success, or 0 on
- * failure (no modes).
- */
-int drm_panel_get_modes(struct drm_panel *panel,
- struct drm_connector *connector)
-{
- if (!panel)
- return 0;
-
- if (panel->funcs && panel->funcs->get_modes) {
- int num;
-
- num = panel->funcs->get_modes(panel, connector);
- if (num > 0)
- return num;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(drm_panel_get_modes);
-
-#ifdef CONFIG_OF
-/**
- * of_drm_find_panel - look up a panel using a 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.
- *
- * 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.
- *
- * Possible error codes returned by this function:
- *
- * - EPROBE_DEFER: the panel device has not been probed yet, and the caller
- * should retry later
- * - ENODEV: the device is not available (status != "okay" or "ok")
- */
-struct drm_panel *of_drm_find_panel(const struct device_node *np)
-{
- struct drm_panel *panel;
-
- if (!of_device_is_available(np))
- return ERR_PTR(-ENODEV);
-
- mutex_lock(&panel_lock);
-
- list_for_each_entry(panel, &panel_list, list) {
- if (panel->dev->of_node == np) {
- mutex_unlock(&panel_lock);
- return panel;
- }
- }
-
- mutex_unlock(&panel_lock);
- return ERR_PTR(-EPROBE_DEFER);
-}
-EXPORT_SYMBOL(of_drm_find_panel);
-
-/**
- * of_drm_get_panel_orientation - look up the orientation of the panel through
- * the "rotation" binding from a device tree node
- * @np: device tree node of the panel
- * @orientation: orientation enum to be filled in
- *
- * Looks up the rotation of a panel in the device tree. The orientation of the
- * panel is expressed as a property name "rotation" in the device tree. The
- * rotation in the device tree is counter clockwise.
- *
- * Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the
- * rotation property doesn't exist. Return a negative error code on failure.
- */
-int of_drm_get_panel_orientation(const struct device_node *np,
- enum drm_panel_orientation *orientation)
-{
- int rotation, ret;
-
- ret = of_property_read_u32(np, "rotation", &rotation);
- if (ret == -EINVAL) {
- /* Don't return an error if there's no rotation property. */
- *orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
- return 0;
- }
-
- if (ret < 0)
- return ret;
-
- if (rotation == 0)
- *orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
- else if (rotation == 90)
- *orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
- else if (rotation == 180)
- *orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
- else if (rotation == 270)
- *orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
- else
- return -EINVAL;
-
- return 0;
-}
-EXPORT_SYMBOL(of_drm_get_panel_orientation);
-#endif
-
-/**
- * drm_is_panel_follower() - Check if the device is a panel follower
- * @dev: The 'struct device' to check
- *
- * This checks to see if a device needs to be power sequenced together with
- * a panel using the panel follower API.
- * At the moment panels can only be followed on device tree enabled systems.
- * The "panel" property of the follower points to the panel to be followed.
- *
- * Return: true if we should be power sequenced with a panel; false otherwise.
- */
-bool drm_is_panel_follower(struct device *dev)
-{
- /*
- * The "panel" property is actually a phandle, but for simplicity we
- * don't bother trying to parse it here. We just need to know if the
- * property is there.
- */
- return of_property_present(dev->of_node, "panel");
-}
-EXPORT_SYMBOL(drm_is_panel_follower);
-
-/**
- * drm_panel_add_follower() - Register something to follow panel state.
- * @follower_dev: The 'struct device' for the follower.
- * @follower: The panel follower descriptor for the follower.
- *
- * A panel follower is called right after preparing the panel and right before
- * unpreparing the panel. It's primary intention is to power on an associated
- * touchscreen, though it could be used for any similar devices. Multiple
- * devices are allowed the follow the same panel.
- *
- * If a follower is added to a panel that's already been turned on, the
- * follower's prepare callback is called right away.
- *
- * At the moment panels can only be followed on device tree enabled systems.
- * The "panel" property of the follower points to the panel to be followed.
- *
- * Return: 0 or an error code. Note that -ENODEV means that we detected that
- * follower_dev is not actually following a panel. The caller may
- * choose to ignore this return value if following a panel is optional.
- */
-int drm_panel_add_follower(struct device *follower_dev,
- struct drm_panel_follower *follower)
-{
- struct device_node *panel_np;
- struct drm_panel *panel;
- int ret;
-
- panel_np = of_parse_phandle(follower_dev->of_node, "panel", 0);
- if (!panel_np)
- return -ENODEV;
-
- panel = of_drm_find_panel(panel_np);
- of_node_put(panel_np);
- if (IS_ERR(panel))
- return PTR_ERR(panel);
-
- get_device(panel->dev);
- follower->panel = panel;
-
- mutex_lock(&panel->follower_lock);
-
- list_add_tail(&follower->list, &panel->followers);
- if (panel->prepared) {
- ret = follower->funcs->panel_prepared(follower);
- if (ret < 0)
- dev_info(panel->dev, "%ps failed: %d\n",
- follower->funcs->panel_prepared, ret);
- }
-
- mutex_unlock(&panel->follower_lock);
-
- return 0;
-}
-EXPORT_SYMBOL(drm_panel_add_follower);
-
-/**
- * drm_panel_remove_follower() - Reverse drm_panel_add_follower().
- * @follower: The panel follower descriptor for the follower.
- *
- * Undo drm_panel_add_follower(). This includes calling the follower's
- * unprepare function if we're removed from a panel that's currently prepared.
- *
- * Return: 0 or an error code.
- */
-void drm_panel_remove_follower(struct drm_panel_follower *follower)
-{
- struct drm_panel *panel = follower->panel;
- int ret;
-
- mutex_lock(&panel->follower_lock);
-
- if (panel->prepared) {
- ret = follower->funcs->panel_unpreparing(follower);
- if (ret < 0)
- dev_info(panel->dev, "%ps failed: %d\n",
- follower->funcs->panel_unpreparing, ret);
- }
- list_del_init(&follower->list);
-
- mutex_unlock(&panel->follower_lock);
-
- put_device(panel->dev);
-}
-EXPORT_SYMBOL(drm_panel_remove_follower);
-
-static void drm_panel_remove_follower_void(void *follower)
-{
- drm_panel_remove_follower(follower);
-}
-
-/**
- * devm_drm_panel_add_follower() - devm version of drm_panel_add_follower()
- * @follower_dev: The 'struct device' for the follower.
- * @follower: The panel follower descriptor for the follower.
- *
- * Handles calling drm_panel_remove_follower() using devm on the follower_dev.
- *
- * Return: 0 or an error code.
- */
-int devm_drm_panel_add_follower(struct device *follower_dev,
- struct drm_panel_follower *follower)
-{
- int ret;
-
- ret = drm_panel_add_follower(follower_dev, follower);
- if (ret)
- return ret;
-
- return devm_add_action_or_reset(follower_dev,
- drm_panel_remove_follower_void, follower);
-}
-EXPORT_SYMBOL(devm_drm_panel_add_follower);
-
-#if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE)
-/**
- * drm_panel_of_backlight - use backlight device node for backlight
- * @panel: DRM panel
- *
- * Use this function to enable backlight handling if your panel
- * uses device tree and has a backlight phandle.
- *
- * When the panel is enabled backlight will be enabled after a
- * successful call to &drm_panel_funcs.enable()
- *
- * When the panel is disabled backlight will be disabled before the
- * call to &drm_panel_funcs.disable().
- *
- * A typical implementation for a panel driver supporting device tree
- * will call this function at probe time. Backlight will then be handled
- * transparently without requiring any intervention from the driver.
- * drm_panel_of_backlight() must be called after the call to drm_panel_init().
- *
- * Return: 0 on success or a negative error code on failure.
- */
-int drm_panel_of_backlight(struct drm_panel *panel)
-{
- struct backlight_device *backlight;
-
- if (!panel || !panel->dev)
- return -EINVAL;
-
- backlight = devm_of_find_backlight(panel->dev);
-
- if (IS_ERR(backlight))
- return PTR_ERR(backlight);
-
- panel->backlight = backlight;
- return 0;
-}
-EXPORT_SYMBOL(drm_panel_of_backlight);
-#endif
-
-MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
-MODULE_DESCRIPTION("DRM panel infrastructure");
-MODULE_LICENSE("GPL and additional rights");
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v6 07/26] drm/bridge: panel: forbid initializing a panel with unknown connector type
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (5 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 06/26] drm/panel: move all code into bridge/panel.c Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 2:44 ` Dmitry Baryshkov
2025-02-06 18:14 ` [PATCH v6 08/26] drm/bridge: panel: add a panel_bridge to every panel Luca Ceresoli
` (19 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
Having an DRM_MODE_CONNECTOR_Unknown connector type is consuidered bad, and
drm_panel_bridge_add_typed() and derivatives are deprecated for this.
drm_panel_init() won't prevent initializing a panel with a
DRM_MODE_CONNECTOR_Unknown connector type. Luckily there are no in-tree
users doing it, so take this as an opportinuty to document a valid
connector type must be passed.
Returning an error if this rule is violated is not possible because
drm_panel_init() is a void function. Add at least a warning to make any
violations noticeable, especially to non-upstream drivers.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/bridge/panel.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index bd61e57e1a2dd3d1eb034da4f9213a6bcb3e6dc5..58570ff6952ca313b3def084262c9bb3772272ef 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -40,7 +40,7 @@ static LIST_HEAD(panel_list);
* @dev: parent device of the panel
* @funcs: panel operations
* @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to
- * the panel interface
+ * the panel interface (must NOT be DRM_MODE_CONNECTOR_Unknown)
*
* Initialize the panel structure for subsequent registration with
* drm_panel_add().
@@ -48,6 +48,9 @@ static LIST_HEAD(panel_list);
void drm_panel_init(struct drm_panel *panel, struct device *dev,
const struct drm_panel_funcs *funcs, int connector_type)
{
+ if (connector_type == DRM_MODE_CONNECTOR_Unknown)
+ DRM_WARN("%s: %s: a valid connector type is required!\n", __func__, dev_name(dev));
+
INIT_LIST_HEAD(&panel->list);
INIT_LIST_HEAD(&panel->followers);
mutex_init(&panel->follower_lock);
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 07/26] drm/bridge: panel: forbid initializing a panel with unknown connector type
2025-02-06 18:14 ` [PATCH v6 07/26] drm/bridge: panel: forbid initializing a panel with unknown connector type Luca Ceresoli
@ 2025-02-07 2:44 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 2:44 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Thu, Feb 06, 2025 at 07:14:22PM +0100, Luca Ceresoli wrote:
> Having an DRM_MODE_CONNECTOR_Unknown connector type is consuidered bad, and
considered
> drm_panel_bridge_add_typed() and derivatives are deprecated for this.
>
> drm_panel_init() won't prevent initializing a panel with a
> DRM_MODE_CONNECTOR_Unknown connector type. Luckily there are no in-tree
> users doing it, so take this as an opportinuty to document a valid
> connector type must be passed.
>
> Returning an error if this rule is violated is not possible because
> drm_panel_init() is a void function. Add at least a warning to make any
> violations noticeable, especially to non-upstream drivers.
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> This patch was added in v6.
> ---
> drivers/gpu/drm/bridge/panel.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 08/26] drm/bridge: panel: add a panel_bridge to every panel
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (6 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 07/26] drm/bridge: panel: forbid initializing a panel with unknown connector type Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 2:49 ` Dmitry Baryshkov
2025-02-10 18:34 ` Maxime Ripard
2025-02-06 18:14 ` [PATCH v6 09/26] drm/bridge: move devm_drm_of_get_bridge and drmm_of_get_bridge to drm_bridge.c Luca Ceresoli
` (18 subsequent siblings)
26 siblings, 2 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
Adding a panel does currently not add a panel_bridge wrapping it. Usually
the panel_bridge creation happens 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:
* 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 this 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.
Correct removal of a panel_bridge might possibly be obtained by adding more
devm/drmm technology to have it freed correctly at all times. However this
would add more complexity and not help making lifetime more understandable.
Use a different approach instead: 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.
With the panel_bridge always created, the functions to get a bridge
[devm_drm_of_get_bridge() and drmm_of_get_bridge()] become simpler because
the bridge they are looking for exists already (if it can exist at all). In
turn, this is implemented based on a variant of
drm_of_find_panel_or_bridge() that only looks for panels:
of_drm_find_bridge_by_endpoint(). In the future
drm_of_find_panel_or_bridge() can be progressively removed because there
will never be a panel not exposing a bridge.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/bridge/panel.c | 74 +++++++++++++++++++++++++++++++++---------
include/drm/drm_panel.h | 8 +++++
2 files changed, 66 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 58570ff6952ca313b3def084262c9bb3772272ef..6995de605e7317dd1eb153afd475746ced764712 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -69,6 +69,9 @@ EXPORT_SYMBOL(drm_panel_init);
*/
void drm_panel_add(struct drm_panel *panel)
{
+ panel->bridge = drm_panel_bridge_add(panel);
+ WARN_ON(!panel->bridge);
+
mutex_lock(&panel_lock);
list_add_tail(&panel->list, &panel_list);
mutex_unlock(&panel_lock);
@@ -86,6 +89,9 @@ void drm_panel_remove(struct drm_panel *panel)
mutex_lock(&panel_lock);
list_del_init(&panel->list);
mutex_unlock(&panel_lock);
+
+ drm_panel_bridge_remove(panel->bridge);
+ panel->bridge = NULL;
}
EXPORT_SYMBOL(drm_panel_remove);
@@ -412,6 +418,49 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
}
EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
+/**
+ * of_drm_find_bridge_by_endpoint - return drm_bridge connected to an endpoint
+ * @np: device tree node containing encoder output ports
+ * @port: port in the device tree node
+ * @endpoint: endpoint in the device tree node
+ * @bridge: pointer to hold returned drm_bridge (must not be NULL)
+ *
+ * Given a DT node's port and endpoint number, find the connected node and
+ * return the associated struct drm_bridge.
+ *
+ * Returns zero if successful, or one of the standard error codes if it fails.
+ */
+static int of_drm_find_bridge_by_endpoint(const struct device_node *np,
+ int port, int endpoint,
+ struct drm_bridge **bridge)
+{
+ int ret = -EPROBE_DEFER;
+ struct device_node *remote;
+
+ if (!bridge)
+ return -EINVAL;
+
+ /*
+ * of_graph_get_remote_node() produces a noisy error message if port
+ * node isn't found and the absence of the port is a legit case here,
+ * so at first we silently check whether graph presents in the
+ * device-tree node.
+ */
+ if (!of_graph_is_present(np))
+ return -ENODEV;
+
+ remote = of_graph_get_remote_node(np, port, endpoint);
+ if (!remote)
+ return -ENODEV;
+
+ *bridge = of_drm_find_bridge(remote);
+ if (*bridge)
+ ret = 0;
+
+ of_node_put(remote);
+ return ret;
+}
+
/**
* of_drm_get_panel_orientation - look up the orientation of the panel through
* the "rotation" binding from a device tree node
@@ -1018,6 +1067,11 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
{
struct drm_bridge **ptr, *bridge;
+ if (panel->bridge) {
+ DRM_DEBUG("panel %s: returning existing bridge=%p", dev_name(dev), panel->bridge);
+ return panel->bridge;
+ }
+
ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
@@ -1106,8 +1160,7 @@ EXPORT_SYMBOL(drm_panel_bridge_connector);
* @endpoint: endpoint in the device tree node
*
* 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.
@@ -1117,17 +1170,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);
+ ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge);
if (ret)
return ERR_PTR(ret);
- if (panel)
- bridge = devm_drm_panel_bridge_add(dev, panel);
-
return bridge;
}
EXPORT_SYMBOL(devm_drm_of_get_bridge);
@@ -1140,8 +1188,7 @@ EXPORT_SYMBOL(devm_drm_of_get_bridge);
* @endpoint: endpoint in the device tree node
*
* 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.
@@ -1151,17 +1198,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);
+ ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge);
if (ret)
return ERR_PTR(ret);
- if (panel)
- bridge = drmm_panel_bridge_add(drm, panel);
-
return bridge;
}
EXPORT_SYMBOL(drmm_of_get_bridge);
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
index 10015891b056f816c7a992a2052b36fd26943c5b..7ace6c1389c5353c08de5ac46127e46e1de69359 100644
--- a/include/drm/drm_panel.h
+++ b/include/drm/drm_panel.h
@@ -196,6 +196,14 @@ struct drm_panel {
*/
struct device *dev;
+ /**
+ * @bridge:
+ *
+ * Pointer to the panel bridge that allows accessing this panel as
+ * a DRM bridge.
+ */
+ struct drm_bridge *bridge;
+
/**
* @backlight:
*
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 08/26] drm/bridge: panel: add a panel_bridge to every panel
2025-02-06 18:14 ` [PATCH v6 08/26] drm/bridge: panel: add a panel_bridge to every panel Luca Ceresoli
@ 2025-02-07 2:49 ` Dmitry Baryshkov
2025-02-07 8:54 ` Luca Ceresoli
2025-02-10 18:34 ` Maxime Ripard
1 sibling, 1 reply; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 2:49 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Thu, Feb 06, 2025 at 07:14:23PM +0100, Luca Ceresoli wrote:
> Adding a panel does currently not add a panel_bridge wrapping it. Usually
> the panel_bridge creation happens 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:
>
> * 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 this 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.
>
> Correct removal of a panel_bridge might possibly be obtained by adding more
> devm/drmm technology to have it freed correctly at all times. However this
> would add more complexity and not help making lifetime more understandable.
>
> Use a different approach instead: 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.
>
> With the panel_bridge always created, the functions to get a bridge
> [devm_drm_of_get_bridge() and drmm_of_get_bridge()] become simpler because
> the bridge they are looking for exists already (if it can exist at all). In
> turn, this is implemented based on a variant of
> drm_of_find_panel_or_bridge() that only looks for panels:
> of_drm_find_bridge_by_endpoint(). In the future
> drm_of_find_panel_or_bridge() can be progressively removed because there
> will never be a panel not exposing a bridge.
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> This patch was added in v6.
> ---
> drivers/gpu/drm/bridge/panel.c | 74 +++++++++++++++++++++++++++++++++---------
> include/drm/drm_panel.h | 8 +++++
> 2 files changed, 66 insertions(+), 16 deletions(-)
>
LGTM, minor issue below.
> @@ -1018,6 +1067,11 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
> {
> struct drm_bridge **ptr, *bridge;
>
> + if (panel->bridge) {
> + DRM_DEBUG("panel %s: returning existing bridge=%p", dev_name(dev), panel->bridge);
> + return panel->bridge;
> + }
Shouldn't the rest of the function also be removed as you do in other
cases?
> +
> ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr),
> GFP_KERNEL);
> if (!ptr)
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 08/26] drm/bridge: panel: add a panel_bridge to every panel
2025-02-07 2:49 ` Dmitry Baryshkov
@ 2025-02-07 8:54 ` Luca Ceresoli
2025-02-07 19:43 ` Dmitry Baryshkov
0 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-07 8:54 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Fri, 7 Feb 2025 04:49:21 +0200
Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> On Thu, Feb 06, 2025 at 07:14:23PM +0100, Luca Ceresoli wrote:
> > Adding a panel does currently not add a panel_bridge wrapping it. Usually
> > the panel_bridge creation happens 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:
> >
> > * 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 this 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.
> >
> > Correct removal of a panel_bridge might possibly be obtained by adding more
> > devm/drmm technology to have it freed correctly at all times. However this
> > would add more complexity and not help making lifetime more understandable.
> >
> > Use a different approach instead: 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.
> >
> > With the panel_bridge always created, the functions to get a bridge
> > [devm_drm_of_get_bridge() and drmm_of_get_bridge()] become simpler because
> > the bridge they are looking for exists already (if it can exist at all). In
> > turn, this is implemented based on a variant of
> > drm_of_find_panel_or_bridge() that only looks for panels:
> > of_drm_find_bridge_by_endpoint(). In the future
> > drm_of_find_panel_or_bridge() can be progressively removed because there
> > will never be a panel not exposing a bridge.
> >
> > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> >
> > ---
> >
> > This patch was added in v6.
> > ---
> > drivers/gpu/drm/bridge/panel.c | 74 +++++++++++++++++++++++++++++++++---------
> > include/drm/drm_panel.h | 8 +++++
> > 2 files changed, 66 insertions(+), 16 deletions(-)
> >
>
> LGTM, minor issue below.
>
> > @@ -1018,6 +1067,11 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
> > {
> > struct drm_bridge **ptr, *bridge;
> >
> > + if (panel->bridge) {
> > + DRM_DEBUG("panel %s: returning existing bridge=%p", dev_name(dev), panel->bridge);
> > + return panel->bridge;
> > + }
>
> Shouldn't the rest of the function also be removed as you do in other
> cases?
Indeed it should.
And even more, I now realize drm_panel_bridge_add_typed() should also
become a simple 'return panel->bridge', like its devm and drmm
variants, and its code, implementing the actual creation of a panel
bridge, move to an internal function. Otherwise this patch is a bug:
existing drivers which do call drm_panel_bridge_add_typed() would end
up in having two panel_bridges for the same panel.
I must say the process of developing this patch together with the
hotplug work was "convoluted" to say the least, which probably explains
why this got unnoticed so far.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 08/26] drm/bridge: panel: add a panel_bridge to every panel
2025-02-07 8:54 ` Luca Ceresoli
@ 2025-02-07 19:43 ` Dmitry Baryshkov
2025-02-10 17:11 ` Luca Ceresoli
0 siblings, 1 reply; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 19:43 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Fri, Feb 07, 2025 at 09:54:28AM +0100, Luca Ceresoli wrote:
> On Fri, 7 Feb 2025 04:49:21 +0200
> Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
>
> > On Thu, Feb 06, 2025 at 07:14:23PM +0100, Luca Ceresoli wrote:
> > > Adding a panel does currently not add a panel_bridge wrapping it. Usually
> > > the panel_bridge creation happens 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:
> > >
> > > * 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 this 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.
> > >
> > > Correct removal of a panel_bridge might possibly be obtained by adding more
> > > devm/drmm technology to have it freed correctly at all times. However this
> > > would add more complexity and not help making lifetime more understandable.
> > >
> > > Use a different approach instead: 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.
> > >
> > > With the panel_bridge always created, the functions to get a bridge
> > > [devm_drm_of_get_bridge() and drmm_of_get_bridge()] become simpler because
> > > the bridge they are looking for exists already (if it can exist at all). In
> > > turn, this is implemented based on a variant of
> > > drm_of_find_panel_or_bridge() that only looks for panels:
> > > of_drm_find_bridge_by_endpoint(). In the future
> > > drm_of_find_panel_or_bridge() can be progressively removed because there
> > > will never be a panel not exposing a bridge.
> > >
> > > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> > >
> > > ---
> > >
> > > This patch was added in v6.
> > > ---
> > > drivers/gpu/drm/bridge/panel.c | 74 +++++++++++++++++++++++++++++++++---------
> > > include/drm/drm_panel.h | 8 +++++
> > > 2 files changed, 66 insertions(+), 16 deletions(-)
> > >
> >
> > LGTM, minor issue below.
> >
> > > @@ -1018,6 +1067,11 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
> > > {
> > > struct drm_bridge **ptr, *bridge;
> > >
> > > + if (panel->bridge) {
> > > + DRM_DEBUG("panel %s: returning existing bridge=%p", dev_name(dev), panel->bridge);
> > > + return panel->bridge;
> > > + }
> >
> > Shouldn't the rest of the function also be removed as you do in other
> > cases?
>
> Indeed it should.
>
> And even more, I now realize drm_panel_bridge_add_typed() should also
> become a simple 'return panel->bridge', like its devm and drmm
> variants, and its code, implementing the actual creation of a panel
> bridge, move to an internal function. Otherwise this patch is a bug:
> existing drivers which do call drm_panel_bridge_add_typed() would end
> up in having two panel_bridges for the same panel.
>
> I must say the process of developing this patch together with the
> hotplug work was "convoluted" to say the least, which probably explains
> why this got unnoticed so far.
That's why I suggested to post this series separately - it saves you
from rebasing hotplug work on top.
>
> Luca
>
> --
> Luca Ceresoli, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 08/26] drm/bridge: panel: add a panel_bridge to every panel
2025-02-07 19:43 ` Dmitry Baryshkov
@ 2025-02-10 17:11 ` Luca Ceresoli
0 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-10 17:11 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
Hi Dmitry,
On Fri, 7 Feb 2025 21:43:26 +0200
Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> > > > @@ -1018,6 +1067,11 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
> > > > {
> > > > struct drm_bridge **ptr, *bridge;
> > > >
> > > > + if (panel->bridge) {
> > > > + DRM_DEBUG("panel %s: returning existing bridge=%p", dev_name(dev), panel->bridge);
> > > > + return panel->bridge;
> > > > + }
> > >
> > > Shouldn't the rest of the function also be removed as you do in other
> > > cases?
> >
> > Indeed it should.
> >
> > And even more, I now realize drm_panel_bridge_add_typed() should also
> > become a simple 'return panel->bridge', like its devm and drmm
> > variants, and its code, implementing the actual creation of a panel
> > bridge, move to an internal function. Otherwise this patch is a bug:
> > existing drivers which do call drm_panel_bridge_add_typed() would end
> > up in having two panel_bridges for the same panel.
> >
> > I must say the process of developing this patch together with the
> > hotplug work was "convoluted" to say the least, which probably explains
> > why this got unnoticed so far.
>
> That's why I suggested to post this series separately - it saves you
> from rebasing hotplug work on top.
Yes, that's sure, but not keeping my hotplug patches on top of the
panel_bridge ones makes it much harder for me to test on real hardware,
so each way has pros and cons.
However I might send only the panel_bridge patches at the next
iteration.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 08/26] drm/bridge: panel: add a panel_bridge to every panel
2025-02-06 18:14 ` [PATCH v6 08/26] drm/bridge: panel: add a panel_bridge to every panel Luca Ceresoli
2025-02-07 2:49 ` Dmitry Baryshkov
@ 2025-02-10 18:34 ` Maxime Ripard
2025-02-18 9:43 ` Chen-Yu Tsai
1 sibling, 1 reply; 81+ messages in thread
From: Maxime Ripard @ 2025-02-10 18:34 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Inki Dae, linux-arm-kernel,
Neil Armstrong, Boris Brezillon, linux-kernel, Paul Kocialkowski,
Pengutronix Kernel Team, Dmitry Baryshkov, Shawn Guo
[-- Attachment #1: Type: text/plain, Size: 3631 bytes --]
On Thu, Feb 06, 2025 at 07:14:23PM +0100, Luca Ceresoli wrote:
> Adding a panel does currently not add a panel_bridge wrapping it. Usually
> the panel_bridge creation happens 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:
>
> * 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 this 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.
>
> Correct removal of a panel_bridge might possibly be obtained by adding more
> devm/drmm technology to have it freed correctly at all times. However this
> would add more complexity and not help making lifetime more understandable.
>
> Use a different approach instead: 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.
>
> With the panel_bridge always created, the functions to get a bridge
> [devm_drm_of_get_bridge() and drmm_of_get_bridge()] become simpler because
> the bridge they are looking for exists already (if it can exist at all). In
> turn, this is implemented based on a variant of
> drm_of_find_panel_or_bridge() that only looks for panels:
> of_drm_find_bridge_by_endpoint(). In the future
> drm_of_find_panel_or_bridge() can be progressively removed because there
> will never be a panel not exposing a bridge.
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> This patch was added in v6.
> ---
> drivers/gpu/drm/bridge/panel.c | 74 +++++++++++++++++++++++++++++++++---------
> include/drm/drm_panel.h | 8 +++++
> 2 files changed, 66 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
> index 58570ff6952ca313b3def084262c9bb3772272ef..6995de605e7317dd1eb153afd475746ced764712 100644
> --- a/drivers/gpu/drm/bridge/panel.c
> +++ b/drivers/gpu/drm/bridge/panel.c
> @@ -69,6 +69,9 @@ EXPORT_SYMBOL(drm_panel_init);
> */
> void drm_panel_add(struct drm_panel *panel)
> {
> + panel->bridge = drm_panel_bridge_add(panel);
> + WARN_ON(!panel->bridge);
> +
> mutex_lock(&panel_lock);
> list_add_tail(&panel->list, &panel_list);
> mutex_unlock(&panel_lock);
> @@ -86,6 +89,9 @@ void drm_panel_remove(struct drm_panel *panel)
> mutex_lock(&panel_lock);
> list_del_init(&panel->list);
> mutex_unlock(&panel_lock);
> +
> + drm_panel_bridge_remove(panel->bridge);
> + panel->bridge = NULL;
> }
> EXPORT_SYMBOL(drm_panel_remove);
Given that drm_panel_add and drm_panel_remove are typically called at
probe/remove, it's pretty much equivalent to using devm. Both of these
solutions aren't safe, and the drm_panel lifetime is still broken.
I'd rather work on a solution that actually fixes those lifetime issues.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 08/26] drm/bridge: panel: add a panel_bridge to every panel
2025-02-10 18:34 ` Maxime Ripard
@ 2025-02-18 9:43 ` Chen-Yu Tsai
2025-02-18 10:17 ` Maxime Ripard
0 siblings, 1 reply; 81+ messages in thread
From: Chen-Yu Tsai @ 2025-02-18 9:43 UTC (permalink / raw)
To: Maxime Ripard
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni,
Sam Ravnborg, linux-doc, Catalin Marinas, Paul Kocialkowski,
dri-devel, Claudiu Beznea, Laurent Pinchart, Andrzej Hajda,
David Airlie, Fabio Estevam, Marek Szyprowski, Simona Vetter,
Robert Foss, Jonathan Corbet, Will Deacon, Jernej Skrabec,
Daniel Thompson, Jagan Teki, Jessica Zhang, Luca Ceresoli,
Thomas Zimmermann, Jonas Karlman, Sascha Hauer, Maarten Lankhorst,
Inki Dae, linux-arm-kernel, Neil Armstrong, Boris Brezillon,
linux-kernel, Paul Kocialkowski, Pengutronix Kernel Team,
Dmitry Baryshkov, Shawn Guo
On Tue, Feb 11, 2025 at 2:34 AM Maxime Ripard <mripard@kernel.org> wrote:
>
> On Thu, Feb 06, 2025 at 07:14:23PM +0100, Luca Ceresoli wrote:
> > Adding a panel does currently not add a panel_bridge wrapping it. Usually
> > the panel_bridge creation happens 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:
> >
> > * 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 this 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.
> >
> > Correct removal of a panel_bridge might possibly be obtained by adding more
> > devm/drmm technology to have it freed correctly at all times. However this
> > would add more complexity and not help making lifetime more understandable.
> >
> > Use a different approach instead: 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.
> >
> > With the panel_bridge always created, the functions to get a bridge
> > [devm_drm_of_get_bridge() and drmm_of_get_bridge()] become simpler because
> > the bridge they are looking for exists already (if it can exist at all). In
> > turn, this is implemented based on a variant of
> > drm_of_find_panel_or_bridge() that only looks for panels:
> > of_drm_find_bridge_by_endpoint(). In the future
> > drm_of_find_panel_or_bridge() can be progressively removed because there
> > will never be a panel not exposing a bridge.
> >
> > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> >
> > ---
> >
> > This patch was added in v6.
> > ---
> > drivers/gpu/drm/bridge/panel.c | 74 +++++++++++++++++++++++++++++++++---------
> > include/drm/drm_panel.h | 8 +++++
> > 2 files changed, 66 insertions(+), 16 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
> > index 58570ff6952ca313b3def084262c9bb3772272ef..6995de605e7317dd1eb153afd475746ced764712 100644
> > --- a/drivers/gpu/drm/bridge/panel.c
> > +++ b/drivers/gpu/drm/bridge/panel.c
> > @@ -69,6 +69,9 @@ EXPORT_SYMBOL(drm_panel_init);
> > */
> > void drm_panel_add(struct drm_panel *panel)
> > {
> > + panel->bridge = drm_panel_bridge_add(panel);
> > + WARN_ON(!panel->bridge);
> > +
> > mutex_lock(&panel_lock);
> > list_add_tail(&panel->list, &panel_list);
> > mutex_unlock(&panel_lock);
> > @@ -86,6 +89,9 @@ void drm_panel_remove(struct drm_panel *panel)
> > mutex_lock(&panel_lock);
> > list_del_init(&panel->list);
> > mutex_unlock(&panel_lock);
> > +
> > + drm_panel_bridge_remove(panel->bridge);
> > + panel->bridge = NULL;
> > }
> > EXPORT_SYMBOL(drm_panel_remove);
>
> Given that drm_panel_add and drm_panel_remove are typically called at
> probe/remove, it's pretty much equivalent to using devm. Both of these
> solutions aren't safe, and the drm_panel lifetime is still broken.
FWIW I believe this solves the panel vs panel_bridge lifetime
inconsistencies we previously reported [1]. Of course, as you rightly
point out, any pointers to the bridge become stale if the panel gets
removed.
> I'd rather work on a solution that actually fixes those lifetime issues.
I think that can happen once the bridges are ref-counted?
Instead of removing the bridge from the list, it can just clear the
panel pointer and have all the callbacks skip any operations involving
the panel.
The other option is to have the panel itself be ref-counted. I don't
think that's worth pursuing if the idea is to move everything over to
panel_bridge and things are somewhat ready.
ChenYu
[1] https://lore.kernel.org/dri-devel/20241009052402.411978-1-fshao@chromium.org/
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 08/26] drm/bridge: panel: add a panel_bridge to every panel
2025-02-18 9:43 ` Chen-Yu Tsai
@ 2025-02-18 10:17 ` Maxime Ripard
0 siblings, 0 replies; 81+ messages in thread
From: Maxime Ripard @ 2025-02-18 10:17 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni,
Sam Ravnborg, linux-doc, Catalin Marinas, Paul Kocialkowski,
dri-devel, Claudiu Beznea, Laurent Pinchart, Andrzej Hajda,
David Airlie, Fabio Estevam, Marek Szyprowski, Simona Vetter,
Robert Foss, Jonathan Corbet, Will Deacon, Jernej Skrabec,
Daniel Thompson, Jagan Teki, Jessica Zhang, Luca Ceresoli,
Thomas Zimmermann, Jonas Karlman, Sascha Hauer, Maarten Lankhorst,
Inki Dae, linux-arm-kernel, Neil Armstrong, Boris Brezillon,
linux-kernel, Paul Kocialkowski, Pengutronix Kernel Team,
Dmitry Baryshkov, Shawn Guo
[-- Attachment #1: Type: text/plain, Size: 5623 bytes --]
On Tue, Feb 18, 2025 at 05:43:43PM +0800, Chen-Yu Tsai wrote:
> On Tue, Feb 11, 2025 at 2:34 AM Maxime Ripard <mripard@kernel.org> wrote:
> >
> > On Thu, Feb 06, 2025 at 07:14:23PM +0100, Luca Ceresoli wrote:
> > > Adding a panel does currently not add a panel_bridge wrapping it. Usually
> > > the panel_bridge creation happens 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:
> > >
> > > * 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 this 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.
> > >
> > > Correct removal of a panel_bridge might possibly be obtained by adding more
> > > devm/drmm technology to have it freed correctly at all times. However this
> > > would add more complexity and not help making lifetime more understandable.
> > >
> > > Use a different approach instead: 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.
> > >
> > > With the panel_bridge always created, the functions to get a bridge
> > > [devm_drm_of_get_bridge() and drmm_of_get_bridge()] become simpler because
> > > the bridge they are looking for exists already (if it can exist at all). In
> > > turn, this is implemented based on a variant of
> > > drm_of_find_panel_or_bridge() that only looks for panels:
> > > of_drm_find_bridge_by_endpoint(). In the future
> > > drm_of_find_panel_or_bridge() can be progressively removed because there
> > > will never be a panel not exposing a bridge.
> > >
> > > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> > >
> > > ---
> > >
> > > This patch was added in v6.
> > > ---
> > > drivers/gpu/drm/bridge/panel.c | 74 +++++++++++++++++++++++++++++++++---------
> > > include/drm/drm_panel.h | 8 +++++
> > > 2 files changed, 66 insertions(+), 16 deletions(-)
> > >
> > > diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
> > > index 58570ff6952ca313b3def084262c9bb3772272ef..6995de605e7317dd1eb153afd475746ced764712 100644
> > > --- a/drivers/gpu/drm/bridge/panel.c
> > > +++ b/drivers/gpu/drm/bridge/panel.c
> > > @@ -69,6 +69,9 @@ EXPORT_SYMBOL(drm_panel_init);
> > > */
> > > void drm_panel_add(struct drm_panel *panel)
> > > {
> > > + panel->bridge = drm_panel_bridge_add(panel);
> > > + WARN_ON(!panel->bridge);
> > > +
> > > mutex_lock(&panel_lock);
> > > list_add_tail(&panel->list, &panel_list);
> > > mutex_unlock(&panel_lock);
> > > @@ -86,6 +89,9 @@ void drm_panel_remove(struct drm_panel *panel)
> > > mutex_lock(&panel_lock);
> > > list_del_init(&panel->list);
> > > mutex_unlock(&panel_lock);
> > > +
> > > + drm_panel_bridge_remove(panel->bridge);
> > > + panel->bridge = NULL;
> > > }
> > > EXPORT_SYMBOL(drm_panel_remove);
> >
> > Given that drm_panel_add and drm_panel_remove are typically called at
> > probe/remove, it's pretty much equivalent to using devm. Both of these
> > solutions aren't safe, and the drm_panel lifetime is still broken.
>
> FWIW I believe this solves the panel vs panel_bridge lifetime
> inconsistencies we previously reported [1]. Of course, as you rightly
> point out, any pointers to the bridge become stale if the panel gets
> removed.
>
> > I'd rather work on a solution that actually fixes those lifetime issues.
>
> I think that can happen once the bridges are ref-counted?
Not all panel users use a bridge, some are using the panel API directly.
> Instead of removing the bridge from the list, it can just clear the
> panel pointer and have all the callbacks skip any operations involving
> the panel.
>
> The other option is to have the panel itself be ref-counted. I don't
> think that's worth pursuing if the idea is to move everything over to
> panel_bridge and things are somewhat ready.
Yeah, maybe. I'm kind of skeptical it would be easier than just doing
the work we're doing here though. Most of the "easy" users have been
converted already, and only the problematic ones remain, which will be
difficult to move over. sun4i_rgb and sun6i_dsi, for example, won't be
trivial to switch to panel_bridge. And given that sun4i_rgb is mostly
obsolete, and sun6i_dsi unmaintainable, I don't think it's likely to
happen.
So I don't think it's fair to expect that work from such a series, or to
rely on it eventually being fixed.
I think a solution where we have a bridge in drm_panel is a better
solution (and easier to convert to), but that requires a new panel
allocation API too. We've started to work on it as well, so everything
might just click.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 09/26] drm/bridge: move devm_drm_of_get_bridge and drmm_of_get_bridge to drm_bridge.c
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (7 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 08/26] drm/bridge: panel: add a panel_bridge to every panel Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 2:52 ` Dmitry Baryshkov
2025-02-06 18:14 ` [PATCH v6 10/26] drm/bridge: add devm_drm_of_get_bridge_by_node() Luca Ceresoli
` (17 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
devm_drm_of_get_bridge() and drmm_of_get_bridge() do not have anything to
do with struct drm_panel anymore, they just manage bridges. So move them
from bridge/panel.c to drm_bridge.c.
Move also of_drm_find_bridge_by_endpoint() which is used only by
devm_drm_of_get_bridge() and drmm_of_get_bridge().
No code changes, only move functions to a different file within the same
module and add an #include as needed.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/bridge/panel.c | 102 -----------------------------------------
drivers/gpu/drm/drm_bridge.c | 100 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 100 insertions(+), 102 deletions(-)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 6995de605e7317dd1eb153afd475746ced764712..1230ae50b2020e7a9306cac83009dd600dd61d26 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -418,49 +418,6 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
}
EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
-/**
- * of_drm_find_bridge_by_endpoint - return drm_bridge connected to an endpoint
- * @np: device tree node containing encoder output ports
- * @port: port in the device tree node
- * @endpoint: endpoint in the device tree node
- * @bridge: pointer to hold returned drm_bridge (must not be NULL)
- *
- * Given a DT node's port and endpoint number, find the connected node and
- * return the associated struct drm_bridge.
- *
- * Returns zero if successful, or one of the standard error codes if it fails.
- */
-static int of_drm_find_bridge_by_endpoint(const struct device_node *np,
- int port, int endpoint,
- struct drm_bridge **bridge)
-{
- int ret = -EPROBE_DEFER;
- struct device_node *remote;
-
- if (!bridge)
- return -EINVAL;
-
- /*
- * of_graph_get_remote_node() produces a noisy error message if port
- * node isn't found and the absence of the port is a legit case here,
- * so at first we silently check whether graph presents in the
- * device-tree node.
- */
- if (!of_graph_is_present(np))
- return -ENODEV;
-
- remote = of_graph_get_remote_node(np, port, endpoint);
- if (!remote)
- return -ENODEV;
-
- *bridge = of_drm_find_bridge(remote);
- if (*bridge)
- ret = 0;
-
- of_node_put(remote);
- return ret;
-}
-
/**
* of_drm_get_panel_orientation - look up the orientation of the panel through
* the "rotation" binding from a device tree node
@@ -1150,62 +1107,3 @@ struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge)
return &panel_bridge->connector;
}
EXPORT_SYMBOL(drm_panel_bridge_connector);
-
-#ifdef CONFIG_OF
-/**
- * devm_drm_of_get_bridge - Return next bridge in the chain
- * @dev: device to tie the bridge lifetime to
- * @np: device tree node containing encoder output ports
- * @port: port in the device tree node
- * @endpoint: endpoint in the device tree node
- *
- * Given a DT node's port and endpoint number, finds the connected node
- * and returns the associated bridge if any.
- *
- * Returns a pointer to the bridge if successful, or an error pointer
- * otherwise.
- */
-struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
- struct device_node *np,
- u32 port, u32 endpoint)
-{
- struct drm_bridge *bridge;
- int ret;
-
- ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge);
- if (ret)
- return ERR_PTR(ret);
-
- return bridge;
-}
-EXPORT_SYMBOL(devm_drm_of_get_bridge);
-
-/**
- * drmm_of_get_bridge - Return next bridge in the chain
- * @drm: device to tie the bridge lifetime to
- * @np: device tree node containing encoder output ports
- * @port: port in the device tree node
- * @endpoint: endpoint in the device tree node
- *
- * Given a DT node's port and endpoint number, finds the connected node
- * and returns the associated bridge if any.
- *
- * Returns a drmm managed pointer to the bridge if successful, or an error
- * pointer otherwise.
- */
-struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
- struct device_node *np,
- u32 port, u32 endpoint)
-{
- struct drm_bridge *bridge;
- int ret;
-
- ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge);
- if (ret)
- return ERR_PTR(ret);
-
- return bridge;
-}
-EXPORT_SYMBOL(drmm_of_get_bridge);
-
-#endif
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 87cebec2de806781cee22da54d666eee9bde3648..2aa17fbe538b86066c4e68f0d0e8046e9ca9b965 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -25,6 +25,7 @@
#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/of.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_bridge.h>
@@ -1334,6 +1335,105 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np)
return NULL;
}
EXPORT_SYMBOL(of_drm_find_bridge);
+
+/**
+ * of_drm_find_bridge_by_endpoint - return drm_bridge connected to an endpoint
+ * @np: device tree node containing encoder output ports
+ * @port: port in the device tree node
+ * @endpoint: endpoint in the device tree node
+ * @bridge: pointer to hold returned drm_bridge (must not be NULL)
+ *
+ * Given a DT node's port and endpoint number, find the connected node and
+ * return the associated struct drm_bridge.
+ *
+ * Returns zero if successful, or one of the standard error codes if it fails.
+ */
+static int of_drm_find_bridge_by_endpoint(const struct device_node *np,
+ int port, int endpoint,
+ struct drm_bridge **bridge)
+{
+ int ret = -EPROBE_DEFER;
+ struct device_node *remote;
+
+ if (!bridge)
+ return -EINVAL;
+
+ /*
+ * of_graph_get_remote_node() produces a noisy error message if port
+ * node isn't found and the absence of the port is a legit case here,
+ * so at first we silently check whether graph presents in the
+ * device-tree node.
+ */
+ if (!of_graph_is_present(np))
+ return -ENODEV;
+
+ remote = of_graph_get_remote_node(np, port, endpoint);
+ if (!remote)
+ return -ENODEV;
+
+ *bridge = of_drm_find_bridge(remote);
+ if (*bridge)
+ ret = 0;
+
+ of_node_put(remote);
+ return ret;
+}
+
+/**
+ * devm_drm_of_get_bridge - Return next bridge in the chain
+ * @dev: device to tie the bridge lifetime to
+ * @np: device tree node containing encoder output ports
+ * @port: port in the device tree node
+ * @endpoint: endpoint in the device tree node
+ *
+ * Given a DT node's port and endpoint number, finds the connected node
+ * and returns the associated bridge if any.
+ *
+ * Returns a pointer to the bridge if successful, or an error pointer
+ * otherwise.
+ */
+struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
+ struct device_node *np,
+ u32 port, u32 endpoint)
+{
+ struct drm_bridge *bridge;
+ int ret;
+
+ ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return bridge;
+}
+EXPORT_SYMBOL(devm_drm_of_get_bridge);
+
+/**
+ * drmm_of_get_bridge - Return next bridge in the chain
+ * @drm: device to tie the bridge lifetime to
+ * @np: device tree node containing encoder output ports
+ * @port: port in the device tree node
+ * @endpoint: endpoint in the device tree node
+ *
+ * Given a DT node's port and endpoint number, finds the connected node
+ * and returns the associated bridge if any.
+ *
+ * Returns a drmm managed pointer to the bridge if successful, or an error
+ * pointer otherwise.
+ */
+struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
+ struct device_node *np,
+ u32 port, u32 endpoint)
+{
+ struct drm_bridge *bridge;
+ int ret;
+
+ ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return bridge;
+}
+EXPORT_SYMBOL(drmm_of_get_bridge);
#endif
MODULE_AUTHOR("Ajay Kumar <ajaykumar.rs@samsung.com>");
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 09/26] drm/bridge: move devm_drm_of_get_bridge and drmm_of_get_bridge to drm_bridge.c
2025-02-06 18:14 ` [PATCH v6 09/26] drm/bridge: move devm_drm_of_get_bridge and drmm_of_get_bridge to drm_bridge.c Luca Ceresoli
@ 2025-02-07 2:52 ` Dmitry Baryshkov
2025-02-07 8:54 ` Luca Ceresoli
0 siblings, 1 reply; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 2:52 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Thu, Feb 06, 2025 at 07:14:24PM +0100, Luca Ceresoli wrote:
> devm_drm_of_get_bridge() and drmm_of_get_bridge() do not have anything to
> do with struct drm_panel anymore, they just manage bridges. So move them
> from bridge/panel.c to drm_bridge.c.
>
> Move also of_drm_find_bridge_by_endpoint() which is used only by
> devm_drm_of_get_bridge() and drmm_of_get_bridge().
>
> No code changes, only move functions to a different file within the same
> module and add an #include as needed.
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> This patch was added in v6.
> ---
> drivers/gpu/drm/bridge/panel.c | 102 -----------------------------------------
> drivers/gpu/drm/drm_bridge.c | 100 ++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 100 insertions(+), 102 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
> index 6995de605e7317dd1eb153afd475746ced764712..1230ae50b2020e7a9306cac83009dd600dd61d26 100644
> --- a/drivers/gpu/drm/bridge/panel.c
> +++ b/drivers/gpu/drm/bridge/panel.c
> @@ -418,49 +418,6 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
> }
> EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
>
> -/**
> - * of_drm_find_bridge_by_endpoint - return drm_bridge connected to an endpoint
> - * @np: device tree node containing encoder output ports
> - * @port: port in the device tree node
> - * @endpoint: endpoint in the device tree node
> - * @bridge: pointer to hold returned drm_bridge (must not be NULL)
> - *
> - * Given a DT node's port and endpoint number, find the connected node and
> - * return the associated struct drm_bridge.
> - *
> - * Returns zero if successful, or one of the standard error codes if it fails.
> - */
> -static int of_drm_find_bridge_by_endpoint(const struct device_node *np,
> - int port, int endpoint,
> - struct drm_bridge **bridge)
I'd say make this function the main API instead (and name it drm_of
rather than of_drm, this can happen in the previous patch).
> -{
> - int ret = -EPROBE_DEFER;
> - struct device_node *remote;
> -
> - if (!bridge)
> - return -EINVAL;
> -
> - /*
> - * of_graph_get_remote_node() produces a noisy error message if port
> - * node isn't found and the absence of the port is a legit case here,
> - * so at first we silently check whether graph presents in the
> - * device-tree node.
> - */
> - if (!of_graph_is_present(np))
> - return -ENODEV;
> -
> - remote = of_graph_get_remote_node(np, port, endpoint);
> - if (!remote)
> - return -ENODEV;
> -
> - *bridge = of_drm_find_bridge(remote);
> - if (*bridge)
> - ret = 0;
> -
> - of_node_put(remote);
> - return ret;
> -}
> -
> /**
> * of_drm_get_panel_orientation - look up the orientation of the panel through
> * the "rotation" binding from a device tree node
> @@ -1150,62 +1107,3 @@ struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge)
> return &panel_bridge->connector;
> }
> EXPORT_SYMBOL(drm_panel_bridge_connector);
> -
> -#ifdef CONFIG_OF
> -/**
> - * devm_drm_of_get_bridge - Return next bridge in the chain
> - * @dev: device to tie the bridge lifetime to
> - * @np: device tree node containing encoder output ports
> - * @port: port in the device tree node
> - * @endpoint: endpoint in the device tree node
> - *
> - * Given a DT node's port and endpoint number, finds the connected node
> - * and returns the associated bridge if any.
> - *
> - * Returns a pointer to the bridge if successful, or an error pointer
> - * otherwise.
> - */
> -struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
> - struct device_node *np,
> - u32 port, u32 endpoint)
> -{
> - struct drm_bridge *bridge;
> - int ret;
> -
> - ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge);
> - if (ret)
> - return ERR_PTR(ret);
> -
> - return bridge;
> -}
Then these two functions can go into the header as static inlines with
the proper deprecation notification. There is little point in having
them in the source file. I think eventually all users should be
converted into using your new of_drm_find_bridge_by_endpoint() funcion.
> -EXPORT_SYMBOL(devm_drm_of_get_bridge);
> -
> -/**
> - * drmm_of_get_bridge - Return next bridge in the chain
> - * @drm: device to tie the bridge lifetime to
> - * @np: device tree node containing encoder output ports
> - * @port: port in the device tree node
> - * @endpoint: endpoint in the device tree node
> - *
> - * Given a DT node's port and endpoint number, finds the connected node
> - * and returns the associated bridge if any.
> - *
> - * Returns a drmm managed pointer to the bridge if successful, or an error
> - * pointer otherwise.
> - */
> -struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
> - struct device_node *np,
> - u32 port, u32 endpoint)
> -{
> - struct drm_bridge *bridge;
> - int ret;
> -
> - ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge);
> - if (ret)
> - return ERR_PTR(ret);
> -
> - return bridge;
> -}
> -EXPORT_SYMBOL(drmm_of_get_bridge);
> -
> -#endif
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index 87cebec2de806781cee22da54d666eee9bde3648..2aa17fbe538b86066c4e68f0d0e8046e9ca9b965 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -25,6 +25,7 @@
> #include <linux/media-bus-format.h>
> #include <linux/module.h>
> #include <linux/mutex.h>
> +#include <linux/of.h>
>
> #include <drm/drm_atomic_state_helper.h>
> #include <drm/drm_bridge.h>
> @@ -1334,6 +1335,105 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np)
> return NULL;
> }
> EXPORT_SYMBOL(of_drm_find_bridge);
> +
> +/**
> + * of_drm_find_bridge_by_endpoint - return drm_bridge connected to an endpoint
> + * @np: device tree node containing encoder output ports
> + * @port: port in the device tree node
> + * @endpoint: endpoint in the device tree node
> + * @bridge: pointer to hold returned drm_bridge (must not be NULL)
> + *
> + * Given a DT node's port and endpoint number, find the connected node and
> + * return the associated struct drm_bridge.
> + *
> + * Returns zero if successful, or one of the standard error codes if it fails.
> + */
> +static int of_drm_find_bridge_by_endpoint(const struct device_node *np,
> + int port, int endpoint,
> + struct drm_bridge **bridge)
> +{
> + int ret = -EPROBE_DEFER;
> + struct device_node *remote;
> +
> + if (!bridge)
> + return -EINVAL;
> +
> + /*
> + * of_graph_get_remote_node() produces a noisy error message if port
> + * node isn't found and the absence of the port is a legit case here,
> + * so at first we silently check whether graph presents in the
> + * device-tree node.
> + */
> + if (!of_graph_is_present(np))
> + return -ENODEV;
> +
> + remote = of_graph_get_remote_node(np, port, endpoint);
> + if (!remote)
> + return -ENODEV;
> +
> + *bridge = of_drm_find_bridge(remote);
> + if (*bridge)
> + ret = 0;
> +
> + of_node_put(remote);
> + return ret;
> +}
> +
> +/**
> + * devm_drm_of_get_bridge - Return next bridge in the chain
> + * @dev: device to tie the bridge lifetime to
> + * @np: device tree node containing encoder output ports
> + * @port: port in the device tree node
> + * @endpoint: endpoint in the device tree node
> + *
> + * Given a DT node's port and endpoint number, finds the connected node
> + * and returns the associated bridge if any.
> + *
> + * Returns a pointer to the bridge if successful, or an error pointer
> + * otherwise.
> + */
> +struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
> + struct device_node *np,
> + u32 port, u32 endpoint)
> +{
> + struct drm_bridge *bridge;
> + int ret;
> +
> + ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + return bridge;
> +}
> +EXPORT_SYMBOL(devm_drm_of_get_bridge);
> +
> +/**
> + * drmm_of_get_bridge - Return next bridge in the chain
> + * @drm: device to tie the bridge lifetime to
> + * @np: device tree node containing encoder output ports
> + * @port: port in the device tree node
> + * @endpoint: endpoint in the device tree node
> + *
> + * Given a DT node's port and endpoint number, finds the connected node
> + * and returns the associated bridge if any.
> + *
> + * Returns a drmm managed pointer to the bridge if successful, or an error
> + * pointer otherwise.
> + */
> +struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
> + struct device_node *np,
> + u32 port, u32 endpoint)
> +{
> + struct drm_bridge *bridge;
> + int ret;
> +
> + ret = of_drm_find_bridge_by_endpoint(np, port, endpoint, &bridge);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + return bridge;
> +}
> +EXPORT_SYMBOL(drmm_of_get_bridge);
> #endif
>
> MODULE_AUTHOR("Ajay Kumar <ajaykumar.rs@samsung.com>");
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 09/26] drm/bridge: move devm_drm_of_get_bridge and drmm_of_get_bridge to drm_bridge.c
2025-02-07 2:52 ` Dmitry Baryshkov
@ 2025-02-07 8:54 ` Luca Ceresoli
2025-02-07 19:47 ` Dmitry Baryshkov
0 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-07 8:54 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Fri, 7 Feb 2025 04:52:20 +0200
Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> On Thu, Feb 06, 2025 at 07:14:24PM +0100, Luca Ceresoli wrote:
> > devm_drm_of_get_bridge() and drmm_of_get_bridge() do not have anything to
> > do with struct drm_panel anymore, they just manage bridges. So move them
> > from bridge/panel.c to drm_bridge.c.
> >
> > Move also of_drm_find_bridge_by_endpoint() which is used only by
> > devm_drm_of_get_bridge() and drmm_of_get_bridge().
> >
> > No code changes, only move functions to a different file within the same
> > module and add an #include as needed.
> >
> > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> >
> > ---
> >
> > This patch was added in v6.
> > ---
> > drivers/gpu/drm/bridge/panel.c | 102 -----------------------------------------
> > drivers/gpu/drm/drm_bridge.c | 100 ++++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 100 insertions(+), 102 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
> > index 6995de605e7317dd1eb153afd475746ced764712..1230ae50b2020e7a9306cac83009dd600dd61d26 100644
> > --- a/drivers/gpu/drm/bridge/panel.c
> > +++ b/drivers/gpu/drm/bridge/panel.c
> > @@ -418,49 +418,6 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
> > }
> > EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
> >
> > -/**
> > - * of_drm_find_bridge_by_endpoint - return drm_bridge connected to an endpoint
> > - * @np: device tree node containing encoder output ports
> > - * @port: port in the device tree node
> > - * @endpoint: endpoint in the device tree node
> > - * @bridge: pointer to hold returned drm_bridge (must not be NULL)
> > - *
> > - * Given a DT node's port and endpoint number, find the connected node and
> > - * return the associated struct drm_bridge.
> > - *
> > - * Returns zero if successful, or one of the standard error codes if it fails.
> > - */
> > -static int of_drm_find_bridge_by_endpoint(const struct device_node *np,
> > - int port, int endpoint,
> > - struct drm_bridge **bridge)
>
> I'd say make this function the main API instead (and name it drm_of
> rather than of_drm, this can happen in the previous patch).
I agree there should be a small number of APIs for the foreseeable
future (and any number of, hopefully decreasing-at-some-point,
deprecated ones).
And I agree this one ^ and the devm_drm_of_get_bridge() below are
equivalent, despite having different signatures, and so one should
disappear.
So, time to think about what APIs we want. Some thoughts of mine:
* I prefer "get" over "find", looks more intuitive as these functions
will drm_bridge_get()
* Is there a logic between of_drm_ and drm_of_? Just "the former is
old and deprecated"?
* Since getting bridges via the endpoint is the preferred way, I'd
like this function to have a shorter name than its variants
* Returning a struct drm_bridge err_ptr looks better to me than
returning an error and the bridge via a ptr-to-ptr, especially as we
don't have anymore the case of returning a panel or a bridge from
the same function
So, bottom line, we'd have:
- struct drm_bridge *drm_of_get_bridge(np, port, endpoint)
- struct drm_bridge *drm_of_get_bridge_by_node(bridge_np)
- devm_ and drmm_ variants of the above
or a subset of these, in case some is not needed.
What are your opinions?
> > -struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
> > - struct device_node *np,
> > - u32 port, u32 endpoint)
^ kept for reference
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 09/26] drm/bridge: move devm_drm_of_get_bridge and drmm_of_get_bridge to drm_bridge.c
2025-02-07 8:54 ` Luca Ceresoli
@ 2025-02-07 19:47 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 19:47 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Fri, Feb 07, 2025 at 09:54:21AM +0100, Luca Ceresoli wrote:
> On Fri, 7 Feb 2025 04:52:20 +0200
> Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
>
> > On Thu, Feb 06, 2025 at 07:14:24PM +0100, Luca Ceresoli wrote:
> > > devm_drm_of_get_bridge() and drmm_of_get_bridge() do not have anything to
> > > do with struct drm_panel anymore, they just manage bridges. So move them
> > > from bridge/panel.c to drm_bridge.c.
> > >
> > > Move also of_drm_find_bridge_by_endpoint() which is used only by
> > > devm_drm_of_get_bridge() and drmm_of_get_bridge().
> > >
> > > No code changes, only move functions to a different file within the same
> > > module and add an #include as needed.
> > >
> > > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> > >
> > > ---
> > >
> > > This patch was added in v6.
> > > ---
> > > drivers/gpu/drm/bridge/panel.c | 102 -----------------------------------------
> > > drivers/gpu/drm/drm_bridge.c | 100 ++++++++++++++++++++++++++++++++++++++++
> > > 2 files changed, 100 insertions(+), 102 deletions(-)
> > >
> > > diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
> > > index 6995de605e7317dd1eb153afd475746ced764712..1230ae50b2020e7a9306cac83009dd600dd61d26 100644
> > > --- a/drivers/gpu/drm/bridge/panel.c
> > > +++ b/drivers/gpu/drm/bridge/panel.c
> > > @@ -418,49 +418,6 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
> > > }
> > > EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
> > >
> > > -/**
> > > - * of_drm_find_bridge_by_endpoint - return drm_bridge connected to an endpoint
> > > - * @np: device tree node containing encoder output ports
> > > - * @port: port in the device tree node
> > > - * @endpoint: endpoint in the device tree node
> > > - * @bridge: pointer to hold returned drm_bridge (must not be NULL)
> > > - *
> > > - * Given a DT node's port and endpoint number, find the connected node and
> > > - * return the associated struct drm_bridge.
> > > - *
> > > - * Returns zero if successful, or one of the standard error codes if it fails.
> > > - */
> > > -static int of_drm_find_bridge_by_endpoint(const struct device_node *np,
> > > - int port, int endpoint,
> > > - struct drm_bridge **bridge)
> >
> > I'd say make this function the main API instead (and name it drm_of
> > rather than of_drm, this can happen in the previous patch).
>
> I agree there should be a small number of APIs for the foreseeable
> future (and any number of, hopefully decreasing-at-some-point,
> deprecated ones).
>
> And I agree this one ^ and the devm_drm_of_get_bridge() below are
> equivalent, despite having different signatures, and so one should
> disappear.
>
> So, time to think about what APIs we want. Some thoughts of mine:
>
> * I prefer "get" over "find", looks more intuitive as these functions
> will drm_bridge_get()
> * Is there a logic between of_drm_ and drm_of_? Just "the former is
> old and deprecated"?
I don't know, it might be historical. Nevertheless, I think, having just
drm_ prefix for all DRM-related symbols is a good idea.
> * Since getting bridges via the endpoint is the preferred way, I'd
> like this function to have a shorter name than its variants
> * Returning a struct drm_bridge err_ptr looks better to me than
> returning an error and the bridge via a ptr-to-ptr, especially as we
> don't have anymore the case of returning a panel or a bridge from
> the same function
>
> So, bottom line, we'd have:
>
> - struct drm_bridge *drm_of_get_bridge(np, port, endpoint)
> - struct drm_bridge *drm_of_get_bridge_by_node(bridge_np)
I think these two are fine, please go with them
> - devm_ and drmm_ variants of the above
These two are only necessary for the refcounted bridges. I'd say, skip
them as a part of the panel / bridge patchset. Please don't
overcomplicate it too much.
>
> or a subset of these, in case some is not needed.
>
> What are your opinions?
>
> > > -struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
> > > - struct device_node *np,
> > > - u32 port, u32 endpoint)
>
> ^ kept for reference
>
> Luca
>
> --
> Luca Ceresoli, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 10/26] drm/bridge: add devm_drm_of_get_bridge_by_node()
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (8 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 09/26] drm/bridge: move devm_drm_of_get_bridge and drmm_of_get_bridge to drm_bridge.c Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 2:53 ` Dmitry Baryshkov
2025-02-10 18:22 ` Maxime Ripard
2025-02-06 18:14 ` [PATCH v6 11/26] drm/bridge: samsung-dsim: use devm_drm_of_get_bridge[_by_node] to find the out_bridge Luca Ceresoli
` (16 subsequent siblings)
26 siblings, 2 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
devm_drm_of_get_bridge(), which is based on graph links, is the recommended
function to get a pointer to the following bridge.
This is valid even for panels, for which the recommended device tree
description is via graph links and not (or not only) panel subnodes of a
panel controller (e.g. "dsi@1234" controller node with a "panel@0"
subnode).
However there are drivers supporting the panel subnode description in
addition to the graph links. For those drivers add a _by_node variant that
takes the node of the target node.
Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/drm_bridge.c | 30 ++++++++++++++++++++++++++++++
include/drm/drm_bridge.h | 8 ++++++++
2 files changed, 38 insertions(+)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 2aa17fbe538b86066c4e68f0d0e8046e9ca9b965..b0834b8644284e5f7751cec81724af849b4180e7 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -1407,6 +1407,36 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
}
EXPORT_SYMBOL(devm_drm_of_get_bridge);
+/**
+ * devm_drm_of_get_bridge_by_node - Return bridge for a given OF node
+ * @dev: device to tie the bridge lifetime to
+ * @bridge_node: device node of the remote bridge
+ *
+ * Given a bridge DT node, returns the associated bridge if any. This
+ * should be used in addition to devm_drm_of_get_bridge() when the regular
+ * graph link search is not enough, e.g. for drivers that need to support
+ * panels described only as subnodes.
+ *
+ * RETURNS:
+ * A pointer to the bridge if successful, or an error pointer otherwise.
+ */
+struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *dev,
+ struct device_node *bridge_node)
+{
+ struct drm_bridge *bridge;
+ int ret;
+
+ if (!bridge_node)
+ return ERR_PTR(-EINVAL);
+
+ bridge = of_drm_find_bridge(bridge_node);
+ if (!bridge)
+ return ERR_PTR(-ENODEV);
+
+ return bridge;
+}
+EXPORT_SYMBOL(devm_drm_of_get_bridge_by_node);
+
/**
* drmm_of_get_bridge - Return next bridge in the chain
* @drm: device to tie the bridge lifetime to
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 496dbbd2ad7edff7f091adfbe62de1e33ef0cf07..1561347c4991dac6022319774510f9560c9283c3 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -1088,6 +1088,8 @@ static inline int drm_panel_bridge_set_orientation(struct drm_connector *connect
#if defined(CONFIG_OF) && defined(CONFIG_DRM_PANEL_BRIDGE)
struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, struct device_node *node,
u32 port, u32 endpoint);
+struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *dev,
+ struct device_node *bridge_node);
struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, struct device_node *node,
u32 port, u32 endpoint);
#else
@@ -1099,6 +1101,12 @@ static inline struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
return ERR_PTR(-ENODEV);
}
+static inline struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *dev,
+ struct device_node *bridge_node)
+{
+ return ERR_PTR(-ENODEV);
+}
+
static inline struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
struct device_node *node,
u32 port,
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 10/26] drm/bridge: add devm_drm_of_get_bridge_by_node()
2025-02-06 18:14 ` [PATCH v6 10/26] drm/bridge: add devm_drm_of_get_bridge_by_node() Luca Ceresoli
@ 2025-02-07 2:53 ` Dmitry Baryshkov
2025-02-07 8:54 ` Luca Ceresoli
2025-02-10 18:22 ` Maxime Ripard
1 sibling, 1 reply; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 2:53 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Thu, Feb 06, 2025 at 07:14:25PM +0100, Luca Ceresoli wrote:
> devm_drm_of_get_bridge(), which is based on graph links, is the recommended
> function to get a pointer to the following bridge.
>
> This is valid even for panels, for which the recommended device tree
> description is via graph links and not (or not only) panel subnodes of a
> panel controller (e.g. "dsi@1234" controller node with a "panel@0"
> subnode).
>
> However there are drivers supporting the panel subnode description in
I'd say, name them here (at least the one which we looked upon).
> addition to the graph links. For those drivers add a _by_node variant that
> takes the node of the target node.
>
> Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 10/26] drm/bridge: add devm_drm_of_get_bridge_by_node()
2025-02-07 2:53 ` Dmitry Baryshkov
@ 2025-02-07 8:54 ` Luca Ceresoli
0 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-07 8:54 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Fri, 7 Feb 2025 04:53:32 +0200
Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> On Thu, Feb 06, 2025 at 07:14:25PM +0100, Luca Ceresoli wrote:
> > devm_drm_of_get_bridge(), which is based on graph links, is the recommended
> > function to get a pointer to the following bridge.
> >
> > This is valid even for panels, for which the recommended device tree
> > description is via graph links and not (or not only) panel subnodes of a
> > panel controller (e.g. "dsi@1234" controller node with a "panel@0"
> > subnode).
> >
> > However there are drivers supporting the panel subnode description in
>
> I'd say, name them here (at least the one which we looked upon).
Sure, adding the samsung-dsim to the commit message. I'm not aware of
other drivers doing the same, but I will do a quick search.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 10/26] drm/bridge: add devm_drm_of_get_bridge_by_node()
2025-02-06 18:14 ` [PATCH v6 10/26] drm/bridge: add devm_drm_of_get_bridge_by_node() Luca Ceresoli
2025-02-07 2:53 ` Dmitry Baryshkov
@ 2025-02-10 18:22 ` Maxime Ripard
1 sibling, 0 replies; 81+ messages in thread
From: Maxime Ripard @ 2025-02-10 18:22 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Inki Dae, linux-arm-kernel,
Neil Armstrong, Boris Brezillon, linux-kernel, Paul Kocialkowski,
Pengutronix Kernel Team, Dmitry Baryshkov, Shawn Guo
[-- Attachment #1: Type: text/plain, Size: 2458 bytes --]
On Thu, Feb 06, 2025 at 07:14:25PM +0100, Luca Ceresoli wrote:
> devm_drm_of_get_bridge(), which is based on graph links, is the recommended
> function to get a pointer to the following bridge.
>
> This is valid even for panels, for which the recommended device tree
> description is via graph links and not (or not only) panel subnodes of a
> panel controller (e.g. "dsi@1234" controller node with a "panel@0"
> subnode).
>
> However there are drivers supporting the panel subnode description in
> addition to the graph links. For those drivers add a _by_node variant that
> takes the node of the target node.
>
> Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> This patch was added in v6.
> ---
> drivers/gpu/drm/drm_bridge.c | 30 ++++++++++++++++++++++++++++++
> include/drm/drm_bridge.h | 8 ++++++++
> 2 files changed, 38 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index 2aa17fbe538b86066c4e68f0d0e8046e9ca9b965..b0834b8644284e5f7751cec81724af849b4180e7 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -1407,6 +1407,36 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
> }
> EXPORT_SYMBOL(devm_drm_of_get_bridge);
>
> +/**
> + * devm_drm_of_get_bridge_by_node - Return bridge for a given OF node
> + * @dev: device to tie the bridge lifetime to
> + * @bridge_node: device node of the remote bridge
> + *
> + * Given a bridge DT node, returns the associated bridge if any. This
> + * should be used in addition to devm_drm_of_get_bridge() when the regular
> + * graph link search is not enough, e.g. for drivers that need to support
> + * panels described only as subnodes.
> + *
> + * RETURNS:
> + * A pointer to the bridge if successful, or an error pointer otherwise.
> + */
> +struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *dev,
> + struct device_node *bridge_node)
> +{
> + struct drm_bridge *bridge;
> + int ret;
> +
> + if (!bridge_node)
> + return ERR_PTR(-EINVAL);
> +
> + bridge = of_drm_find_bridge(bridge_node);
> + if (!bridge)
> + return ERR_PTR(-ENODEV);
> +
> + return bridge;
> +}
> +EXPORT_SYMBOL(devm_drm_of_get_bridge_by_node);
> +
A bridge is a KMS-facing structure, there's no reason to tie it to the
lifetime of the device.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 11/26] drm/bridge: samsung-dsim: use devm_drm_of_get_bridge[_by_node] to find the out_bridge
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (9 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 10/26] drm/bridge: add devm_drm_of_get_bridge_by_node() Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 2:55 ` Dmitry Baryshkov
2025-02-10 18:23 ` Maxime Ripard
2025-02-06 18:14 ` [PATCH v6 12/26] drm/bridge: allow bridges to be informed about added and removed bridges Luca Ceresoli
` (15 subsequent siblings)
26 siblings, 2 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
In order to support panels described either via graph links or via a
subnode (e.g. "panel@0"), this driver uses low-level deprecated functions
to find the next bridge. The resulting logic is complex and duplicates code
already present in the DRM bridge core. Switch to the new APIs in DRM
bridge core that allow to do the same in a much cleaner way.
Note there are two slight changes in the new logic intended to improve the
final result:
* the old code looks for a subnode with any name except "port" or "ports",
while the new code uses the node passed as a parameter
* the old code looks for a subnode first and falls back to a graph link,
while the new code uses the reverse order because graph links are the
recommended device tree representation now
The first change makes the code more robust by avoiding the risk of using
an unrelated node which is not describing a panel and not names "port" or
"ports".
The second change is not expected to expose regressions because, in the
cases where both a subnode and a graph link are used to describe a panel,
the graph link should point to the subnode itself, such as in
arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
As a further cleanup, use a temporary variable to assign dsi->out_bridge
only on success. This avoids the risk of leaving a non-NULL value in
dsi->out_bridge when samsung_dsim_host_attach() fails.
Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/bridge/samsung-dsim.c | 55 ++++++-----------------------------
1 file changed, 9 insertions(+), 46 deletions(-)
diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
index f8b4fb8357659018ec0db65374ee5d05330639ae..bbd0a4f5a3f52b61bf48f10d6e8ca741bffa5e46 100644
--- a/drivers/gpu/drm/bridge/samsung-dsim.c
+++ b/drivers/gpu/drm/bridge/samsung-dsim.c
@@ -1704,55 +1704,16 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
const struct samsung_dsim_plat_data *pdata = dsi->plat_data;
struct device *dev = dsi->dev;
struct device_node *np = dev->of_node;
- struct device_node *remote;
- struct drm_panel *panel;
+ struct drm_bridge *out_bridge;
int ret;
- /*
- * Devices can also be child nodes when we also control that device
- * through the upstream device (ie, MIPI-DCS for a MIPI-DSI device).
- *
- * Lookup for a child node of the given parent that isn't either port
- * or ports.
- */
- for_each_available_child_of_node(np, remote) {
- if (of_node_name_eq(remote, "port") ||
- of_node_name_eq(remote, "ports"))
- continue;
+ out_bridge = devm_drm_of_get_bridge(dev, np, 1, 0);
+ if (IS_ERR(out_bridge) && PTR_ERR(out_bridge) != -EPROBE_DEFER)
+ out_bridge = devm_drm_of_get_bridge_by_node(dev, device->dev.of_node);
- goto of_find_panel_or_bridge;
- }
-
- /*
- * of_graph_get_remote_node() produces a noisy error message if port
- * node isn't found and the absence of the port is a legit case here,
- * so at first we silently check whether graph presents in the
- * device-tree node.
- */
- if (!of_graph_is_present(np))
- return -ENODEV;
-
- remote = of_graph_get_remote_node(np, 1, 0);
-
-of_find_panel_or_bridge:
- if (!remote)
- return -ENODEV;
-
- panel = of_drm_find_panel(remote);
- if (!IS_ERR(panel)) {
- dsi->out_bridge = devm_drm_panel_bridge_add(dev, panel);
- } else {
- dsi->out_bridge = of_drm_find_bridge(remote);
- if (!dsi->out_bridge)
- dsi->out_bridge = ERR_PTR(-EINVAL);
- }
-
- of_node_put(remote);
-
- if (IS_ERR(dsi->out_bridge)) {
- ret = PTR_ERR(dsi->out_bridge);
- DRM_DEV_ERROR(dev, "failed to find the bridge: %d\n", ret);
- return ret;
+ if (IS_ERR(out_bridge)) {
+ DRM_DEV_ERROR(dev, "failed to find the bridge: %ld\n", PTR_ERR(out_bridge));
+ return PTR_ERR(out_bridge);
}
DRM_DEV_INFO(dev, "Attached %s device (lanes:%d bpp:%d mode-flags:0x%lx)\n",
@@ -1784,6 +1745,8 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
dsi->format = device->format;
dsi->mode_flags = device->mode_flags;
+ dsi->out_bridge = out_bridge;
+
return 0;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 11/26] drm/bridge: samsung-dsim: use devm_drm_of_get_bridge[_by_node] to find the out_bridge
2025-02-06 18:14 ` [PATCH v6 11/26] drm/bridge: samsung-dsim: use devm_drm_of_get_bridge[_by_node] to find the out_bridge Luca Ceresoli
@ 2025-02-07 2:55 ` Dmitry Baryshkov
2025-02-07 8:54 ` Luca Ceresoli
2025-02-10 18:23 ` Maxime Ripard
1 sibling, 1 reply; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 2:55 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Thu, Feb 06, 2025 at 07:14:26PM +0100, Luca Ceresoli wrote:
> In order to support panels described either via graph links or via a
> subnode (e.g. "panel@0"), this driver uses low-level deprecated functions
> to find the next bridge. The resulting logic is complex and duplicates code
> already present in the DRM bridge core. Switch to the new APIs in DRM
> bridge core that allow to do the same in a much cleaner way.
>
> Note there are two slight changes in the new logic intended to improve the
> final result:
>
> * the old code looks for a subnode with any name except "port" or "ports",
> while the new code uses the node passed as a parameter
>
> * the old code looks for a subnode first and falls back to a graph link,
> while the new code uses the reverse order because graph links are the
> recommended device tree representation now
>
> The first change makes the code more robust by avoiding the risk of using
> an unrelated node which is not describing a panel and not names "port" or
> "ports".
>
> The second change is not expected to expose regressions because, in the
> cases where both a subnode and a graph link are used to describe a panel,
> the graph link should point to the subnode itself, such as in
> arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
>
> As a further cleanup, use a temporary variable to assign dsi->out_bridge
> only on success. This avoids the risk of leaving a non-NULL value in
> dsi->out_bridge when samsung_dsim_host_attach() fails.
>
> Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> This patch was added in v6.
> ---
> drivers/gpu/drm/bridge/samsung-dsim.c | 55 ++++++-----------------------------
> 1 file changed, 9 insertions(+), 46 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
> index f8b4fb8357659018ec0db65374ee5d05330639ae..bbd0a4f5a3f52b61bf48f10d6e8ca741bffa5e46 100644
> --- a/drivers/gpu/drm/bridge/samsung-dsim.c
> +++ b/drivers/gpu/drm/bridge/samsung-dsim.c
> @@ -1704,55 +1704,16 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
> const struct samsung_dsim_plat_data *pdata = dsi->plat_data;
> struct device *dev = dsi->dev;
> struct device_node *np = dev->of_node;
> - struct device_node *remote;
> - struct drm_panel *panel;
> + struct drm_bridge *out_bridge;
> int ret;
>
> - /*
> - * Devices can also be child nodes when we also control that device
> - * through the upstream device (ie, MIPI-DCS for a MIPI-DSI device).
> - *
> - * Lookup for a child node of the given parent that isn't either port
> - * or ports.
> - */
Please leave the comment in place (maybe rewrite it slightly).
> - for_each_available_child_of_node(np, remote) {
> - if (of_node_name_eq(remote, "port") ||
> - of_node_name_eq(remote, "ports"))
> - continue;
> + out_bridge = devm_drm_of_get_bridge(dev, np, 1, 0);
> + if (IS_ERR(out_bridge) && PTR_ERR(out_bridge) != -EPROBE_DEFER)
Can it actually return EPROBE_DEFER?
> + out_bridge = devm_drm_of_get_bridge_by_node(dev, device->dev.of_node);
>
> - goto of_find_panel_or_bridge;
> - }
> -
> - /*
> - * of_graph_get_remote_node() produces a noisy error message if port
> - * node isn't found and the absence of the port is a legit case here,
> - * so at first we silently check whether graph presents in the
> - * device-tree node.
> - */
> - if (!of_graph_is_present(np))
> - return -ENODEV;
> -
> - remote = of_graph_get_remote_node(np, 1, 0);
> -
> -of_find_panel_or_bridge:
> - if (!remote)
> - return -ENODEV;
> -
> - panel = of_drm_find_panel(remote);
> - if (!IS_ERR(panel)) {
> - dsi->out_bridge = devm_drm_panel_bridge_add(dev, panel);
> - } else {
> - dsi->out_bridge = of_drm_find_bridge(remote);
> - if (!dsi->out_bridge)
> - dsi->out_bridge = ERR_PTR(-EINVAL);
> - }
> -
> - of_node_put(remote);
> -
> - if (IS_ERR(dsi->out_bridge)) {
> - ret = PTR_ERR(dsi->out_bridge);
> - DRM_DEV_ERROR(dev, "failed to find the bridge: %d\n", ret);
> - return ret;
> + if (IS_ERR(out_bridge)) {
> + DRM_DEV_ERROR(dev, "failed to find the bridge: %ld\n", PTR_ERR(out_bridge));
> + return PTR_ERR(out_bridge);
> }
>
> DRM_DEV_INFO(dev, "Attached %s device (lanes:%d bpp:%d mode-flags:0x%lx)\n",
> @@ -1784,6 +1745,8 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
> dsi->format = device->format;
> dsi->mode_flags = device->mode_flags;
>
> + dsi->out_bridge = out_bridge;
> +
Please move the assignment closer to the original place.
> return 0;
> }
>
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 11/26] drm/bridge: samsung-dsim: use devm_drm_of_get_bridge[_by_node] to find the out_bridge
2025-02-07 2:55 ` Dmitry Baryshkov
@ 2025-02-07 8:54 ` Luca Ceresoli
0 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-07 8:54 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Fri, 7 Feb 2025 04:55:54 +0200
Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> On Thu, Feb 06, 2025 at 07:14:26PM +0100, Luca Ceresoli wrote:
> > In order to support panels described either via graph links or via a
> > subnode (e.g. "panel@0"), this driver uses low-level deprecated functions
> > to find the next bridge. The resulting logic is complex and duplicates code
> > already present in the DRM bridge core. Switch to the new APIs in DRM
> > bridge core that allow to do the same in a much cleaner way.
> >
> > Note there are two slight changes in the new logic intended to improve the
> > final result:
> >
> > * the old code looks for a subnode with any name except "port" or "ports",
> > while the new code uses the node passed as a parameter
> >
> > * the old code looks for a subnode first and falls back to a graph link,
> > while the new code uses the reverse order because graph links are the
> > recommended device tree representation now
> >
> > The first change makes the code more robust by avoiding the risk of using
> > an unrelated node which is not describing a panel and not names "port" or
> > "ports".
> >
> > The second change is not expected to expose regressions because, in the
> > cases where both a subnode and a graph link are used to describe a panel,
> > the graph link should point to the subnode itself, such as in
> > arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
> >
> > As a further cleanup, use a temporary variable to assign dsi->out_bridge
> > only on success. This avoids the risk of leaving a non-NULL value in
> > dsi->out_bridge when samsung_dsim_host_attach() fails.
> >
> > Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> >
> > ---
> >
> > This patch was added in v6.
> > ---
> > drivers/gpu/drm/bridge/samsung-dsim.c | 55 ++++++-----------------------------
> > 1 file changed, 9 insertions(+), 46 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
> > index f8b4fb8357659018ec0db65374ee5d05330639ae..bbd0a4f5a3f52b61bf48f10d6e8ca741bffa5e46 100644
> > --- a/drivers/gpu/drm/bridge/samsung-dsim.c
> > +++ b/drivers/gpu/drm/bridge/samsung-dsim.c
> > @@ -1704,55 +1704,16 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
> > const struct samsung_dsim_plat_data *pdata = dsi->plat_data;
> > struct device *dev = dsi->dev;
> > struct device_node *np = dev->of_node;
> > - struct device_node *remote;
> > - struct drm_panel *panel;
> > + struct drm_bridge *out_bridge;
> > int ret;
> >
> > - /*
> > - * Devices can also be child nodes when we also control that device
> > - * through the upstream device (ie, MIPI-DCS for a MIPI-DSI device).
> > - *
> > - * Lookup for a child node of the given parent that isn't either port
> > - * or ports.
> > - */
>
> Please leave the comment in place (maybe rewrite it slightly).
OK
> > - for_each_available_child_of_node(np, remote) {
> > - if (of_node_name_eq(remote, "port") ||
> > - of_node_name_eq(remote, "ports"))
> > - continue;
> > + out_bridge = devm_drm_of_get_bridge(dev, np, 1, 0);
> > + if (IS_ERR(out_bridge) && PTR_ERR(out_bridge) != -EPROBE_DEFER)
>
> Can it actually return EPROBE_DEFER?
It can't indeed. Dropping the second condition.
>
> > + out_bridge = devm_drm_of_get_bridge_by_node(dev, device->dev.of_node);
> >
> > - goto of_find_panel_or_bridge;
> > - }
> > -
> > - /*
> > - * of_graph_get_remote_node() produces a noisy error message if port
> > - * node isn't found and the absence of the port is a legit case here,
> > - * so at first we silently check whether graph presents in the
> > - * device-tree node.
> > - */
> > - if (!of_graph_is_present(np))
> > - return -ENODEV;
> > -
> > - remote = of_graph_get_remote_node(np, 1, 0);
> > -
> > -of_find_panel_or_bridge:
> > - if (!remote)
> > - return -ENODEV;
> > -
> > - panel = of_drm_find_panel(remote);
> > - if (!IS_ERR(panel)) {
> > - dsi->out_bridge = devm_drm_panel_bridge_add(dev, panel);
> > - } else {
> > - dsi->out_bridge = of_drm_find_bridge(remote);
> > - if (!dsi->out_bridge)
> > - dsi->out_bridge = ERR_PTR(-EINVAL);
> > - }
> > -
> > - of_node_put(remote);
> > -
> > - if (IS_ERR(dsi->out_bridge)) {
> > - ret = PTR_ERR(dsi->out_bridge);
> > - DRM_DEV_ERROR(dev, "failed to find the bridge: %d\n", ret);
> > - return ret;
> > + if (IS_ERR(out_bridge)) {
> > + DRM_DEV_ERROR(dev, "failed to find the bridge: %ld\n", PTR_ERR(out_bridge));
> > + return PTR_ERR(out_bridge);
> > }
> >
> > DRM_DEV_INFO(dev, "Attached %s device (lanes:%d bpp:%d mode-flags:0x%lx)\n",
> > @@ -1784,6 +1745,8 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
> > dsi->format = device->format;
> > dsi->mode_flags = device->mode_flags;
> >
> > + dsi->out_bridge = out_bridge;
> > +
>
> Please move the assignment closer to the original place.
OK. With this change it doesn't make sense to have a temporary
out_bridge variable, so I'll drop it as well.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 11/26] drm/bridge: samsung-dsim: use devm_drm_of_get_bridge[_by_node] to find the out_bridge
2025-02-06 18:14 ` [PATCH v6 11/26] drm/bridge: samsung-dsim: use devm_drm_of_get_bridge[_by_node] to find the out_bridge Luca Ceresoli
2025-02-07 2:55 ` Dmitry Baryshkov
@ 2025-02-10 18:23 ` Maxime Ripard
1 sibling, 0 replies; 81+ messages in thread
From: Maxime Ripard @ 2025-02-10 18:23 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Inki Dae, linux-arm-kernel,
Neil Armstrong, Boris Brezillon, linux-kernel, Paul Kocialkowski,
Pengutronix Kernel Team, Dmitry Baryshkov, Shawn Guo
[-- Attachment #1: Type: text/plain, Size: 3269 bytes --]
On Thu, Feb 06, 2025 at 07:14:26PM +0100, Luca Ceresoli wrote:
> In order to support panels described either via graph links or via a
> subnode (e.g. "panel@0"), this driver uses low-level deprecated functions
> to find the next bridge. The resulting logic is complex and duplicates code
> already present in the DRM bridge core. Switch to the new APIs in DRM
> bridge core that allow to do the same in a much cleaner way.
>
> Note there are two slight changes in the new logic intended to improve the
> final result:
>
> * the old code looks for a subnode with any name except "port" or "ports",
> while the new code uses the node passed as a parameter
>
> * the old code looks for a subnode first and falls back to a graph link,
> while the new code uses the reverse order because graph links are the
> recommended device tree representation now
>
> The first change makes the code more robust by avoiding the risk of using
> an unrelated node which is not describing a panel and not names "port" or
> "ports".
>
> The second change is not expected to expose regressions because, in the
> cases where both a subnode and a graph link are used to describe a panel,
> the graph link should point to the subnode itself, such as in
> arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
>
> As a further cleanup, use a temporary variable to assign dsi->out_bridge
> only on success. This avoids the risk of leaving a non-NULL value in
> dsi->out_bridge when samsung_dsim_host_attach() fails.
>
> Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> This patch was added in v6.
> ---
> drivers/gpu/drm/bridge/samsung-dsim.c | 55 ++++++-----------------------------
> 1 file changed, 9 insertions(+), 46 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
> index f8b4fb8357659018ec0db65374ee5d05330639ae..bbd0a4f5a3f52b61bf48f10d6e8ca741bffa5e46 100644
> --- a/drivers/gpu/drm/bridge/samsung-dsim.c
> +++ b/drivers/gpu/drm/bridge/samsung-dsim.c
> @@ -1704,55 +1704,16 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
> const struct samsung_dsim_plat_data *pdata = dsi->plat_data;
> struct device *dev = dsi->dev;
> struct device_node *np = dev->of_node;
> - struct device_node *remote;
> - struct drm_panel *panel;
> + struct drm_bridge *out_bridge;
> int ret;
>
> - /*
> - * Devices can also be child nodes when we also control that device
> - * through the upstream device (ie, MIPI-DCS for a MIPI-DSI device).
> - *
> - * Lookup for a child node of the given parent that isn't either port
> - * or ports.
> - */
> - for_each_available_child_of_node(np, remote) {
> - if (of_node_name_eq(remote, "port") ||
> - of_node_name_eq(remote, "ports"))
> - continue;
> + out_bridge = devm_drm_of_get_bridge(dev, np, 1, 0);
> + if (IS_ERR(out_bridge) && PTR_ERR(out_bridge) != -EPROBE_DEFER)
> + out_bridge = devm_drm_of_get_bridge_by_node(dev, device->dev.of_node);
>
For the same reason I mentioned earlier, this is inherently unsafe if
the bridge device goes away but the DRM device doesn't.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 12/26] drm/bridge: allow bridges to be informed about added and removed bridges
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (10 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 11/26] drm/bridge: samsung-dsim: use devm_drm_of_get_bridge[_by_node] to find the out_bridge Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 3:01 ` Dmitry Baryshkov
2025-02-06 18:14 ` [PATCH v6 13/26] drm/encoder: add drm_encoder_cleanup_from() Luca Ceresoli
` (14 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
In preparation for allowing bridges to be added to and removed from a DRM
card without destroying the whole card, add a new DRM bridge function
called on addition and removal of bridges.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
Changed in v6:
- rebased fixing conflicts
Changed in v5:
- fixed kerneldoc errors
This patch was added in v4.
---
drivers/gpu/drm/drm_bridge.c | 12 ++++++++++++
include/drm/drm_bridge.h | 23 +++++++++++++++++++++++
2 files changed, 35 insertions(+)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index b0834b8644284e5f7751cec81724af849b4180e7..1955a231378050abf1071d74a145831b425c47c5 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -207,12 +207,18 @@ LIST_HEAD(bridge_list);
*/
void drm_bridge_add(struct drm_bridge *bridge)
{
+ struct drm_bridge *br, *tmp;
+
mutex_init(&bridge->hpd_mutex);
if (bridge->ops & DRM_BRIDGE_OP_HDMI)
bridge->ycbcr_420_allowed = !!(bridge->supported_formats &
BIT(HDMI_COLORSPACE_YUV420));
+ list_for_each_entry_safe(br, tmp, &bridge_list, list)
+ if (br->funcs->bridge_event_notify)
+ br->funcs->bridge_event_notify(br, DRM_EVENT_BRIDGE_ADD, bridge);
+
mutex_lock(&bridge_lock);
list_add_tail(&bridge->list, &bridge_list);
mutex_unlock(&bridge_lock);
@@ -249,10 +255,16 @@ EXPORT_SYMBOL(devm_drm_bridge_add);
*/
void drm_bridge_remove(struct drm_bridge *bridge)
{
+ struct drm_bridge *br, *tmp;
+
mutex_lock(&bridge_lock);
list_del_init(&bridge->list);
mutex_unlock(&bridge_lock);
+ list_for_each_entry_safe(br, tmp, &bridge_list, list)
+ if (br->funcs->bridge_event_notify)
+ br->funcs->bridge_event_notify(br, DRM_EVENT_BRIDGE_REMOVE, bridge);
+
mutex_destroy(&bridge->hpd_mutex);
}
EXPORT_SYMBOL(drm_bridge_remove);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 1561347c4991dac6022319774510f9560c9283c3..ad7ba444a13e5ecf16f996de3742e4ac67dc21f1 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -56,6 +56,11 @@ enum drm_bridge_attach_flags {
DRM_BRIDGE_ATTACH_NO_CONNECTOR = BIT(0),
};
+enum drm_bridge_event_type {
+ DRM_EVENT_BRIDGE_ADD,
+ DRM_EVENT_BRIDGE_REMOVE,
+};
+
/**
* struct drm_bridge_funcs - drm_bridge control functions
*/
@@ -729,6 +734,24 @@ struct drm_bridge_funcs {
struct drm_bridge *bridge,
bool enable, int direction);
+ /**
+ * @bridge_event_notify:
+ *
+ * Notify that another bridge is being added or removed.
+ *
+ * This callback is optional. Bridges implementing it must always
+ * check whether the event refers to a bridge they actually need to
+ * interact with.
+ *
+ * @bridge: bridge being notified
+ * @event: event happened (add/remove bridge)
+ * @event_bridge: the bridge mentioned by the event (i.e. the
+ * bridge being added or removed)
+ */
+ void (*bridge_event_notify)(struct drm_bridge *bridge,
+ enum drm_bridge_event_type event,
+ struct drm_bridge *event_bridge);
+
/**
* @debugfs_init:
*
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 12/26] drm/bridge: allow bridges to be informed about added and removed bridges
2025-02-06 18:14 ` [PATCH v6 12/26] drm/bridge: allow bridges to be informed about added and removed bridges Luca Ceresoli
@ 2025-02-07 3:01 ` Dmitry Baryshkov
0 siblings, 0 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 3:01 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Thu, Feb 06, 2025 at 07:14:27PM +0100, Luca Ceresoli wrote:
> In preparation for allowing bridges to be added to and removed from a DRM
> card without destroying the whole card, add a new DRM bridge function
> called on addition and removal of bridges.
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> Changed in v6:
> - rebased fixing conflicts
>
> Changed in v5:
> - fixed kerneldoc errors
>
> This patch was added in v4.
> ---
> drivers/gpu/drm/drm_bridge.c | 12 ++++++++++++
> include/drm/drm_bridge.h | 23 +++++++++++++++++++++++
> 2 files changed, 35 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index b0834b8644284e5f7751cec81724af849b4180e7..1955a231378050abf1071d74a145831b425c47c5 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -207,12 +207,18 @@ LIST_HEAD(bridge_list);
> */
> void drm_bridge_add(struct drm_bridge *bridge)
> {
> + struct drm_bridge *br, *tmp;
> +
> mutex_init(&bridge->hpd_mutex);
>
> if (bridge->ops & DRM_BRIDGE_OP_HDMI)
> bridge->ycbcr_420_allowed = !!(bridge->supported_formats &
> BIT(HDMI_COLORSPACE_YUV420));
>
> + list_for_each_entry_safe(br, tmp, &bridge_list, list)
> + if (br->funcs->bridge_event_notify)
> + br->funcs->bridge_event_notify(br, DRM_EVENT_BRIDGE_ADD, bridge);
> +
> mutex_lock(&bridge_lock);
> list_add_tail(&bridge->list, &bridge_list);
> mutex_unlock(&bridge_lock);
> @@ -249,10 +255,16 @@ EXPORT_SYMBOL(devm_drm_bridge_add);
> */
> void drm_bridge_remove(struct drm_bridge *bridge)
> {
> + struct drm_bridge *br, *tmp;
> +
> mutex_lock(&bridge_lock);
> list_del_init(&bridge->list);
> mutex_unlock(&bridge_lock);
>
> + list_for_each_entry_safe(br, tmp, &bridge_list, list)
> + if (br->funcs->bridge_event_notify)
> + br->funcs->bridge_event_notify(br, DRM_EVENT_BRIDGE_REMOVE, bridge);
> +
I think the order should be different: notify about the added bridge
after adding to the list, notify about bridge removal before removing it
from the list.
> mutex_destroy(&bridge->hpd_mutex);
> }
> EXPORT_SYMBOL(drm_bridge_remove);
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index 1561347c4991dac6022319774510f9560c9283c3..ad7ba444a13e5ecf16f996de3742e4ac67dc21f1 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -56,6 +56,11 @@ enum drm_bridge_attach_flags {
> DRM_BRIDGE_ATTACH_NO_CONNECTOR = BIT(0),
> };
>
> +enum drm_bridge_event_type {
> + DRM_EVENT_BRIDGE_ADD,
> + DRM_EVENT_BRIDGE_REMOVE,
> +};
> +
> /**
> * struct drm_bridge_funcs - drm_bridge control functions
> */
> @@ -729,6 +734,24 @@ struct drm_bridge_funcs {
> struct drm_bridge *bridge,
> bool enable, int direction);
>
> + /**
> + * @bridge_event_notify:
> + *
> + * Notify that another bridge is being added or removed.
> + *
> + * This callback is optional. Bridges implementing it must always
> + * check whether the event refers to a bridge they actually need to
> + * interact with.
> + *
> + * @bridge: bridge being notified
> + * @event: event happened (add/remove bridge)
> + * @event_bridge: the bridge mentioned by the event (i.e. the
> + * bridge being added or removed)
> + */
> + void (*bridge_event_notify)(struct drm_bridge *bridge,
> + enum drm_bridge_event_type event,
> + struct drm_bridge *event_bridge);
> +
This creates a small issue. It requires drivers to have a bridge, even
if they don't need one - e.g. the drm_encoder doesn't get notifications
about the added bridges.
I'm not sure if that can be solved in an efficient way.
> /**
> * @debugfs_init:
> *
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 13/26] drm/encoder: add drm_encoder_cleanup_from()
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (11 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 12/26] drm/bridge: allow bridges to be informed about added and removed bridges Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 3:03 ` Dmitry Baryshkov
2025-02-06 18:14 ` [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges Luca Ceresoli
` (13 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
Supporting hardware whose final part of the DRM pipeline can be physically
removed requires the ability to detach all bridges from a given point to
the end of the pipeline.
Introduce a variant of drm_encoder_cleanup() for this.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
Changes in v6: none
Changes in v5: none
Changes in v4: none
Changes in v3: none
Changed in v2:
- fix a typo in a comment
---
drivers/gpu/drm/drm_encoder.c | 21 +++++++++++++++++++++
include/drm/drm_encoder.h | 1 +
2 files changed, 22 insertions(+)
diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
index 8f2bc6a28482229fd0b030a1958f87753ad7885f..472dfbefe2960924a4e83bec425af8c7ef5f5265 100644
--- a/drivers/gpu/drm/drm_encoder.c
+++ b/drivers/gpu/drm/drm_encoder.c
@@ -207,6 +207,27 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
}
EXPORT_SYMBOL(drm_encoder_cleanup);
+/**
+ * drm_encoder_cleanup_from - remove a given bridge and all the following
+ * @encoder: encoder whole list of bridges shall be pruned
+ * @bridge: first bridge to remove
+ *
+ * Removes from an encoder all the bridges starting with a given bridge
+ * and until the end of the chain.
+ *
+ * This should not be used in "normal" DRM pipelines. It is only useful for
+ * devices whose final part of the DRM chain can be physically removed and
+ * later reconnected (possibly with different hardware).
+ */
+void drm_encoder_cleanup_from(struct drm_encoder *encoder, struct drm_bridge *bridge)
+{
+ struct drm_bridge *next;
+
+ list_for_each_entry_safe_from(bridge, next, &encoder->bridge_chain, chain_node)
+ drm_bridge_detach(bridge);
+}
+EXPORT_SYMBOL(drm_encoder_cleanup_from);
+
static void drmm_encoder_alloc_release(struct drm_device *dev, void *ptr)
{
struct drm_encoder *encoder = ptr;
diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h
index 977a9381c8ba943b4d3e021635ea14856df8a17d..bafcabb242674880a97dfb62a50d93cc4d80c1d4 100644
--- a/include/drm/drm_encoder.h
+++ b/include/drm/drm_encoder.h
@@ -320,6 +320,7 @@ static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev,
}
void drm_encoder_cleanup(struct drm_encoder *encoder);
+void drm_encoder_cleanup_from(struct drm_encoder *encoder, struct drm_bridge *bridge);
/**
* drm_for_each_encoder_mask - iterate over encoders specified by bitmask
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 13/26] drm/encoder: add drm_encoder_cleanup_from()
2025-02-06 18:14 ` [PATCH v6 13/26] drm/encoder: add drm_encoder_cleanup_from() Luca Ceresoli
@ 2025-02-07 3:03 ` Dmitry Baryshkov
2025-02-07 8:53 ` Luca Ceresoli
0 siblings, 1 reply; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 3:03 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Thu, Feb 06, 2025 at 07:14:28PM +0100, Luca Ceresoli wrote:
> Supporting hardware whose final part of the DRM pipeline can be physically
> removed requires the ability to detach all bridges from a given point to
> the end of the pipeline.
>
> Introduce a variant of drm_encoder_cleanup() for this.
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> Changes in v6: none
> Changes in v5: none
> Changes in v4: none
> Changes in v3: none
>
> Changed in v2:
> - fix a typo in a comment
> ---
> drivers/gpu/drm/drm_encoder.c | 21 +++++++++++++++++++++
> include/drm/drm_encoder.h | 1 +
> 2 files changed, 22 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
> index 8f2bc6a28482229fd0b030a1958f87753ad7885f..472dfbefe2960924a4e83bec425af8c7ef5f5265 100644
> --- a/drivers/gpu/drm/drm_encoder.c
> +++ b/drivers/gpu/drm/drm_encoder.c
> @@ -207,6 +207,27 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
> }
> EXPORT_SYMBOL(drm_encoder_cleanup);
>
> +/**
> + * drm_encoder_cleanup_from - remove a given bridge and all the following
> + * @encoder: encoder whole list of bridges shall be pruned
> + * @bridge: first bridge to remove
> + *
> + * Removes from an encoder all the bridges starting with a given bridge
> + * and until the end of the chain.
> + *
> + * This should not be used in "normal" DRM pipelines. It is only useful for
> + * devices whose final part of the DRM chain can be physically removed and
> + * later reconnected (possibly with different hardware).
> + */
> +void drm_encoder_cleanup_from(struct drm_encoder *encoder, struct drm_bridge *bridge)
> +{
> + struct drm_bridge *next;
> +
> + list_for_each_entry_safe_from(bridge, next, &encoder->bridge_chain, chain_node)
> + drm_bridge_detach(bridge);
> +}
> +EXPORT_SYMBOL(drm_encoder_cleanup_from);
Shouldn't drm_encoder_cleanup() also use this function?
> +
> static void drmm_encoder_alloc_release(struct drm_device *dev, void *ptr)
> {
> struct drm_encoder *encoder = ptr;
> diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h
> index 977a9381c8ba943b4d3e021635ea14856df8a17d..bafcabb242674880a97dfb62a50d93cc4d80c1d4 100644
> --- a/include/drm/drm_encoder.h
> +++ b/include/drm/drm_encoder.h
> @@ -320,6 +320,7 @@ static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev,
> }
>
> void drm_encoder_cleanup(struct drm_encoder *encoder);
> +void drm_encoder_cleanup_from(struct drm_encoder *encoder, struct drm_bridge *bridge);
>
> /**
> * drm_for_each_encoder_mask - iterate over encoders specified by bitmask
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 13/26] drm/encoder: add drm_encoder_cleanup_from()
2025-02-07 3:03 ` Dmitry Baryshkov
@ 2025-02-07 8:53 ` Luca Ceresoli
0 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-07 8:53 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Fri, 7 Feb 2025 05:03:13 +0200
Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> On Thu, Feb 06, 2025 at 07:14:28PM +0100, Luca Ceresoli wrote:
> > Supporting hardware whose final part of the DRM pipeline can be physically
> > removed requires the ability to detach all bridges from a given point to
> > the end of the pipeline.
> >
> > Introduce a variant of drm_encoder_cleanup() for this.
> >
> > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> >
> > ---
> >
> > Changes in v6: none
> > Changes in v5: none
> > Changes in v4: none
> > Changes in v3: none
> >
> > Changed in v2:
> > - fix a typo in a comment
> > ---
> > drivers/gpu/drm/drm_encoder.c | 21 +++++++++++++++++++++
> > include/drm/drm_encoder.h | 1 +
> > 2 files changed, 22 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
> > index 8f2bc6a28482229fd0b030a1958f87753ad7885f..472dfbefe2960924a4e83bec425af8c7ef5f5265 100644
> > --- a/drivers/gpu/drm/drm_encoder.c
> > +++ b/drivers/gpu/drm/drm_encoder.c
> > @@ -207,6 +207,27 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
> > }
> > EXPORT_SYMBOL(drm_encoder_cleanup);
> >
> > +/**
> > + * drm_encoder_cleanup_from - remove a given bridge and all the following
> > + * @encoder: encoder whole list of bridges shall be pruned
> > + * @bridge: first bridge to remove
> > + *
> > + * Removes from an encoder all the bridges starting with a given bridge
> > + * and until the end of the chain.
> > + *
> > + * This should not be used in "normal" DRM pipelines. It is only useful for
> > + * devices whose final part of the DRM chain can be physically removed and
> > + * later reconnected (possibly with different hardware).
> > + */
> > +void drm_encoder_cleanup_from(struct drm_encoder *encoder, struct drm_bridge *bridge)
> > +{
> > + struct drm_bridge *next;
> > +
> > + list_for_each_entry_safe_from(bridge, next, &encoder->bridge_chain, chain_node)
> > + drm_bridge_detach(bridge);
> > +}
> > +EXPORT_SYMBOL(drm_encoder_cleanup_from);
>
> Shouldn't drm_encoder_cleanup() also use this function?
Sure. I'll do it in v7.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (12 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 13/26] drm/encoder: add drm_encoder_cleanup_from() Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 11:47 ` Maxime Ripard
2025-02-06 18:14 ` [PATCH v6 15/26] drm/bridge: devm_drm_of_get_bridge and drmm_of_get_bridge: automatically put the bridge Luca Ceresoli
` (12 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
DRM bridges are currently considered as a fixed element of a DRM card, and
thus their lifetime is assumed to extend for as long as the card
exists. New use cases, such as hot-pluggable hardware with video bridges,
require DRM bridges to be added and removed to a DRM card without tearing
the card down. This is possible for connectors already (used by DP MST), so
add this possibility to DRM bridges as well.
Implementation is based on drm_connector_init() as far as it makes sense,
and differs when it doesn't. A difference is that bridges are not exposed
to userspace, hence struct drm_bridge does not embed a struct
drm_mode_object which would provide the refcount. Instead we add to struct
drm_bridge a refcount field (we don't need other struct drm_mode_object
fields here) and instead of using the drm_mode_object_*() functions we
reimplement from those functions the few lines that drm_bridge needs for
refcounting.
Also add a new devm_drm_bridge_alloc() macro to allocate a new refcounted
bridge.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
Changes in v6:
- use drm_warn, not WARN_ON (Jani Nikula)
- Add devm_drm_bridge_alloc() to replace drm_bridge_init() (similar to
drmm_encoder_alloc)
- Remove .destroy func: deallocation is done via the struct offset
computed by the devm_drm_bridge_alloc() macro
- use fixed free callback, as the same callback is used in all cases
anyway (remove free_cb, add bool is_refcounted)
- add drm_bridge_get/put() to drm_bridge_attach/detach() (add the bridge
to a list)
- make some DRM_DEBUG() strings more informative
This patch was added in v5.
---
drivers/gpu/drm/drm_bridge.c | 76 ++++++++++++++++++++++++++--
include/drm/drm_bridge.h | 117 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 189 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 1955a231378050abf1071d74a145831b425c47c5..f694b32ca59cb91c32846bc00b43360df41cc1ad 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -200,6 +200,57 @@
DEFINE_MUTEX(bridge_lock);
LIST_HEAD(bridge_list);
+/* Internal function (for refcounted bridges) */
+void __drm_bridge_free(struct kref *kref)
+{
+ struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount);
+ void *container = ((void *)bridge) - bridge->container_offset;
+
+ DRM_DEBUG("bridge=%p, container=%p FREE\n", bridge, container);
+
+ kfree(container);
+}
+EXPORT_SYMBOL(__drm_bridge_free);
+
+static void drm_bridge_put_void(void *data)
+{
+ struct drm_bridge *bridge = (struct drm_bridge *)data;
+
+ drm_bridge_put(bridge);
+}
+
+void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
+ const struct drm_bridge_funcs *funcs)
+{
+ void *container;
+ struct drm_bridge *bridge;
+ int err;
+
+ if (!funcs) {
+ dev_warn(dev, "Missing funcs pointer\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ container = kzalloc(size, GFP_KERNEL);
+ if (!container)
+ return ERR_PTR(-ENOMEM);
+
+ bridge = container + offset;
+ bridge->container_offset = offset;
+ bridge->funcs = funcs;
+ kref_init(&bridge->refcount);
+ bridge->is_refcounted = 1;
+
+ err = devm_add_action_or_reset(dev, drm_bridge_put_void, bridge);
+ if (err)
+ return ERR_PTR(err);
+
+ DRM_DEBUG("bridge=%p, container=%p, funcs=%ps ALLOC\n", bridge, container, funcs);
+
+ return container;
+}
+EXPORT_SYMBOL(__devm_drm_bridge_alloc);
+
/**
* drm_bridge_add - add the given bridge to the global bridge list
*
@@ -209,6 +260,10 @@ void drm_bridge_add(struct drm_bridge *bridge)
{
struct drm_bridge *br, *tmp;
+ DRM_DEBUG("bridge=%p ADD\n", bridge);
+
+ drm_bridge_get(bridge);
+
mutex_init(&bridge->hpd_mutex);
if (bridge->ops & DRM_BRIDGE_OP_HDMI)
@@ -257,6 +312,8 @@ void drm_bridge_remove(struct drm_bridge *bridge)
{
struct drm_bridge *br, *tmp;
+ DRM_DEBUG("bridge=%p REMOVE\n", bridge);
+
mutex_lock(&bridge_lock);
list_del_init(&bridge->list);
mutex_unlock(&bridge_lock);
@@ -266,6 +323,8 @@ void drm_bridge_remove(struct drm_bridge *bridge)
br->funcs->bridge_event_notify(br, DRM_EVENT_BRIDGE_REMOVE, bridge);
mutex_destroy(&bridge->hpd_mutex);
+
+ drm_bridge_put(bridge);
}
EXPORT_SYMBOL(drm_bridge_remove);
@@ -326,11 +385,17 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
if (!encoder || !bridge)
return -EINVAL;
- if (previous && (!previous->dev || previous->encoder != encoder))
- return -EINVAL;
+ drm_bridge_get(bridge);
- if (bridge->dev)
- return -EBUSY;
+ if (previous && (!previous->dev || previous->encoder != encoder)) {
+ ret = -EINVAL;
+ goto err_put_bridge;
+ }
+
+ if (bridge->dev) {
+ ret = -EBUSY;
+ goto err_put_bridge;
+ }
bridge->dev = encoder->dev;
bridge->encoder = encoder;
@@ -379,6 +444,8 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
"failed to attach bridge %pOF to encoder %s\n",
bridge->of_node, encoder->name);
+err_put_bridge:
+ drm_bridge_put(bridge);
return ret;
}
EXPORT_SYMBOL(drm_bridge_attach);
@@ -399,6 +466,7 @@ void drm_bridge_detach(struct drm_bridge *bridge)
list_del(&bridge->chain_node);
bridge->dev = NULL;
+ drm_bridge_put(bridge);
}
/**
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index ad7ba444a13e5ecf16f996de3742e4ac67dc21f1..43cef0f6ccd36034f64ad2babfebea62db1d9e43 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -31,6 +31,7 @@
#include <drm/drm_encoder.h>
#include <drm/drm_mode_object.h>
#include <drm/drm_modes.h>
+#include <drm/drm_print.h>
struct device_node;
@@ -863,6 +864,22 @@ struct drm_bridge {
const struct drm_bridge_timings *timings;
/** @funcs: control functions */
const struct drm_bridge_funcs *funcs;
+
+ /**
+ * @container_offset: Offset of this struct within the container
+ * struct embedding it. Used for refcounted bridges to free the
+ * embeddeing struct when the refcount drops to zero. Unused on
+ * legacy bridges.
+ */
+ size_t container_offset;
+ /**
+ * @refcount: reference count for bridges with dynamic lifetime
+ * (see drm_bridge_init)
+ */
+ struct kref refcount;
+ /** @is_refcounted: this bridge has dynamic lifetime management */
+ bool is_refcounted;
+
/** @driver_private: pointer to the bridge driver's internal context */
void *driver_private;
/** @ops: bitmask of operations supported by the bridge */
@@ -964,6 +981,106 @@ drm_priv_to_bridge(struct drm_private_obj *priv)
return container_of(priv, struct drm_bridge, base);
}
+static inline bool drm_bridge_is_refcounted(struct drm_bridge *bridge)
+{
+ return bridge->is_refcounted;
+}
+
+void __drm_bridge_free(struct kref *kref);
+
+/**
+ * drm_bridge_get - Acquire a bridge reference
+ * @bridge: DRM bridge
+ *
+ * This function increments the bridge's refcount.
+ *
+ * It does nothing on non-refcounted bridges. See drm_bridge_init().
+ */
+static inline void drm_bridge_get(struct drm_bridge *bridge)
+{
+ if (!drm_bridge_is_refcounted(bridge))
+ return;
+
+ DRM_DEBUG("bridge=%p GET\n", bridge);
+
+ kref_get(&bridge->refcount);
+}
+
+/**
+ * drm_bridge_put - Release a bridge reference
+ * @bridge: DRM bridge
+ *
+ * This function decrements the bridge's reference count and frees the
+ * object if the reference count drops to zero.
+ *
+ * It does nothing on non-refcounted bridges. See drm_bridge_init().
+ *
+ * See also drm_bridge_put_and_clear() which is more handy in many cases.
+ */
+static inline void drm_bridge_put(struct drm_bridge *bridge)
+{
+ if (!drm_bridge_is_refcounted(bridge))
+ return;
+
+ DRM_DEBUG("bridge=%p PUT\n", bridge);
+
+ kref_put(&bridge->refcount, __drm_bridge_free);
+}
+
+/**
+ * drm_bridge_put_and_clear - Given a bridge pointer, clear the pointer
+ * then put the bridge
+ *
+ * @bridge_pp: pointer to pointer to a struct drm_bridge
+ *
+ * Helper to put a DRM bridge (whose pointer is passed), but only after
+ * setting its pointer to NULL. Useful for drivers having struct drm_bridge
+ * pointers they need to dispose of, without leaving a use-after-free
+ * window where the pointed bridge might have been freed while still
+ * holding a pointer to it.
+ *
+ * For example a driver having this private struct::
+ *
+ * struct my_bridge {
+ * struct drm_bridge *remote_bridge;
+ * ...
+ * };
+ *
+ * can dispose of remote_bridge using::
+ *
+ * drm_bridge_put_and_clear(&my_bridge->remote_bridge);
+ */
+static inline void drm_bridge_put_and_clear(struct drm_bridge **bridge_pp)
+{
+ struct drm_bridge *bridge = *bridge_pp;
+
+ *bridge_pp = NULL;
+ drm_bridge_put(bridge);
+}
+
+void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
+ const struct drm_bridge_funcs *funcs);
+
+/**
+ * devm_drm_bridge_alloc - Allocate and initialize an refcounted bridge
+ * @dev: struct device of the bridge device
+ * @type: the type of the struct which contains struct &drm_bridge
+ * @member: the name of the &drm_bridge within @type
+ * @funcs: callbacks for this bridge
+ *
+ * The bridge will have a dynamic lifetime (aka a refcounted bridge).
+ *
+ * The returned refcount is initialized to 1. This reference will be
+ * automatically dropped via devm (by calling drm_bridge_put()) when the
+ * device is removed.
+ *
+ * Returns:
+ * Pointer to new bridge, or ERR_PTR on failure.
+ */
+#define devm_drm_bridge_alloc(dev, type, member, funcs) \
+ ((type *)__devm_drm_bridge_alloc(dev, sizeof(type), \
+ offsetof(type, member), funcs))
+
void drm_bridge_add(struct drm_bridge *bridge);
int devm_drm_bridge_add(struct device *dev, struct drm_bridge *bridge);
void drm_bridge_remove(struct drm_bridge *bridge);
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-06 18:14 ` [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges Luca Ceresoli
@ 2025-02-07 11:47 ` Maxime Ripard
2025-02-07 19:54 ` Dmitry Baryshkov
` (2 more replies)
0 siblings, 3 replies; 81+ messages in thread
From: Maxime Ripard @ 2025-02-07 11:47 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Inki Dae, linux-arm-kernel,
Neil Armstrong, Boris Brezillon, linux-kernel, Paul Kocialkowski,
Pengutronix Kernel Team, Dmitry Baryshkov, Shawn Guo
[-- Attachment #1: Type: text/plain, Size: 8746 bytes --]
Hi,
On Thu, Feb 06, 2025 at 07:14:29PM +0100, Luca Ceresoli wrote:
> DRM bridges are currently considered as a fixed element of a DRM card, and
> thus their lifetime is assumed to extend for as long as the card
> exists. New use cases, such as hot-pluggable hardware with video bridges,
> require DRM bridges to be added and removed to a DRM card without tearing
> the card down. This is possible for connectors already (used by DP MST), so
> add this possibility to DRM bridges as well.
>
> Implementation is based on drm_connector_init() as far as it makes sense,
> and differs when it doesn't. A difference is that bridges are not exposed
> to userspace, hence struct drm_bridge does not embed a struct
> drm_mode_object which would provide the refcount. Instead we add to struct
> drm_bridge a refcount field (we don't need other struct drm_mode_object
> fields here) and instead of using the drm_mode_object_*() functions we
> reimplement from those functions the few lines that drm_bridge needs for
> refcounting.
>
> Also add a new devm_drm_bridge_alloc() macro to allocate a new refcounted
> bridge.
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
So, a couple of general comments:
- I've said it a couple of times already, but I really think you're
making it harder than necessary for you here. This (and only this!)
should be the very first series you should be pushing. The rest can
only ever work if that work goes through, and it's already hard enough
as it is. So, split that patch into a series of its own, get that
merged, and then we will be able to deal with panels conversion and
whatever. That's even more true with panels since there's ongoing work
that will make it easier for you too. So the best thing here is
probably to wait.
- This patch really needs to be split into several patches, something
along the lines of:
+ Creating devm_drm_bridge_alloc()
+ Adding refcounting
+ Taking the references in all the needed places
+ Converting a bunch of drivers
> Changes in v6:
> - use drm_warn, not WARN_ON (Jani Nikula)
> - Add devm_drm_bridge_alloc() to replace drm_bridge_init() (similar to
> drmm_encoder_alloc)
> - Remove .destroy func: deallocation is done via the struct offset
> computed by the devm_drm_bridge_alloc() macro
> - use fixed free callback, as the same callback is used in all cases
> anyway (remove free_cb, add bool is_refcounted)
> - add drm_bridge_get/put() to drm_bridge_attach/detach() (add the bridge
> to a list)
> - make some DRM_DEBUG() strings more informative
>
> This patch was added in v5.
> ---
> drivers/gpu/drm/drm_bridge.c | 76 ++++++++++++++++++++++++++--
> include/drm/drm_bridge.h | 117 +++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 189 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index 1955a231378050abf1071d74a145831b425c47c5..f694b32ca59cb91c32846bc00b43360df41cc1ad 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -200,6 +200,57 @@
> DEFINE_MUTEX(bridge_lock);
> LIST_HEAD(bridge_list);
>
> +/* Internal function (for refcounted bridges) */
> +void __drm_bridge_free(struct kref *kref)
> +{
> + struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount);
> + void *container = ((void *)bridge) - bridge->container_offset;
> +
> + DRM_DEBUG("bridge=%p, container=%p FREE\n", bridge, container);
Pointers are not really useful to track here, since they are obfuscated
most of the time. Using the bridge device name would probably be better
(or removing the SHOUTING DEBUG entirely :))
> + kfree(container);
> +}
> +EXPORT_SYMBOL(__drm_bridge_free);
> +
> +static void drm_bridge_put_void(void *data)
> +{
> + struct drm_bridge *bridge = (struct drm_bridge *)data;
> +
> + drm_bridge_put(bridge);
> +}
> +
> +void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
> + const struct drm_bridge_funcs *funcs)
> +{
> + void *container;
> + struct drm_bridge *bridge;
> + int err;
> +
> + if (!funcs) {
> + dev_warn(dev, "Missing funcs pointer\n");
> + return ERR_PTR(-EINVAL);
> + }
> +
> + container = kzalloc(size, GFP_KERNEL);
> + if (!container)
> + return ERR_PTR(-ENOMEM);
> +
> + bridge = container + offset;
> + bridge->container_offset = offset;
> + bridge->funcs = funcs;
> + kref_init(&bridge->refcount);
> + bridge->is_refcounted = 1;
> +
> + err = devm_add_action_or_reset(dev, drm_bridge_put_void, bridge);
> + if (err)
> + return ERR_PTR(err);
> +
> + DRM_DEBUG("bridge=%p, container=%p, funcs=%ps ALLOC\n", bridge, container, funcs);
> +
> + return container;
> +}
> +EXPORT_SYMBOL(__devm_drm_bridge_alloc);
> +
> /**
> * drm_bridge_add - add the given bridge to the global bridge list
> *
> @@ -209,6 +260,10 @@ void drm_bridge_add(struct drm_bridge *bridge)
> {
> struct drm_bridge *br, *tmp;
>
> + DRM_DEBUG("bridge=%p ADD\n", bridge);
> +
> + drm_bridge_get(bridge);
> +
> mutex_init(&bridge->hpd_mutex);
>
> if (bridge->ops & DRM_BRIDGE_OP_HDMI)
> @@ -257,6 +312,8 @@ void drm_bridge_remove(struct drm_bridge *bridge)
> {
> struct drm_bridge *br, *tmp;
>
> + DRM_DEBUG("bridge=%p REMOVE\n", bridge);
> +
> mutex_lock(&bridge_lock);
> list_del_init(&bridge->list);
> mutex_unlock(&bridge_lock);
> @@ -266,6 +323,8 @@ void drm_bridge_remove(struct drm_bridge *bridge)
> br->funcs->bridge_event_notify(br, DRM_EVENT_BRIDGE_REMOVE, bridge);
>
> mutex_destroy(&bridge->hpd_mutex);
> +
> + drm_bridge_put(bridge);
> }
> EXPORT_SYMBOL(drm_bridge_remove);
>
> @@ -326,11 +385,17 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
> if (!encoder || !bridge)
> return -EINVAL;
>
> - if (previous && (!previous->dev || previous->encoder != encoder))
> - return -EINVAL;
> + drm_bridge_get(bridge);
>
> - if (bridge->dev)
> - return -EBUSY;
> + if (previous && (!previous->dev || previous->encoder != encoder)) {
> + ret = -EINVAL;
> + goto err_put_bridge;
> + }
> +
> + if (bridge->dev) {
> + ret = -EBUSY;
> + goto err_put_bridge;
> + }
>
> bridge->dev = encoder->dev;
> bridge->encoder = encoder;
> @@ -379,6 +444,8 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
> "failed to attach bridge %pOF to encoder %s\n",
> bridge->of_node, encoder->name);
>
> +err_put_bridge:
> + drm_bridge_put(bridge);
> return ret;
> }
> EXPORT_SYMBOL(drm_bridge_attach);
> @@ -399,6 +466,7 @@ void drm_bridge_detach(struct drm_bridge *bridge)
>
> list_del(&bridge->chain_node);
> bridge->dev = NULL;
> + drm_bridge_put(bridge);
> }
>
> /**
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index ad7ba444a13e5ecf16f996de3742e4ac67dc21f1..43cef0f6ccd36034f64ad2babfebea62db1d9e43 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -31,6 +31,7 @@
> #include <drm/drm_encoder.h>
> #include <drm/drm_mode_object.h>
> #include <drm/drm_modes.h>
> +#include <drm/drm_print.h>
>
> struct device_node;
>
> @@ -863,6 +864,22 @@ struct drm_bridge {
> const struct drm_bridge_timings *timings;
> /** @funcs: control functions */
> const struct drm_bridge_funcs *funcs;
> +
> + /**
> + * @container_offset: Offset of this struct within the container
> + * struct embedding it. Used for refcounted bridges to free the
> + * embeddeing struct when the refcount drops to zero. Unused on
> + * legacy bridges.
> + */
> + size_t container_offset;
This shouldn't be in there. You can create an intermediate structure and
store both pointers for the action to consume.
> + /**
> + * @refcount: reference count for bridges with dynamic lifetime
> + * (see drm_bridge_init)
> + */
> + struct kref refcount;
> + /** @is_refcounted: this bridge has dynamic lifetime management */
> + bool is_refcounted;
> +
I'm not sure we want to treat both paths separately too. It'll require
to update most of/all the drivers, but it's not too hard with
coccinelle:
virtual patch
@@
identifier f;
identifier b, c, d;
expression bf;
type T;
@@
f(...)
{
...
- T *c;
+ T *c;
...
- c = devm_kzalloc(d, ...);
+ c = devm_drm_bridge_alloc(d, T, b, bf);
...
- c->b.funcs = bf;
...
drm_bridge_add(&c->b);
...
}
We need to add a bit more variations (like kzalloc vs devm_kzalloc,
drm_bridge_add vs devm_drm_bridge_add, etc.), but it should be a good
first approximation
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-07 11:47 ` Maxime Ripard
@ 2025-02-07 19:54 ` Dmitry Baryshkov
2025-02-10 12:31 ` Maxime Ripard
2025-02-10 17:12 ` Luca Ceresoli
2025-02-10 17:12 ` Luca Ceresoli
2025-03-13 11:56 ` Luca Ceresoli
2 siblings, 2 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 19:54 UTC (permalink / raw)
To: Maxime Ripard
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni,
Sam Ravnborg, linux-doc, Catalin Marinas, Paul Kocialkowski,
dri-devel, Claudiu Beznea, Laurent Pinchart, Andrzej Hajda,
David Airlie, Fabio Estevam, Marek Szyprowski, Simona Vetter,
Robert Foss, Jonathan Corbet, Will Deacon, Jernej Skrabec,
Daniel Thompson, Jagan Teki, Jessica Zhang, Luca Ceresoli,
Thomas Zimmermann, Jonas Karlman, Sascha Hauer, Maarten Lankhorst,
Inki Dae, linux-arm-kernel, Neil Armstrong, Boris Brezillon,
linux-kernel, Paul Kocialkowski, Pengutronix Kernel Team,
Shawn Guo
On Fri, Feb 07, 2025 at 12:47:51PM +0100, Maxime Ripard wrote:
> Hi,
>
> On Thu, Feb 06, 2025 at 07:14:29PM +0100, Luca Ceresoli wrote:
> > DRM bridges are currently considered as a fixed element of a DRM card, and
> > thus their lifetime is assumed to extend for as long as the card
> > exists. New use cases, such as hot-pluggable hardware with video bridges,
> > require DRM bridges to be added and removed to a DRM card without tearing
> > the card down. This is possible for connectors already (used by DP MST), so
> > add this possibility to DRM bridges as well.
> >
> > Implementation is based on drm_connector_init() as far as it makes sense,
> > and differs when it doesn't. A difference is that bridges are not exposed
> > to userspace, hence struct drm_bridge does not embed a struct
> > drm_mode_object which would provide the refcount. Instead we add to struct
> > drm_bridge a refcount field (we don't need other struct drm_mode_object
> > fields here) and instead of using the drm_mode_object_*() functions we
> > reimplement from those functions the few lines that drm_bridge needs for
> > refcounting.
> >
> > Also add a new devm_drm_bridge_alloc() macro to allocate a new refcounted
> > bridge.
> >
> > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> So, a couple of general comments:
>
> - I've said it a couple of times already, but I really think you're
> making it harder than necessary for you here. This (and only this!)
> should be the very first series you should be pushing. The rest can
> only ever work if that work goes through, and it's already hard enough
> as it is. So, split that patch into a series of its own, get that
> merged, and then we will be able to deal with panels conversion and
> whatever. That's even more true with panels since there's ongoing work
> that will make it easier for you too. So the best thing here is
> probably to wait.
Luca and I had a quick chat on this during FOSDEM. I really think that
panel (part of the) series can go in first as it fixes a very well known
bug _and_ allows a pretty good cleanup to a whole set of drivers. With
all those panel / bridge wrappers gone we should be able to see a
clearer picture of what individual drivers are doing. In other words,
which memory and which code actually hosts and uses internal
'next_bridge' reference.
> - This patch really needs to be split into several patches, something
> along the lines of:
>
> + Creating devm_drm_bridge_alloc()
> + Adding refcounting
> + Taking the references in all the needed places
> + Converting a bunch of drivers
The last two parts seem troublematic to me, but, I must admit, I didn't
spend so much time reviewing all drm_bridge usage patterns.
>
> > Changes in v6:
> > - use drm_warn, not WARN_ON (Jani Nikula)
> > - Add devm_drm_bridge_alloc() to replace drm_bridge_init() (similar to
> > drmm_encoder_alloc)
> > - Remove .destroy func: deallocation is done via the struct offset
> > computed by the devm_drm_bridge_alloc() macro
> > - use fixed free callback, as the same callback is used in all cases
> > anyway (remove free_cb, add bool is_refcounted)
> > - add drm_bridge_get/put() to drm_bridge_attach/detach() (add the bridge
> > to a list)
> > - make some DRM_DEBUG() strings more informative
> >
> > This patch was added in v5.
> > ---
> > drivers/gpu/drm/drm_bridge.c | 76 ++++++++++++++++++++++++++--
> > include/drm/drm_bridge.h | 117 +++++++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 189 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> > index 1955a231378050abf1071d74a145831b425c47c5..f694b32ca59cb91c32846bc00b43360df41cc1ad 100644
> > --- a/drivers/gpu/drm/drm_bridge.c
> > +++ b/drivers/gpu/drm/drm_bridge.c
> > @@ -200,6 +200,57 @@
> > DEFINE_MUTEX(bridge_lock);
> > LIST_HEAD(bridge_list);
> >
> > +/* Internal function (for refcounted bridges) */
> > +void __drm_bridge_free(struct kref *kref)
> > +{
> > + struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount);
> > + void *container = ((void *)bridge) - bridge->container_offset;
> > +
> > + DRM_DEBUG("bridge=%p, container=%p FREE\n", bridge, container);
>
> Pointers are not really useful to track here, since they are obfuscated
> most of the time. Using the bridge device name would probably be better
> (or removing the SHOUTING DEBUG entirely :))
bridge device name or bridge funcs (I opted for the latter for the
debugfs file)
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-07 19:54 ` Dmitry Baryshkov
@ 2025-02-10 12:31 ` Maxime Ripard
2025-02-10 14:23 ` Dmitry Baryshkov
2025-02-10 17:12 ` Luca Ceresoli
1 sibling, 1 reply; 81+ messages in thread
From: Maxime Ripard @ 2025-02-10 12:31 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni,
Sam Ravnborg, linux-doc, Catalin Marinas, Paul Kocialkowski,
dri-devel, Claudiu Beznea, Laurent Pinchart, Andrzej Hajda,
David Airlie, Fabio Estevam, Marek Szyprowski, Simona Vetter,
Robert Foss, Jonathan Corbet, Will Deacon, Jernej Skrabec,
Daniel Thompson, Jagan Teki, Jessica Zhang, Luca Ceresoli,
Thomas Zimmermann, Jonas Karlman, Sascha Hauer, Maarten Lankhorst,
Inki Dae, linux-arm-kernel, Neil Armstrong, Boris Brezillon,
linux-kernel, Paul Kocialkowski, Pengutronix Kernel Team,
Shawn Guo
[-- Attachment #1: Type: text/plain, Size: 3411 bytes --]
On Fri, Feb 07, 2025 at 09:54:06PM +0200, Dmitry Baryshkov wrote:
> On Fri, Feb 07, 2025 at 12:47:51PM +0100, Maxime Ripard wrote:
> > Hi,
> >
> > On Thu, Feb 06, 2025 at 07:14:29PM +0100, Luca Ceresoli wrote:
> > > DRM bridges are currently considered as a fixed element of a DRM card, and
> > > thus their lifetime is assumed to extend for as long as the card
> > > exists. New use cases, such as hot-pluggable hardware with video bridges,
> > > require DRM bridges to be added and removed to a DRM card without tearing
> > > the card down. This is possible for connectors already (used by DP MST), so
> > > add this possibility to DRM bridges as well.
> > >
> > > Implementation is based on drm_connector_init() as far as it makes sense,
> > > and differs when it doesn't. A difference is that bridges are not exposed
> > > to userspace, hence struct drm_bridge does not embed a struct
> > > drm_mode_object which would provide the refcount. Instead we add to struct
> > > drm_bridge a refcount field (we don't need other struct drm_mode_object
> > > fields here) and instead of using the drm_mode_object_*() functions we
> > > reimplement from those functions the few lines that drm_bridge needs for
> > > refcounting.
> > >
> > > Also add a new devm_drm_bridge_alloc() macro to allocate a new refcounted
> > > bridge.
> > >
> > > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> >
> > So, a couple of general comments:
> >
> > - I've said it a couple of times already, but I really think you're
> > making it harder than necessary for you here. This (and only this!)
> > should be the very first series you should be pushing. The rest can
> > only ever work if that work goes through, and it's already hard enough
> > as it is. So, split that patch into a series of its own, get that
> > merged, and then we will be able to deal with panels conversion and
> > whatever. That's even more true with panels since there's ongoing work
> > that will make it easier for you too. So the best thing here is
> > probably to wait.
>
> Luca and I had a quick chat on this during FOSDEM. I really think that
> panel (part of the) series can go in first as it fixes a very well known
> bug _and_ allows a pretty good cleanup to a whole set of drivers.
I don't necessarily disagree on principle, but if you state that it can
get first, and fixes a known problem (which one?), then it should be a
separate, standalone, series.
Ever-expanding features are bad for both the reviewers and the
contributors, even more so when the discussion happens off-list.
> With all those panel / bridge wrappers gone we should be able to see a
> clearer picture of what individual drivers are doing. In other words,
> which memory and which code actually hosts and uses internal
> 'next_bridge' reference.
>
> > - This patch really needs to be split into several patches, something
> > along the lines of:
> >
> > + Creating devm_drm_bridge_alloc()
> > + Adding refcounting
> > + Taking the references in all the needed places
> > + Converting a bunch of drivers
>
> The last two parts seem troublematic to me, but, I must admit, I didn't
> spend so much time reviewing all drm_bridge usage patterns.
Why? the third one is already done by that patch, the fourth can
relatively easily be done using coccinelle.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-10 12:31 ` Maxime Ripard
@ 2025-02-10 14:23 ` Dmitry Baryshkov
2025-02-10 17:12 ` Luca Ceresoli
2025-02-10 18:17 ` Maxime Ripard
0 siblings, 2 replies; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-10 14:23 UTC (permalink / raw)
To: Maxime Ripard
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni,
Sam Ravnborg, linux-doc, Catalin Marinas, Paul Kocialkowski,
dri-devel, Claudiu Beznea, Laurent Pinchart, Andrzej Hajda,
David Airlie, Fabio Estevam, Marek Szyprowski, Simona Vetter,
Robert Foss, Jonathan Corbet, Will Deacon, Jernej Skrabec,
Daniel Thompson, Jagan Teki, Jessica Zhang, Luca Ceresoli,
Thomas Zimmermann, Jonas Karlman, Sascha Hauer, Maarten Lankhorst,
Inki Dae, linux-arm-kernel, Neil Armstrong, Boris Brezillon,
linux-kernel, Paul Kocialkowski, Pengutronix Kernel Team,
Shawn Guo
On Mon, 10 Feb 2025 at 14:31, Maxime Ripard <mripard@kernel.org> wrote:
>
> On Fri, Feb 07, 2025 at 09:54:06PM +0200, Dmitry Baryshkov wrote:
> > On Fri, Feb 07, 2025 at 12:47:51PM +0100, Maxime Ripard wrote:
> > > Hi,
> > >
> > > On Thu, Feb 06, 2025 at 07:14:29PM +0100, Luca Ceresoli wrote:
> > > > DRM bridges are currently considered as a fixed element of a DRM card, and
> > > > thus their lifetime is assumed to extend for as long as the card
> > > > exists. New use cases, such as hot-pluggable hardware with video bridges,
> > > > require DRM bridges to be added and removed to a DRM card without tearing
> > > > the card down. This is possible for connectors already (used by DP MST), so
> > > > add this possibility to DRM bridges as well.
> > > >
> > > > Implementation is based on drm_connector_init() as far as it makes sense,
> > > > and differs when it doesn't. A difference is that bridges are not exposed
> > > > to userspace, hence struct drm_bridge does not embed a struct
> > > > drm_mode_object which would provide the refcount. Instead we add to struct
> > > > drm_bridge a refcount field (we don't need other struct drm_mode_object
> > > > fields here) and instead of using the drm_mode_object_*() functions we
> > > > reimplement from those functions the few lines that drm_bridge needs for
> > > > refcounting.
> > > >
> > > > Also add a new devm_drm_bridge_alloc() macro to allocate a new refcounted
> > > > bridge.
> > > >
> > > > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> > >
> > > So, a couple of general comments:
> > >
> > > - I've said it a couple of times already, but I really think you're
> > > making it harder than necessary for you here. This (and only this!)
> > > should be the very first series you should be pushing. The rest can
> > > only ever work if that work goes through, and it's already hard enough
> > > as it is. So, split that patch into a series of its own, get that
> > > merged, and then we will be able to deal with panels conversion and
> > > whatever. That's even more true with panels since there's ongoing work
> > > that will make it easier for you too. So the best thing here is
> > > probably to wait.
> >
> > Luca and I had a quick chat on this during FOSDEM. I really think that
> > panel (part of the) series can go in first as it fixes a very well known
> > bug _and_ allows a pretty good cleanup to a whole set of drivers.
>
> I don't necessarily disagree on principle, but if you state that it can
> get first, and fixes a known problem (which one?), then it should be a
> separate, standalone, series.
A problem of panel bridges having the wrong lifetime because of devm_
attachment to a wrong device and so either being kept for too long or
being destroyed too early.
>
> Ever-expanding features are bad for both the reviewers and the
> contributors, even more so when the discussion happens off-list.
>
> > With all those panel / bridge wrappers gone we should be able to see a
> > clearer picture of what individual drivers are doing. In other words,
> > which memory and which code actually hosts and uses internal
> > 'next_bridge' reference.
> >
> > > - This patch really needs to be split into several patches, something
> > > along the lines of:
> > >
> > > + Creating devm_drm_bridge_alloc()
> > > + Adding refcounting
> > > + Taking the references in all the needed places
> > > + Converting a bunch of drivers
> >
> > The last two parts seem troublematic to me, but, I must admit, I didn't
> > spend so much time reviewing all drm_bridge usage patterns.
>
> Why? the third one is already done by that patch, the fourth can
> relatively easily be done using coccinelle.
I have doubts about cocci. It doesn't have a way to know, what is the
lifetime of the references to the reference-holding memory. Maybe I'm
missing a point there.
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-10 14:23 ` Dmitry Baryshkov
@ 2025-02-10 17:12 ` Luca Ceresoli
2025-02-10 18:16 ` Maxime Ripard
2025-02-10 18:17 ` Maxime Ripard
1 sibling, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-10 17:12 UTC (permalink / raw)
To: Dmitry Baryshkov, Maxime Ripard
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Inki Dae, linux-arm-kernel,
Neil Armstrong, Boris Brezillon, linux-kernel, Paul Kocialkowski,
Pengutronix Kernel Team, Shawn Guo
Hello Maxime, Dmitry,
On Mon, 10 Feb 2025 16:23:44 +0200
Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> On Mon, 10 Feb 2025 at 14:31, Maxime Ripard <mripard@kernel.org>
> wrote:
> >
> > On Fri, Feb 07, 2025 at 09:54:06PM +0200, Dmitry Baryshkov wrote:
> > > On Fri, Feb 07, 2025 at 12:47:51PM +0100, Maxime Ripard wrote:
> > > > Hi,
> > > >
> > > > On Thu, Feb 06, 2025 at 07:14:29PM +0100, Luca Ceresoli wrote:
> > > > > DRM bridges are currently considered as a fixed element of a
> > > > > DRM card, and thus their lifetime is assumed to extend for as
> > > > > long as the card exists. New use cases, such as hot-pluggable
> > > > > hardware with video bridges, require DRM bridges to be added
> > > > > and removed to a DRM card without tearing the card down. This
> > > > > is possible for connectors already (used by DP MST), so add
> > > > > this possibility to DRM bridges as well.
> > > > >
> > > > > Implementation is based on drm_connector_init() as far as it
> > > > > makes sense, and differs when it doesn't. A difference is
> > > > > that bridges are not exposed to userspace, hence struct
> > > > > drm_bridge does not embed a struct drm_mode_object which
> > > > > would provide the refcount. Instead we add to struct
> > > > > drm_bridge a refcount field (we don't need other struct
> > > > > drm_mode_object fields here) and instead of using the
> > > > > drm_mode_object_*() functions we reimplement from those
> > > > > functions the few lines that drm_bridge needs for refcounting.
> > > > >
> > > > > Also add a new devm_drm_bridge_alloc() macro to allocate a
> > > > > new refcounted bridge.
> > > > >
> > > > > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> > > >
> > > > So, a couple of general comments:
> > > >
> > > > - I've said it a couple of times already, but I really think
> > > > you're making it harder than necessary for you here. This (and
> > > > only this!) should be the very first series you should be
> > > > pushing. The rest can only ever work if that work goes through,
> > > > and it's already hard enough as it is. So, split that patch
> > > > into a series of its own, get that merged, and then we will be
> > > > able to deal with panels conversion and whatever. That's even
> > > > more true with panels since there's ongoing work that will make
> > > > it easier for you too. So the best thing here is probably to
> > > > wait.
The idea you proposed was to handle the issues current panel bridge
code adds to the hotplug work by adding a .destroy callback and some
more devm magic. I explored the idea but even after some clarifications
from you it still didn't appear clearly doable and correct to me. And
even in the case it were perfectly correct and doable, it is based on
adding more complexity and "magic" on top of a topic that is already
hard to understand: panel_bridge lifetime.
So I opted for the other way: rework panel_bridge code so its lifetime
is clear and as one would expect (panel_bridge lifetime == panel
lifetime).
Possibly more work for me, but now it's done and it's in these patches
so why waiting?
This work has made the hotplug work on top of it way cleaner, and you
can also see the good cleanup in the samsung-dsim driver in patch 11.
I'm at work to apply the improvements suggested by Dmitry and send a
new version, and I'm fine with applying more fixes as needed.
And after this "basic" panel_bridge, more cleanups and rationalization
can be done at any moment, in order to make the panel and panel_bridge
even closer, and remove the code duplication.
> > > Luca and I had a quick chat on this during FOSDEM. I really think
> > > that panel (part of the) series can go in first as it fixes a
> > > very well known bug _and_ allows a pretty good cleanup to a whole
> > > set of drivers.
> >
> > I don't necessarily disagree on principle, but if you state that it
> > can get first, and fixes a known problem (which one?), then it
> > should be a separate, standalone, series.
>
> A problem of panel bridges having the wrong lifetime because of devm_
> attachment to a wrong device and so either being kept for too long or
> being destroyed too early.
Exactly, because panel_bridge is usually created in the consumer driver
context, so the devm calls get a struct device pointer to the consumer,
not the panel itself (more in patch 8).
> > Ever-expanding features are bad for both the reviewers and the
> > contributors, even more so when the discussion happens off-list.
> >
> > > With all those panel / bridge wrappers gone we should be able to
> > > see a clearer picture of what individual drivers are doing. In
> > > other words, which memory and which code actually hosts and uses
> > > internal 'next_bridge' reference.
> > >
> > > > - This patch really needs to be split into several patches,
> > > > something along the lines of:
> > > >
> > > > + Creating devm_drm_bridge_alloc()
> > > > + Adding refcounting
> > > > + Taking the references in all the needed places
> > > > + Converting a bunch of drivers
OK, I can split it.
> > > The last two parts seem troublematic to me, but, I must admit, I
> > > didn't spend so much time reviewing all drm_bridge usage
> > > patterns.
> >
> > Why? the third one is already done by that patch, the fourth can
> > relatively easily be done using coccinelle.
>
> I have doubts about cocci. It doesn't have a way to know, what is the
> lifetime of the references to the reference-holding memory. Maybe I'm
> missing a point there.
I haven't looked into that, but I think coccinelle would be helpful in
adding the needed gets, but probably not for the puts, which are
usually more complex to get right.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-10 17:12 ` Luca Ceresoli
@ 2025-02-10 18:16 ` Maxime Ripard
0 siblings, 0 replies; 81+ messages in thread
From: Maxime Ripard @ 2025-02-10 18:16 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Inki Dae, linux-arm-kernel,
Neil Armstrong, Boris Brezillon, linux-kernel, Paul Kocialkowski,
Pengutronix Kernel Team, Dmitry Baryshkov, Shawn Guo
[-- Attachment #1: Type: text/plain, Size: 3948 bytes --]
On Mon, Feb 10, 2025 at 06:12:03PM +0100, Luca Ceresoli wrote:
> Hello Maxime, Dmitry,
>
> On Mon, 10 Feb 2025 16:23:44 +0200
> Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
>
> > On Mon, 10 Feb 2025 at 14:31, Maxime Ripard <mripard@kernel.org>
> > wrote:
> > >
> > > On Fri, Feb 07, 2025 at 09:54:06PM +0200, Dmitry Baryshkov wrote:
> > > > On Fri, Feb 07, 2025 at 12:47:51PM +0100, Maxime Ripard wrote:
> > > > > Hi,
> > > > >
> > > > > On Thu, Feb 06, 2025 at 07:14:29PM +0100, Luca Ceresoli wrote:
> > > > > > DRM bridges are currently considered as a fixed element of a
> > > > > > DRM card, and thus their lifetime is assumed to extend for as
> > > > > > long as the card exists. New use cases, such as hot-pluggable
> > > > > > hardware with video bridges, require DRM bridges to be added
> > > > > > and removed to a DRM card without tearing the card down. This
> > > > > > is possible for connectors already (used by DP MST), so add
> > > > > > this possibility to DRM bridges as well.
> > > > > >
> > > > > > Implementation is based on drm_connector_init() as far as it
> > > > > > makes sense, and differs when it doesn't. A difference is
> > > > > > that bridges are not exposed to userspace, hence struct
> > > > > > drm_bridge does not embed a struct drm_mode_object which
> > > > > > would provide the refcount. Instead we add to struct
> > > > > > drm_bridge a refcount field (we don't need other struct
> > > > > > drm_mode_object fields here) and instead of using the
> > > > > > drm_mode_object_*() functions we reimplement from those
> > > > > > functions the few lines that drm_bridge needs for refcounting.
> > > > > >
> > > > > > Also add a new devm_drm_bridge_alloc() macro to allocate a
> > > > > > new refcounted bridge.
> > > > > >
> > > > > > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> > > > >
> > > > > So, a couple of general comments:
> > > > >
> > > > > - I've said it a couple of times already, but I really think
> > > > > you're making it harder than necessary for you here. This (and
> > > > > only this!) should be the very first series you should be
> > > > > pushing. The rest can only ever work if that work goes through,
> > > > > and it's already hard enough as it is. So, split that patch
> > > > > into a series of its own, get that merged, and then we will be
> > > > > able to deal with panels conversion and whatever. That's even
> > > > > more true with panels since there's ongoing work that will make
> > > > > it easier for you too. So the best thing here is probably to
> > > > > wait.
>
> The idea you proposed was to handle the issues current panel bridge
> code adds to the hotplug work by adding a .destroy callback and some
> more devm magic. I explored the idea but even after some clarifications
> from you it still didn't appear clearly doable and correct to me. And
> even in the case it were perfectly correct and doable, it is based on
> adding more complexity and "magic" on top of a topic that is already
> hard to understand: panel_bridge lifetime.
Not really, no. I told you several time that you shouldn't deal with
panels yet.
> So I opted for the other way: rework panel_bridge code so its lifetime
> is clear and as one would expect (panel_bridge lifetime == panel
> lifetime).
>
> Possibly more work for me, but now it's done and it's in these patches
> so why waiting?
No, it's not done. You have the same issue with panels than you are
trying to fix with bridges: it's allocated through devm so they'll get
destroyed too soon. The panel_bridge might work fine now, but the panel
won't.
So it's more work, more scope-creep, and more discussions. For example,
I'm really not convinced on moving the drm_panel code under bridge.
Splitting it up will allow you to at least merge the parts that are
somewhat agreed upon. But do however you want :)
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-10 14:23 ` Dmitry Baryshkov
2025-02-10 17:12 ` Luca Ceresoli
@ 2025-02-10 18:17 ` Maxime Ripard
1 sibling, 0 replies; 81+ messages in thread
From: Maxime Ripard @ 2025-02-10 18:17 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni,
Sam Ravnborg, linux-doc, Catalin Marinas, Paul Kocialkowski,
dri-devel, Claudiu Beznea, Laurent Pinchart, Andrzej Hajda,
David Airlie, Fabio Estevam, Marek Szyprowski, Simona Vetter,
Robert Foss, Jonathan Corbet, Will Deacon, Jernej Skrabec,
Daniel Thompson, Jagan Teki, Jessica Zhang, Luca Ceresoli,
Thomas Zimmermann, Jonas Karlman, Sascha Hauer, Maarten Lankhorst,
Inki Dae, linux-arm-kernel, Neil Armstrong, Boris Brezillon,
linux-kernel, Paul Kocialkowski, Pengutronix Kernel Team,
Shawn Guo
[-- Attachment #1: Type: text/plain, Size: 4362 bytes --]
On Mon, Feb 10, 2025 at 04:23:44PM +0200, Dmitry Baryshkov wrote:
> On Mon, 10 Feb 2025 at 14:31, Maxime Ripard <mripard@kernel.org> wrote:
> >
> > On Fri, Feb 07, 2025 at 09:54:06PM +0200, Dmitry Baryshkov wrote:
> > > On Fri, Feb 07, 2025 at 12:47:51PM +0100, Maxime Ripard wrote:
> > > > Hi,
> > > >
> > > > On Thu, Feb 06, 2025 at 07:14:29PM +0100, Luca Ceresoli wrote:
> > > > > DRM bridges are currently considered as a fixed element of a DRM card, and
> > > > > thus their lifetime is assumed to extend for as long as the card
> > > > > exists. New use cases, such as hot-pluggable hardware with video bridges,
> > > > > require DRM bridges to be added and removed to a DRM card without tearing
> > > > > the card down. This is possible for connectors already (used by DP MST), so
> > > > > add this possibility to DRM bridges as well.
> > > > >
> > > > > Implementation is based on drm_connector_init() as far as it makes sense,
> > > > > and differs when it doesn't. A difference is that bridges are not exposed
> > > > > to userspace, hence struct drm_bridge does not embed a struct
> > > > > drm_mode_object which would provide the refcount. Instead we add to struct
> > > > > drm_bridge a refcount field (we don't need other struct drm_mode_object
> > > > > fields here) and instead of using the drm_mode_object_*() functions we
> > > > > reimplement from those functions the few lines that drm_bridge needs for
> > > > > refcounting.
> > > > >
> > > > > Also add a new devm_drm_bridge_alloc() macro to allocate a new refcounted
> > > > > bridge.
> > > > >
> > > > > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> > > >
> > > > So, a couple of general comments:
> > > >
> > > > - I've said it a couple of times already, but I really think you're
> > > > making it harder than necessary for you here. This (and only this!)
> > > > should be the very first series you should be pushing. The rest can
> > > > only ever work if that work goes through, and it's already hard enough
> > > > as it is. So, split that patch into a series of its own, get that
> > > > merged, and then we will be able to deal with panels conversion and
> > > > whatever. That's even more true with panels since there's ongoing work
> > > > that will make it easier for you too. So the best thing here is
> > > > probably to wait.
> > >
> > > Luca and I had a quick chat on this during FOSDEM. I really think that
> > > panel (part of the) series can go in first as it fixes a very well known
> > > bug _and_ allows a pretty good cleanup to a whole set of drivers.
> >
> > I don't necessarily disagree on principle, but if you state that it can
> > get first, and fixes a known problem (which one?), then it should be a
> > separate, standalone, series.
>
> A problem of panel bridges having the wrong lifetime because of devm_
> attachment to a wrong device and so either being kept for too long or
> being destroyed too early.
Yeah, and panels too. Which isn't solved there.
> > Ever-expanding features are bad for both the reviewers and the
> > contributors, even more so when the discussion happens off-list.
> >
> > > With all those panel / bridge wrappers gone we should be able to see a
> > > clearer picture of what individual drivers are doing. In other words,
> > > which memory and which code actually hosts and uses internal
> > > 'next_bridge' reference.
> > >
> > > > - This patch really needs to be split into several patches, something
> > > > along the lines of:
> > > >
> > > > + Creating devm_drm_bridge_alloc()
> > > > + Adding refcounting
> > > > + Taking the references in all the needed places
> > > > + Converting a bunch of drivers
> > >
> > > The last two parts seem troublematic to me, but, I must admit, I didn't
> > > spend so much time reviewing all drm_bridge usage patterns.
> >
> > Why? the third one is already done by that patch, the fourth can
> > relatively easily be done using coccinelle.
>
> I have doubts about cocci. It doesn't have a way to know, what is the
> lifetime of the references to the reference-holding memory. Maybe I'm
> missing a point there.
It doesn't matter? Cocci doesn't (have to) understand the code, it
generates a patch for you. It's up to the author to make a correct
patch.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-07 19:54 ` Dmitry Baryshkov
2025-02-10 12:31 ` Maxime Ripard
@ 2025-02-10 17:12 ` Luca Ceresoli
2025-02-10 23:14 ` Dmitry Baryshkov
1 sibling, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-10 17:12 UTC (permalink / raw)
To: Dmitry Baryshkov, Maxime Ripard
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Inki Dae, linux-arm-kernel,
Neil Armstrong, Boris Brezillon, linux-kernel, Paul Kocialkowski,
Pengutronix Kernel Team, Shawn Guo
Hi Maxime, Dmitry
On Fri, 7 Feb 2025 21:54:06 +0200
Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> > > +/* Internal function (for refcounted bridges) */
> > > +void __drm_bridge_free(struct kref *kref)
> > > +{
> > > + struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount);
> > > + void *container = ((void *)bridge) - bridge->container_offset;
> > > +
> > > + DRM_DEBUG("bridge=%p, container=%p FREE\n", bridge, container);
> >
> > Pointers are not really useful to track here, since they are obfuscated
> > most of the time. Using the bridge device name would probably be better
> > (or removing the SHOUTING DEBUG entirely :))
>
> bridge device name or bridge funcs (I opted for the latter for the
> debugfs file)
These DRM_DEBUG()s proved extremely useful exactly because of the
pointer. This is because when using hotplug one normally has the same
device added and removed multiple times, and so the device name or
bridge funcs is always the same, preventing from understanding which
instance is leaking, or being freed, get, put, etc.
Do you think this is a sufficient motivation to keep it?
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-10 17:12 ` Luca Ceresoli
@ 2025-02-10 23:14 ` Dmitry Baryshkov
2025-02-11 8:48 ` Maxime Ripard
0 siblings, 1 reply; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-10 23:14 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Mon, Feb 10, 2025 at 06:12:44PM +0100, Luca Ceresoli wrote:
> Hi Maxime, Dmitry
>
> On Fri, 7 Feb 2025 21:54:06 +0200
> Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
>
> > > > +/* Internal function (for refcounted bridges) */
> > > > +void __drm_bridge_free(struct kref *kref)
> > > > +{
> > > > + struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount);
> > > > + void *container = ((void *)bridge) - bridge->container_offset;
> > > > +
> > > > + DRM_DEBUG("bridge=%p, container=%p FREE\n", bridge, container);
> > >
> > > Pointers are not really useful to track here, since they are obfuscated
> > > most of the time. Using the bridge device name would probably be better
> > > (or removing the SHOUTING DEBUG entirely :))
> >
> > bridge device name or bridge funcs (I opted for the latter for the
> > debugfs file)
>
> These DRM_DEBUG()s proved extremely useful exactly because of the
> pointer. This is because when using hotplug one normally has the same
> device added and removed multiple times, and so the device name or
> bridge funcs is always the same, preventing from understanding which
> instance is leaking, or being freed, get, put, etc.
>
> Do you think this is a sufficient motivation to keep it?
Then it should be something like %px. I found that %p is mangled.
What about having both device name _and_ a pointer?
>
> Luca
>
> --
> Luca Ceresoli, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-10 23:14 ` Dmitry Baryshkov
@ 2025-02-11 8:48 ` Maxime Ripard
2025-02-12 0:55 ` Dmitry Baryshkov
0 siblings, 1 reply; 81+ messages in thread
From: Maxime Ripard @ 2025-02-11 8:48 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni,
Sam Ravnborg, linux-doc, Catalin Marinas, Paul Kocialkowski,
dri-devel, Claudiu Beznea, Laurent Pinchart, Andrzej Hajda,
David Airlie, Fabio Estevam, Marek Szyprowski, Simona Vetter,
Robert Foss, Jonathan Corbet, Will Deacon, Jernej Skrabec,
Daniel Thompson, Jagan Teki, Jessica Zhang, Luca Ceresoli,
Thomas Zimmermann, Jonas Karlman, Sascha Hauer, Maarten Lankhorst,
Inki Dae, linux-arm-kernel, Neil Armstrong, Boris Brezillon,
linux-kernel, Paul Kocialkowski, Pengutronix Kernel Team,
Shawn Guo
[-- Attachment #1: Type: text/plain, Size: 1720 bytes --]
On Tue, Feb 11, 2025 at 01:14:28AM +0200, Dmitry Baryshkov wrote:
> On Mon, Feb 10, 2025 at 06:12:44PM +0100, Luca Ceresoli wrote:
> > Hi Maxime, Dmitry
> >
> > On Fri, 7 Feb 2025 21:54:06 +0200
> > Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> >
> > > > > +/* Internal function (for refcounted bridges) */
> > > > > +void __drm_bridge_free(struct kref *kref)
> > > > > +{
> > > > > + struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount);
> > > > > + void *container = ((void *)bridge) - bridge->container_offset;
> > > > > +
> > > > > + DRM_DEBUG("bridge=%p, container=%p FREE\n", bridge, container);
> > > >
> > > > Pointers are not really useful to track here, since they are obfuscated
> > > > most of the time. Using the bridge device name would probably be better
> > > > (or removing the SHOUTING DEBUG entirely :))
> > >
> > > bridge device name or bridge funcs (I opted for the latter for the
> > > debugfs file)
> >
> > These DRM_DEBUG()s proved extremely useful exactly because of the
> > pointer. This is because when using hotplug one normally has the same
> > device added and removed multiple times, and so the device name or
> > bridge funcs is always the same, preventing from understanding which
> > instance is leaking, or being freed, get, put, etc.
> >
> > Do you think this is a sufficient motivation to keep it?
>
> Then it should be something like %px. I found that %p is mangled.
> What about having both device name _and_ a pointer?
No, %px must not be used there. %p is mangled but should be consistent
across calls. But yeah, it's kind of the reason I suggested to use the
bridge device name instead.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-11 8:48 ` Maxime Ripard
@ 2025-02-12 0:55 ` Dmitry Baryshkov
2025-02-12 10:08 ` Maxime Ripard
0 siblings, 1 reply; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-12 0:55 UTC (permalink / raw)
To: Maxime Ripard
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni,
Sam Ravnborg, linux-doc, Catalin Marinas, Paul Kocialkowski,
dri-devel, Claudiu Beznea, Laurent Pinchart, Andrzej Hajda,
David Airlie, Fabio Estevam, Marek Szyprowski, Simona Vetter,
Robert Foss, Jonathan Corbet, Will Deacon, Jernej Skrabec,
Daniel Thompson, Jagan Teki, Jessica Zhang, Luca Ceresoli,
Thomas Zimmermann, Jonas Karlman, Sascha Hauer, Maarten Lankhorst,
Inki Dae, linux-arm-kernel, Neil Armstrong, Boris Brezillon,
linux-kernel, Paul Kocialkowski, Pengutronix Kernel Team,
Shawn Guo
On Tue, Feb 11, 2025 at 09:48:31AM +0100, Maxime Ripard wrote:
> On Tue, Feb 11, 2025 at 01:14:28AM +0200, Dmitry Baryshkov wrote:
> > On Mon, Feb 10, 2025 at 06:12:44PM +0100, Luca Ceresoli wrote:
> > > Hi Maxime, Dmitry
> > >
> > > On Fri, 7 Feb 2025 21:54:06 +0200
> > > Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> > >
> > > > > > +/* Internal function (for refcounted bridges) */
> > > > > > +void __drm_bridge_free(struct kref *kref)
> > > > > > +{
> > > > > > + struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount);
> > > > > > + void *container = ((void *)bridge) - bridge->container_offset;
> > > > > > +
> > > > > > + DRM_DEBUG("bridge=%p, container=%p FREE\n", bridge, container);
> > > > >
> > > > > Pointers are not really useful to track here, since they are obfuscated
> > > > > most of the time. Using the bridge device name would probably be better
> > > > > (or removing the SHOUTING DEBUG entirely :))
> > > >
> > > > bridge device name or bridge funcs (I opted for the latter for the
> > > > debugfs file)
> > >
> > > These DRM_DEBUG()s proved extremely useful exactly because of the
> > > pointer. This is because when using hotplug one normally has the same
> > > device added and removed multiple times, and so the device name or
> > > bridge funcs is always the same, preventing from understanding which
> > > instance is leaking, or being freed, get, put, etc.
> > >
> > > Do you think this is a sufficient motivation to keep it?
> >
> > Then it should be something like %px. I found that %p is mangled.
> > What about having both device name _and_ a pointer?
>
> No, %px must not be used there. %p is mangled but should be consistent
> across calls. But yeah, it's kind of the reason I suggested to use the
> bridge device name instead.
Then we need to extend struct drm_bridge with struct device *dev (which
I would appreciate, it will solve whole hdmi_audio_dev / CEC device /
etc story).
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-12 0:55 ` Dmitry Baryshkov
@ 2025-02-12 10:08 ` Maxime Ripard
0 siblings, 0 replies; 81+ messages in thread
From: Maxime Ripard @ 2025-02-12 10:08 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni,
Sam Ravnborg, linux-doc, Catalin Marinas, Paul Kocialkowski,
dri-devel, Claudiu Beznea, Laurent Pinchart, Andrzej Hajda,
David Airlie, Fabio Estevam, Marek Szyprowski, Simona Vetter,
Robert Foss, Jonathan Corbet, Will Deacon, Jernej Skrabec,
Daniel Thompson, Jagan Teki, Jessica Zhang, Luca Ceresoli,
Thomas Zimmermann, Jonas Karlman, Sascha Hauer, Maarten Lankhorst,
Inki Dae, linux-arm-kernel, Neil Armstrong, Boris Brezillon,
linux-kernel, Paul Kocialkowski, Pengutronix Kernel Team,
Shawn Guo
[-- Attachment #1: Type: text/plain, Size: 2387 bytes --]
On Wed, Feb 12, 2025 at 02:55:10AM +0200, Dmitry Baryshkov wrote:
> On Tue, Feb 11, 2025 at 09:48:31AM +0100, Maxime Ripard wrote:
> > On Tue, Feb 11, 2025 at 01:14:28AM +0200, Dmitry Baryshkov wrote:
> > > On Mon, Feb 10, 2025 at 06:12:44PM +0100, Luca Ceresoli wrote:
> > > > Hi Maxime, Dmitry
> > > >
> > > > On Fri, 7 Feb 2025 21:54:06 +0200
> > > > Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> > > >
> > > > > > > +/* Internal function (for refcounted bridges) */
> > > > > > > +void __drm_bridge_free(struct kref *kref)
> > > > > > > +{
> > > > > > > + struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount);
> > > > > > > + void *container = ((void *)bridge) - bridge->container_offset;
> > > > > > > +
> > > > > > > + DRM_DEBUG("bridge=%p, container=%p FREE\n", bridge, container);
> > > > > >
> > > > > > Pointers are not really useful to track here, since they are obfuscated
> > > > > > most of the time. Using the bridge device name would probably be better
> > > > > > (or removing the SHOUTING DEBUG entirely :))
> > > > >
> > > > > bridge device name or bridge funcs (I opted for the latter for the
> > > > > debugfs file)
> > > >
> > > > These DRM_DEBUG()s proved extremely useful exactly because of the
> > > > pointer. This is because when using hotplug one normally has the same
> > > > device added and removed multiple times, and so the device name or
> > > > bridge funcs is always the same, preventing from understanding which
> > > > instance is leaking, or being freed, get, put, etc.
> > > >
> > > > Do you think this is a sufficient motivation to keep it?
> > >
> > > Then it should be something like %px. I found that %p is mangled.
> > > What about having both device name _and_ a pointer?
> >
> > No, %px must not be used there. %p is mangled but should be consistent
> > across calls. But yeah, it's kind of the reason I suggested to use the
> > bridge device name instead.
>
> Then we need to extend struct drm_bridge with struct device *dev (which
> I would appreciate, it will solve whole hdmi_audio_dev / CEC device /
> etc story).
Let's not get carried away and start yet another side discussion here.
Most of these log messages need to be reworked anyway, so I'm sure we
can find something that wouldn't require yet another rework to argue
about.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-07 11:47 ` Maxime Ripard
2025-02-07 19:54 ` Dmitry Baryshkov
@ 2025-02-10 17:12 ` Luca Ceresoli
2025-02-11 13:10 ` Maxime Ripard
2025-03-13 11:56 ` Luca Ceresoli
2 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-10 17:12 UTC (permalink / raw)
To: Maxime Ripard
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Inki Dae, linux-arm-kernel,
Neil Armstrong, Boris Brezillon, linux-kernel, Paul Kocialkowski,
Pengutronix Kernel Team, Dmitry Baryshkov, Shawn Guo
Hello Maxime,
On Fri, 7 Feb 2025 12:47:51 +0100
Maxime Ripard <mripard@kernel.org> wrote:
> > diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> > index ad7ba444a13e5ecf16f996de3742e4ac67dc21f1..43cef0f6ccd36034f64ad2babfebea62db1d9e43 100644
> > --- a/include/drm/drm_bridge.h
> > +++ b/include/drm/drm_bridge.h
> > @@ -31,6 +31,7 @@
> > #include <drm/drm_encoder.h>
> > #include <drm/drm_mode_object.h>
> > #include <drm/drm_modes.h>
> > +#include <drm/drm_print.h>
> >
> > struct device_node;
> >
> > @@ -863,6 +864,22 @@ struct drm_bridge {
> > const struct drm_bridge_timings *timings;
> > /** @funcs: control functions */
> > const struct drm_bridge_funcs *funcs;
> > +
> > + /**
> > + * @container_offset: Offset of this struct within the container
> > + * struct embedding it. Used for refcounted bridges to free the
> > + * embeddeing struct when the refcount drops to zero. Unused on
> > + * legacy bridges.
> > + */
> > + size_t container_offset;
>
> This shouldn't be in there. You can create an intermediate structure and
> store both pointers for the action to consume.
You mean to store container_offset + refcount + is_refcounted?
It can be named drm_bridge_object maybe, as it is somewhat resembling
struct drm_mode_object?
> > + /**
> > + * @refcount: reference count for bridges with dynamic lifetime
> > + * (see drm_bridge_init)
> > + */
> > + struct kref refcount;
> > + /** @is_refcounted: this bridge has dynamic lifetime management */
> > + bool is_refcounted;
> > +
>
> I'm not sure we want to treat both paths separately too. It'll require
> to update most of/all the drivers, but it's not too hard with
> coccinelle:
>
> virtual patch
>
> @@
> identifier f;
> identifier b, c, d;
> expression bf;
> type T;
> @@
>
> f(...)
> {
> ...
> - T *c;
> + T *c;
> ...
> - c = devm_kzalloc(d, ...);
> + c = devm_drm_bridge_alloc(d, T, b, bf);
> ...
> - c->b.funcs = bf;
> ...
> drm_bridge_add(&c->b);
> ...
> }
>
> We need to add a bit more variations (like kzalloc vs devm_kzalloc,
> drm_bridge_add vs devm_drm_bridge_add, etc.), but it should be a good
> first approximation
Sure, this can be useful, thanks.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-10 17:12 ` Luca Ceresoli
@ 2025-02-11 13:10 ` Maxime Ripard
2025-02-26 14:28 ` Luca Ceresoli
0 siblings, 1 reply; 81+ messages in thread
From: Maxime Ripard @ 2025-02-11 13:10 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Inki Dae, linux-arm-kernel,
Neil Armstrong, Boris Brezillon, linux-kernel, Paul Kocialkowski,
Pengutronix Kernel Team, Dmitry Baryshkov, Shawn Guo
[-- Attachment #1: Type: text/plain, Size: 3637 bytes --]
On Mon, Feb 10, 2025 at 06:12:52PM +0100, Luca Ceresoli wrote:
> Hello Maxime,
>
> On Fri, 7 Feb 2025 12:47:51 +0100
> Maxime Ripard <mripard@kernel.org> wrote:
>
> > > diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> > > index ad7ba444a13e5ecf16f996de3742e4ac67dc21f1..43cef0f6ccd36034f64ad2babfebea62db1d9e43 100644
> > > --- a/include/drm/drm_bridge.h
> > > +++ b/include/drm/drm_bridge.h
> > > @@ -31,6 +31,7 @@
> > > #include <drm/drm_encoder.h>
> > > #include <drm/drm_mode_object.h>
> > > #include <drm/drm_modes.h>
> > > +#include <drm/drm_print.h>
> > >
> > > struct device_node;
> > >
> > > @@ -863,6 +864,22 @@ struct drm_bridge {
> > > const struct drm_bridge_timings *timings;
> > > /** @funcs: control functions */
> > > const struct drm_bridge_funcs *funcs;
> > > +
> > > + /**
> > > + * @container_offset: Offset of this struct within the container
> > > + * struct embedding it. Used for refcounted bridges to free the
> > > + * embeddeing struct when the refcount drops to zero. Unused on
> > > + * legacy bridges.
> > > + */
> > > + size_t container_offset;
> >
> > This shouldn't be in there. You can create an intermediate structure and
> > store both pointers for the action to consume.
>
> You mean to store container_offset + refcount + is_refcounted?
No, I meant for the private structure pointer and the drm_bridge
pointer. refcount should be in drm_bridge, and I think is_refcounted
should be dropped.
> It can be named drm_bridge_object maybe, as it is somewhat resembling
> struct drm_mode_object?
>
> > > + /**
> > > + * @refcount: reference count for bridges with dynamic lifetime
> > > + * (see drm_bridge_init)
> > > + */
> > > + struct kref refcount;
> > > + /** @is_refcounted: this bridge has dynamic lifetime management */
> > > + bool is_refcounted;
> > > +
> >
> > I'm not sure we want to treat both paths separately too. It'll require
> > to update most of/all the drivers, but it's not too hard with
> > coccinelle:
> >
> > virtual patch
> >
> > @@
> > identifier f;
> > identifier b, c, d;
> > expression bf;
> > type T;
> > @@
> >
> > f(...)
> > {
> > ...
> > - T *c;
> > + T *c;
> > ...
> > - c = devm_kzalloc(d, ...);
> > + c = devm_drm_bridge_alloc(d, T, b, bf);
> > ...
> > - c->b.funcs = bf;
> > ...
> > drm_bridge_add(&c->b);
> > ...
> > }
> >
> > We need to add a bit more variations (like kzalloc vs devm_kzalloc,
> > drm_bridge_add vs devm_drm_bridge_add, etc.), but it should be a good
> > first approximation
>
> Sure, this can be useful, thanks.
You can identify all the bridges affected by this issue using:
virtual report
@ find_add @
identifier add_f;
identifier c;
identifier b;
expression d;
position p;
identifier r;
type T;
@@
add_f(...)
{
...
- T *c;
+ T *c;
...
(
drm_bridge_add(&c->b)@p;
|
devm_drm_bridge_add(d, &c->b)@p;
|
r = devm_drm_bridge_add(d, &c->b)@p;
)
...
}
@ find_allocation depends on find_add @
identifier alloc_f;
type find_add.T;
identifier cal;
position p;
@@
alloc_f(...)
{
...
- T *cal;
+ T *cal;
...
(
cal = kzalloc(...)@p;
|
cal = devm_kzalloc(...)@p;
)
...
}
@ script:python depends on report && (find_add && find_allocation) @
add_f << find_add.add_f;
alloc_f << find_allocation.alloc_f;
add_p << find_add.p;
alloc_p << find_allocation.p;
@@
coccilib.report.print_report(alloc_p[0], "ERROR: Bridge Driver is unsafely allocated in %s and added in %s" % (alloc_f, add_f))
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-11 13:10 ` Maxime Ripard
@ 2025-02-26 14:28 ` Luca Ceresoli
2025-02-27 9:32 ` Maxime Ripard
0 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-26 14:28 UTC (permalink / raw)
To: Maxime Ripard
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Inki Dae, linux-arm-kernel,
Neil Armstrong, Boris Brezillon, linux-kernel, Paul Kocialkowski,
Pengutronix Kernel Team, Dmitry Baryshkov, Shawn Guo
Hi Maxime,
On Tue, 11 Feb 2025 14:10:50 +0100
Maxime Ripard <mripard@kernel.org> wrote:
> On Mon, Feb 10, 2025 at 06:12:52PM +0100, Luca Ceresoli wrote:
> > Hello Maxime,
> >
> > On Fri, 7 Feb 2025 12:47:51 +0100
> > Maxime Ripard <mripard@kernel.org> wrote:
> >
> > > > diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> > > > index ad7ba444a13e5ecf16f996de3742e4ac67dc21f1..43cef0f6ccd36034f64ad2babfebea62db1d9e43 100644
> > > > --- a/include/drm/drm_bridge.h
> > > > +++ b/include/drm/drm_bridge.h
> > > > @@ -31,6 +31,7 @@
> > > > #include <drm/drm_encoder.h>
> > > > #include <drm/drm_mode_object.h>
> > > > #include <drm/drm_modes.h>
> > > > +#include <drm/drm_print.h>
> > > >
> > > > struct device_node;
> > > >
> > > > @@ -863,6 +864,22 @@ struct drm_bridge {
> > > > const struct drm_bridge_timings *timings;
> > > > /** @funcs: control functions */
> > > > const struct drm_bridge_funcs *funcs;
> > > > +
> > > > + /**
> > > > + * @container_offset: Offset of this struct within the container
> > > > + * struct embedding it. Used for refcounted bridges to free the
> > > > + * embeddeing struct when the refcount drops to zero. Unused on
> > > > + * legacy bridges.
> > > > + */
> > > > + size_t container_offset;
> > >
> > > This shouldn't be in there. You can create an intermediate structure and
> > > store both pointers for the action to consume.
> >
> > You mean to store container_offset + refcount + is_refcounted?
>
> No, I meant for the private structure pointer and the drm_bridge
> pointer. refcount should be in drm_bridge, and I think is_refcounted
> should be dropped.
Storing the container pointer instead of the offset is a good idea, it
will allow to get rid of is_refcounted: drm_bridge_is_refcounted() can
just return "container != NULL" instead of "bridge->is_refcounted". So
far so good.
I'm not sure about the intermediate struct you have in mind though.
Do you mean:
struct drm_bridge_pointers {
struct drm_bridge *bridge;
void *container;
}
?
If that's what you mean, should it be embedded in drm_struct or
allocated separately?
If you mean to embed that struct in drm_bridge, then I the drm_bridge
pointer inside the intermediate struct would be useless.
If instead you mean to embed it in drm_struct: I'm not sure I see much
benefit except maybe not exposing the container pointer to drm_bridge
users, but I see a drawbacks: at the last put we need to find the
container pointer to free from a struct kref pointer, which can work
only if the container pointer is in the same struct as struct kref.
Additionally, the consuming action for that struct just needs a
drm_bridge pointer:
static void drm_bridge_put_void(void *data)
{
struct drm_bridge *bridge = (struct drm_bridge *)data;
drm_bridge_put(bridge);
}
Can you clarify this? I'd love to have this cleanup in the next
iteration.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-26 14:28 ` Luca Ceresoli
@ 2025-02-27 9:32 ` Maxime Ripard
2025-02-27 11:31 ` Luca Ceresoli
0 siblings, 1 reply; 81+ messages in thread
From: Maxime Ripard @ 2025-02-27 9:32 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Inki Dae, linux-arm-kernel,
Neil Armstrong, Boris Brezillon, linux-kernel, Paul Kocialkowski,
Pengutronix Kernel Team, Dmitry Baryshkov, Shawn Guo
[-- Attachment #1: Type: text/plain, Size: 3399 bytes --]
On Wed, Feb 26, 2025 at 03:28:13PM +0100, Luca Ceresoli wrote:
> On Tue, 11 Feb 2025 14:10:50 +0100
> Maxime Ripard <mripard@kernel.org> wrote:
> > On Mon, Feb 10, 2025 at 06:12:52PM +0100, Luca Ceresoli wrote:
> > > On Fri, 7 Feb 2025 12:47:51 +0100
> > > Maxime Ripard <mripard@kernel.org> wrote:
> > >
> > > > > diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> > > > > index ad7ba444a13e5ecf16f996de3742e4ac67dc21f1..43cef0f6ccd36034f64ad2babfebea62db1d9e43 100644
> > > > > --- a/include/drm/drm_bridge.h
> > > > > +++ b/include/drm/drm_bridge.h
> > > > > @@ -31,6 +31,7 @@
> > > > > #include <drm/drm_encoder.h>
> > > > > #include <drm/drm_mode_object.h>
> > > > > #include <drm/drm_modes.h>
> > > > > +#include <drm/drm_print.h>
> > > > >
> > > > > struct device_node;
> > > > >
> > > > > @@ -863,6 +864,22 @@ struct drm_bridge {
> > > > > const struct drm_bridge_timings *timings;
> > > > > /** @funcs: control functions */
> > > > > const struct drm_bridge_funcs *funcs;
> > > > > +
> > > > > + /**
> > > > > + * @container_offset: Offset of this struct within the container
> > > > > + * struct embedding it. Used for refcounted bridges to free the
> > > > > + * embeddeing struct when the refcount drops to zero. Unused on
> > > > > + * legacy bridges.
> > > > > + */
> > > > > + size_t container_offset;
> > > >
> > > > This shouldn't be in there. You can create an intermediate structure and
> > > > store both pointers for the action to consume.
> > >
> > > You mean to store container_offset + refcount + is_refcounted?
> >
> > No, I meant for the private structure pointer and the drm_bridge
> > pointer. refcount should be in drm_bridge, and I think is_refcounted
> > should be dropped.
>
> Storing the container pointer instead of the offset is a good idea, it
> will allow to get rid of is_refcounted: drm_bridge_is_refcounted() can
> just return "container != NULL" instead of "bridge->is_refcounted". So
> far so good.
Again, I don't think the whole is_refcounted thing is a good idea. Once
we have the right API, we should convert all bridges to the new
allocation and assume that they are refcounted.
> I'm not sure about the intermediate struct you have in mind though.
>
> Do you mean:
>
> struct drm_bridge_pointers {
> struct drm_bridge *bridge;
> void *container;
> }
>
> ?
Yes
> If that's what you mean, should it be embedded in drm_struct or
> allocated separately?
Separately, but still as part of the bridge allocation function.
> If you mean to embed that struct in drm_bridge, then I the drm_bridge
> pointer inside the intermediate struct would be useless.
>
> If instead you mean to embed it in drm_struct: I'm not sure I see much
> benefit except maybe not exposing the container pointer to drm_bridge
> users, but I see a drawbacks: at the last put we need to find the
> container pointer to free from a struct kref pointer, which can work
> only if the container pointer is in the same struct as struct kref.
Yeah, that's true. Storing the container pointer in drm_bridge makes
sense to solve this.
I'm still not sure why we need the container offset though: if we have a
bridge and container pointer, then the offset is bridge - container, so
there's no point in storing it, right?
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-27 9:32 ` Maxime Ripard
@ 2025-02-27 11:31 ` Luca Ceresoli
2025-02-27 14:39 ` Maxime Ripard
0 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-27 11:31 UTC (permalink / raw)
To: Maxime Ripard
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Inki Dae, linux-arm-kernel,
Neil Armstrong, Boris Brezillon, linux-kernel, Paul Kocialkowski,
Pengutronix Kernel Team, Dmitry Baryshkov, Shawn Guo
Hi Maxime,
On Thu, 27 Feb 2025 10:32:20 +0100
Maxime Ripard <mripard@kernel.org> wrote:
> On Wed, Feb 26, 2025 at 03:28:13PM +0100, Luca Ceresoli wrote:
> > On Tue, 11 Feb 2025 14:10:50 +0100
> > Maxime Ripard <mripard@kernel.org> wrote:
> > > On Mon, Feb 10, 2025 at 06:12:52PM +0100, Luca Ceresoli wrote:
> > > > On Fri, 7 Feb 2025 12:47:51 +0100
> > > > Maxime Ripard <mripard@kernel.org> wrote:
> > > >
> > > > > > diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> > > > > > index ad7ba444a13e5ecf16f996de3742e4ac67dc21f1..43cef0f6ccd36034f64ad2babfebea62db1d9e43 100644
> > > > > > --- a/include/drm/drm_bridge.h
> > > > > > +++ b/include/drm/drm_bridge.h
> > > > > > @@ -31,6 +31,7 @@
> > > > > > #include <drm/drm_encoder.h>
> > > > > > #include <drm/drm_mode_object.h>
> > > > > > #include <drm/drm_modes.h>
> > > > > > +#include <drm/drm_print.h>
> > > > > >
> > > > > > struct device_node;
> > > > > >
> > > > > > @@ -863,6 +864,22 @@ struct drm_bridge {
> > > > > > const struct drm_bridge_timings *timings;
> > > > > > /** @funcs: control functions */
> > > > > > const struct drm_bridge_funcs *funcs;
> > > > > > +
> > > > > > + /**
> > > > > > + * @container_offset: Offset of this struct within the container
> > > > > > + * struct embedding it. Used for refcounted bridges to free the
> > > > > > + * embeddeing struct when the refcount drops to zero. Unused on
> > > > > > + * legacy bridges.
> > > > > > + */
> > > > > > + size_t container_offset;
> > > > >
> > > > > This shouldn't be in there. You can create an intermediate structure and
> > > > > store both pointers for the action to consume.
> > > >
> > > > You mean to store container_offset + refcount + is_refcounted?
> > >
> > > No, I meant for the private structure pointer and the drm_bridge
> > > pointer. refcount should be in drm_bridge, and I think is_refcounted
> > > should be dropped.
> >
> > Storing the container pointer instead of the offset is a good idea, it
> > will allow to get rid of is_refcounted: drm_bridge_is_refcounted() can
> > just return "container != NULL" instead of "bridge->is_refcounted". So
> > far so good.
>
> Again, I don't think the whole is_refcounted thing is a good idea. Once
> we have the right API, we should convert all bridges to the new
> allocation and assume that they are refcounted.
Ah, thanks for clarifying, now I understand the reason you'd remove
is_refecounted while I didn't. In my plan it's for a transition phase
where not all bridges are converted yet. I should have added a note
about that, indeed.
While I obviously think all bridges should be converted to dynamic
lifetime, I'm not sure it can happen all in a single run, however.
Converting bridges to refcounting is mostly easy, but before we should
switch all bridge users to put the pointers they have, or the bridges
will never be freed. But the users are more in number and harder to
convert. However I still haven't tried a real conversion of all of
them, so it I'm going to reconsider this after I'll have tried.
Generally speaking, would you be OK with having is_refcounted in a
transition phase, or do you think we absolutely must convert all bridge
drivers and users at once?
> > I'm not sure about the intermediate struct you have in mind though.
> >
> > Do you mean:
> >
> > struct drm_bridge_pointers {
> > struct drm_bridge *bridge;
> > void *container;
> > }
> >
> > ?
>
> Yes
>
> > If that's what you mean, should it be embedded in drm_struct or
> > allocated separately?
>
> Separately, but still as part of the bridge allocation function.
>
> > If you mean to embed that struct in drm_bridge, then I the drm_bridge
> > pointer inside the intermediate struct would be useless.
> >
> > If instead you mean to embed it in drm_struct: I'm not sure I see much
^^^^^^^^^^^^^^^^^^^^^^^^^
For the records, I (obviously?) meant "allocated separately" here.
> > benefit except maybe not exposing the container pointer to drm_bridge
> > users, but I see a drawbacks: at the last put we need to find the
> > container pointer to free from a struct kref pointer, which can work
> > only if the container pointer is in the same struct as struct kref.
>
> Yeah, that's true. Storing the container pointer in drm_bridge makes
> sense to solve this.
OK, so when moving the container pointer to drm_bridge, the
drm_bridge_pointers struct will be left with the drm_bridge pointer
only:
struct drm_bridge_pointer {
struct drm_bridge *bridge;
}
So while it would work, I still don't see the added value. We'd have
one more allocation, we'd need to free both structs at the same time
(correct?) and drm_bridge_put_void() would have an extra indirection
step:
static void drm_bridge_put_void(void *data)
{
struct drm_bridge_pointer *bridge_pointer = (struct drm_bridge_pointers *)data;
struct drm_bridge *bridge = bridge_pointer->bridge;
drm_bridge_put(bridge);
}
Can you elaborate on the gain in having such struct, or point me to
some code using the same pattern?
> I'm still not sure why we need the container offset though: if we have a
> bridge and container pointer, then the offset is bridge - container, so
> there's no point in storing it, right?
We need either the container_offset or the container pointer, not both.
I had chosen the offset in v6, I'm going to convert to the pointer in
v7.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-27 11:31 ` Luca Ceresoli
@ 2025-02-27 14:39 ` Maxime Ripard
0 siblings, 0 replies; 81+ messages in thread
From: Maxime Ripard @ 2025-02-27 14:39 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Inki Dae, linux-arm-kernel,
Neil Armstrong, Boris Brezillon, linux-kernel, Paul Kocialkowski,
Pengutronix Kernel Team, Dmitry Baryshkov, Shawn Guo
[-- Attachment #1: Type: text/plain, Size: 7113 bytes --]
On Thu, Feb 27, 2025 at 12:31:43PM +0100, Luca Ceresoli wrote:
> On Thu, 27 Feb 2025 10:32:20 +0100
> Maxime Ripard <mripard@kernel.org> wrote:
> > On Wed, Feb 26, 2025 at 03:28:13PM +0100, Luca Ceresoli wrote:
> > > On Tue, 11 Feb 2025 14:10:50 +0100
> > > Maxime Ripard <mripard@kernel.org> wrote:
> > > > On Mon, Feb 10, 2025 at 06:12:52PM +0100, Luca Ceresoli wrote:
> > > > > On Fri, 7 Feb 2025 12:47:51 +0100
> > > > > Maxime Ripard <mripard@kernel.org> wrote:
> > > > >
> > > > > > > diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> > > > > > > index ad7ba444a13e5ecf16f996de3742e4ac67dc21f1..43cef0f6ccd36034f64ad2babfebea62db1d9e43 100644
> > > > > > > --- a/include/drm/drm_bridge.h
> > > > > > > +++ b/include/drm/drm_bridge.h
> > > > > > > @@ -31,6 +31,7 @@
> > > > > > > #include <drm/drm_encoder.h>
> > > > > > > #include <drm/drm_mode_object.h>
> > > > > > > #include <drm/drm_modes.h>
> > > > > > > +#include <drm/drm_print.h>
> > > > > > >
> > > > > > > struct device_node;
> > > > > > >
> > > > > > > @@ -863,6 +864,22 @@ struct drm_bridge {
> > > > > > > const struct drm_bridge_timings *timings;
> > > > > > > /** @funcs: control functions */
> > > > > > > const struct drm_bridge_funcs *funcs;
> > > > > > > +
> > > > > > > + /**
> > > > > > > + * @container_offset: Offset of this struct within the container
> > > > > > > + * struct embedding it. Used for refcounted bridges to free the
> > > > > > > + * embeddeing struct when the refcount drops to zero. Unused on
> > > > > > > + * legacy bridges.
> > > > > > > + */
> > > > > > > + size_t container_offset;
> > > > > >
> > > > > > This shouldn't be in there. You can create an intermediate structure and
> > > > > > store both pointers for the action to consume.
> > > > >
> > > > > You mean to store container_offset + refcount + is_refcounted?
> > > >
> > > > No, I meant for the private structure pointer and the drm_bridge
> > > > pointer. refcount should be in drm_bridge, and I think is_refcounted
> > > > should be dropped.
> > >
> > > Storing the container pointer instead of the offset is a good idea, it
> > > will allow to get rid of is_refcounted: drm_bridge_is_refcounted() can
> > > just return "container != NULL" instead of "bridge->is_refcounted". So
> > > far so good.
> >
> > Again, I don't think the whole is_refcounted thing is a good idea. Once
> > we have the right API, we should convert all bridges to the new
> > allocation and assume that they are refcounted.
>
> Ah, thanks for clarifying, now I understand the reason you'd remove
> is_refecounted while I didn't. In my plan it's for a transition phase
> where not all bridges are converted yet. I should have added a note
> about that, indeed.
>
> While I obviously think all bridges should be converted to dynamic
> lifetime, I'm not sure it can happen all in a single run, however.
> Converting bridges to refcounting is mostly easy, but before we should
> switch all bridge users to put the pointers they have, or the bridges
> will never be freed. But the users are more in number and harder to
> convert. However I still haven't tried a real conversion of all of
> them, so it I'm going to reconsider this after I'll have tried.
I mean, you're going to have that issue anyway. There's several calls
that can get a bridge to a consumer:
- of_drm_find_bridge
- drm_bridge_get_prev_bridge / drm_bridge_get_next_bridge
- drm_bridge_chain_get_first_bridge
- drm_for_each_bridge_in_chain
- devm_drm_of_get_bridge
- drmm_of_get_bridge
The last two are easy to deal with: just add an action that will put the
reference, and you're done. devm_drm_of_get_bridge() still is completely
broken and you should deprecate it as well, but the semantics are what
they are already so you're not going to break it more than it already
is.
For all the others though, you do change the semantics of the API, and
you will need to add drm_bridge_put or switch to drmm_of_get_bridge.
Also, is_refcounted doesn't really help. Your problem is that *callers*
might not give back the reference, but the way you set it is how you
allocate the provider.
Even assuming we're not doing the mass-conversion, how do you ensure
that you fixed all the callers that would use the bridge you just
converted?
> Generally speaking, would you be OK with having is_refcounted in a
> transition phase, or do you think we absolutely must convert all bridge
> drivers and users at once?
No, see above.
> > > I'm not sure about the intermediate struct you have in mind though.
> > >
> > > Do you mean:
> > >
> > > struct drm_bridge_pointers {
> > > struct drm_bridge *bridge;
> > > void *container;
> > > }
> > >
> > > ?
> >
> > Yes
> >
> > > If that's what you mean, should it be embedded in drm_struct or
> > > allocated separately?
> >
> > Separately, but still as part of the bridge allocation function.
> >
> > > If you mean to embed that struct in drm_bridge, then I the drm_bridge
> > > pointer inside the intermediate struct would be useless.
> > >
> > > If instead you mean to embed it in drm_struct: I'm not sure I see much
> ^^^^^^^^^^^^^^^^^^^^^^^^^
> For the records, I (obviously?) meant "allocated separately" here.
>
> > > benefit except maybe not exposing the container pointer to drm_bridge
> > > users, but I see a drawbacks: at the last put we need to find the
> > > container pointer to free from a struct kref pointer, which can work
> > > only if the container pointer is in the same struct as struct kref.
> >
> > Yeah, that's true. Storing the container pointer in drm_bridge makes
> > sense to solve this.
>
> OK, so when moving the container pointer to drm_bridge, the
> drm_bridge_pointers struct will be left with the drm_bridge pointer
> only:
>
> struct drm_bridge_pointer {
> struct drm_bridge *bridge;
> }
>
> So while it would work, I still don't see the added value. We'd have
> one more allocation, we'd need to free both structs at the same time
> (correct?) and drm_bridge_put_void() would have an extra indirection
> step:
>
> static void drm_bridge_put_void(void *data)
> {
> struct drm_bridge_pointer *bridge_pointer = (struct drm_bridge_pointers *)data;
> struct drm_bridge *bridge = bridge_pointer->bridge;
>
> drm_bridge_put(bridge);
> }
>
> Can you elaborate on the gain in having such struct, or point me to
> some code using the same pattern?
There's none, if we're going to have a single pointer it's not useful
indeed.
> > I'm still not sure why we need the container offset though: if we have a
> > bridge and container pointer, then the offset is bridge - container, so
> > there's no point in storing it, right?
>
> We need either the container_offset or the container pointer, not both.
> I had chosen the offset in v6, I'm going to convert to the pointer in
> v7.
Sounds good
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-02-07 11:47 ` Maxime Ripard
2025-02-07 19:54 ` Dmitry Baryshkov
2025-02-10 17:12 ` Luca Ceresoli
@ 2025-03-13 11:56 ` Luca Ceresoli
2025-03-13 18:07 ` Maxime Ripard
2 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-03-13 11:56 UTC (permalink / raw)
To: Maxime Ripard
Cc: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang,
Paul Kocialkowski, Dmitry Baryshkov, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Maarten Lankhorst, Thomas Zimmermann, David Airlie,
Hervé Codina, Thomas Petazzoni, linux-kernel, dri-devel,
linux-doc, linux-arm-kernel, Paul Kocialkowski
Hello Maxime,
On Fri, 7 Feb 2025 12:47:51 +0100
Maxime Ripard <mripard@kernel.org> wrote:
> Hi,
>
> On Thu, Feb 06, 2025 at 07:14:29PM +0100, Luca Ceresoli wrote:
> > DRM bridges are currently considered as a fixed element of a DRM card, and
> > thus their lifetime is assumed to extend for as long as the card
> > exists. New use cases, such as hot-pluggable hardware with video bridges,
> > require DRM bridges to be added and removed to a DRM card without tearing
> > the card down. This is possible for connectors already (used by DP MST), so
> > add this possibility to DRM bridges as well.
> >
> > Implementation is based on drm_connector_init() as far as it makes sense,
> > and differs when it doesn't. A difference is that bridges are not exposed
> > to userspace, hence struct drm_bridge does not embed a struct
> > drm_mode_object which would provide the refcount. Instead we add to struct
> > drm_bridge a refcount field (we don't need other struct drm_mode_object
> > fields here) and instead of using the drm_mode_object_*() functions we
> > reimplement from those functions the few lines that drm_bridge needs for
> > refcounting.
> >
> > Also add a new devm_drm_bridge_alloc() macro to allocate a new refcounted
> > bridge.
> >
> > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> So, a couple of general comments:
>
> - I've said it a couple of times already, but I really think you're
> making it harder than necessary for you here. This (and only this!)
> should be the very first series you should be pushing. The rest can
> only ever work if that work goes through, and it's already hard enough
> as it is. So, split that patch into a series of its own, get that
> merged, and then we will be able to deal with panels conversion and
> whatever. That's even more true with panels since there's ongoing work
> that will make it easier for you too. So the best thing here is
> probably to wait.
>
> - This patch really needs to be split into several patches, something
> along the lines of:
>
> + Creating devm_drm_bridge_alloc()
> + Adding refcounting
> + Taking the references in all the needed places
> + Converting a bunch of drivers
After reading Anusha's "[PATCH RFC 0/2] drm/panel: Refcounted panel
allocation" [0] I think I need a clarification about the 4 steps you had
outlined in the above quoted text. Are you suggesting those are four
_series_, and you'd want to see a series only creating
devm_drm_bridge_alloc() as a first step, similarly to Anusha's work?
That was not my understanding so far, and so I've been working on a
series containing all 4 items, and it's growing very long due to item 3
needing to touch many dozen drivers which need to put a bridge (many
are identical oneliner patches though).
Luca
[0] https://lore.kernel.org/all/20250312-drm-panel-v1-0-e99cd69f6136@redhat.com/
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-03-13 11:56 ` Luca Ceresoli
@ 2025-03-13 18:07 ` Maxime Ripard
2025-03-14 8:11 ` Luca Ceresoli
0 siblings, 1 reply; 81+ messages in thread
From: Maxime Ripard @ 2025-03-13 18:07 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang,
Paul Kocialkowski, Dmitry Baryshkov, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Maarten Lankhorst, Thomas Zimmermann, David Airlie,
Hervé Codina, Thomas Petazzoni, linux-kernel, dri-devel,
linux-doc, linux-arm-kernel, Paul Kocialkowski
[-- Attachment #1: Type: text/plain, Size: 3347 bytes --]
On Thu, Mar 13, 2025 at 12:56:56PM +0100, Luca Ceresoli wrote:
> Hello Maxime,
>
> On Fri, 7 Feb 2025 12:47:51 +0100
> Maxime Ripard <mripard@kernel.org> wrote:
> > On Thu, Feb 06, 2025 at 07:14:29PM +0100, Luca Ceresoli wrote:
> > > DRM bridges are currently considered as a fixed element of a DRM card, and
> > > thus their lifetime is assumed to extend for as long as the card
> > > exists. New use cases, such as hot-pluggable hardware with video bridges,
> > > require DRM bridges to be added and removed to a DRM card without tearing
> > > the card down. This is possible for connectors already (used by DP MST), so
> > > add this possibility to DRM bridges as well.
> > >
> > > Implementation is based on drm_connector_init() as far as it makes sense,
> > > and differs when it doesn't. A difference is that bridges are not exposed
> > > to userspace, hence struct drm_bridge does not embed a struct
> > > drm_mode_object which would provide the refcount. Instead we add to struct
> > > drm_bridge a refcount field (we don't need other struct drm_mode_object
> > > fields here) and instead of using the drm_mode_object_*() functions we
> > > reimplement from those functions the few lines that drm_bridge needs for
> > > refcounting.
> > >
> > > Also add a new devm_drm_bridge_alloc() macro to allocate a new refcounted
> > > bridge.
> > >
> > > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> >
> > So, a couple of general comments:
> >
> > - I've said it a couple of times already, but I really think you're
> > making it harder than necessary for you here. This (and only this!)
> > should be the very first series you should be pushing. The rest can
> > only ever work if that work goes through, and it's already hard enough
> > as it is. So, split that patch into a series of its own, get that
> > merged, and then we will be able to deal with panels conversion and
> > whatever. That's even more true with panels since there's ongoing work
> > that will make it easier for you too. So the best thing here is
> > probably to wait.
> >
> > - This patch really needs to be split into several patches, something
> > along the lines of:
> >
> > + Creating devm_drm_bridge_alloc()
> > + Adding refcounting
> > + Taking the references in all the needed places
> > + Converting a bunch of drivers
>
> After reading Anusha's "[PATCH RFC 0/2] drm/panel: Refcounted panel
> allocation" [0] I think I need a clarification about the 4 steps you had
> outlined in the above quoted text. Are you suggesting those are four
> _series_, and you'd want to see a series only creating
> devm_drm_bridge_alloc() as a first step, similarly to Anusha's work?
>
> That was not my understanding so far, and so I've been working on a
> series containing all 4 items, and it's growing very long due to item 3
> needing to touch many dozen drivers which need to put a bridge (many
> are identical oneliner patches though).
I believe I've clarified it already in Anusha's series, but I think a
reasonable series for *early* work would be the bullet points 1, 2, a
bit of 3 and a bit of 4.
Once the API is agreed upon, 1, 2 and 4 should be in the same series. As
you've pointed out, item 3 can be large, so I don't really mind either
way.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges
2025-03-13 18:07 ` Maxime Ripard
@ 2025-03-14 8:11 ` Luca Ceresoli
0 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-03-14 8:11 UTC (permalink / raw)
To: Maxime Ripard
Cc: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang,
Paul Kocialkowski, Dmitry Baryshkov, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Maarten Lankhorst, Thomas Zimmermann, David Airlie,
Hervé Codina, Thomas Petazzoni, linux-kernel, dri-devel,
linux-doc, linux-arm-kernel, Paul Kocialkowski
Hello Maxime,
On Thu, 13 Mar 2025 19:07:17 +0100
Maxime Ripard <mripard@kernel.org> wrote:
> On Thu, Mar 13, 2025 at 12:56:56PM +0100, Luca Ceresoli wrote:
> > Hello Maxime,
> >
> > On Fri, 7 Feb 2025 12:47:51 +0100
> > Maxime Ripard <mripard@kernel.org> wrote:
> > > On Thu, Feb 06, 2025 at 07:14:29PM +0100, Luca Ceresoli wrote:
> > > > DRM bridges are currently considered as a fixed element of a DRM card, and
> > > > thus their lifetime is assumed to extend for as long as the card
> > > > exists. New use cases, such as hot-pluggable hardware with video bridges,
> > > > require DRM bridges to be added and removed to a DRM card without tearing
> > > > the card down. This is possible for connectors already (used by DP MST), so
> > > > add this possibility to DRM bridges as well.
> > > >
> > > > Implementation is based on drm_connector_init() as far as it makes sense,
> > > > and differs when it doesn't. A difference is that bridges are not exposed
> > > > to userspace, hence struct drm_bridge does not embed a struct
> > > > drm_mode_object which would provide the refcount. Instead we add to struct
> > > > drm_bridge a refcount field (we don't need other struct drm_mode_object
> > > > fields here) and instead of using the drm_mode_object_*() functions we
> > > > reimplement from those functions the few lines that drm_bridge needs for
> > > > refcounting.
> > > >
> > > > Also add a new devm_drm_bridge_alloc() macro to allocate a new refcounted
> > > > bridge.
> > > >
> > > > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> > >
> > > So, a couple of general comments:
> > >
> > > - I've said it a couple of times already, but I really think you're
> > > making it harder than necessary for you here. This (and only this!)
> > > should be the very first series you should be pushing. The rest can
> > > only ever work if that work goes through, and it's already hard enough
> > > as it is. So, split that patch into a series of its own, get that
> > > merged, and then we will be able to deal with panels conversion and
> > > whatever. That's even more true with panels since there's ongoing work
> > > that will make it easier for you too. So the best thing here is
> > > probably to wait.
> > >
> > > - This patch really needs to be split into several patches, something
> > > along the lines of:
> > >
> > > + Creating devm_drm_bridge_alloc()
> > > + Adding refcounting
> > > + Taking the references in all the needed places
> > > + Converting a bunch of drivers
> >
> > After reading Anusha's "[PATCH RFC 0/2] drm/panel: Refcounted panel
> > allocation" [0] I think I need a clarification about the 4 steps you had
> > outlined in the above quoted text. Are you suggesting those are four
> > _series_, and you'd want to see a series only creating
> > devm_drm_bridge_alloc() as a first step, similarly to Anusha's work?
> >
> > That was not my understanding so far, and so I've been working on a
> > series containing all 4 items, and it's growing very long due to item 3
> > needing to touch many dozen drivers which need to put a bridge (many
> > are identical oneliner patches though).
>
> I believe I've clarified it already in Anusha's series, but I think a
Yes, you have...
> reasonable series for *early* work would be the bullet points 1, 2, a
> bit of 3 and a bit of 4.
...but thanks for the extra clarification.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 15/26] drm/bridge: devm_drm_of_get_bridge and drmm_of_get_bridge: automatically put the bridge
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (13 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 14/26] drm/bridge: add support for refcounted DRM bridges Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 3:17 ` Dmitry Baryshkov
2025-02-06 18:14 ` [PATCH v6 16/26] drm/bridge: increment refcount in of_drm_find_bridge() Luca Ceresoli
` (11 subsequent siblings)
26 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
Add a devm/drmm action to these functions so the bridge reference is
dropped automatically when the caller is removed.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/drm_bridge.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index f694b32ca59cb91c32846bc00b43360df41cc1ad..497ec06dfcb05ab5dee8ea5e8f1eafb9c2807612 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -33,6 +33,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
#include <drm/drm_file.h>
+#include <drm/drm_managed.h>
#include <drm/drm_of.h>
#include <drm/drm_print.h>
@@ -1459,6 +1460,13 @@ static int of_drm_find_bridge_by_endpoint(const struct device_node *np,
return ret;
}
+static void devm_drm_bridge_put_void(void *data)
+{
+ struct drm_bridge *bridge = (struct drm_bridge *)data;
+
+ drm_bridge_put(bridge);
+}
+
/**
* devm_drm_of_get_bridge - Return next bridge in the chain
* @dev: device to tie the bridge lifetime to
@@ -1469,6 +1477,10 @@ static int of_drm_find_bridge_by_endpoint(const struct device_node *np,
* Given a DT node's port and endpoint number, finds the connected node
* and returns the associated bridge if any.
*
+ * The refcount of the returned bridge is incremented, but the caller does
+ * not have to call drm_bridge_put() when done with the bridge. It will be
+ * done by devres when @dev is removed.
+ *
* Returns a pointer to the bridge if successful, or an error pointer
* otherwise.
*/
@@ -1483,6 +1495,10 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
if (ret)
return ERR_PTR(ret);
+ ret = devm_add_action_or_reset(dev, devm_drm_bridge_put_void, bridge);
+ if (ret)
+ return ERR_PTR(ret);
+
return bridge;
}
EXPORT_SYMBOL(devm_drm_of_get_bridge);
@@ -1497,6 +1513,10 @@ EXPORT_SYMBOL(devm_drm_of_get_bridge);
* graph link search is not enough, e.g. for drivers that need to support
* panels described only as subnodes.
*
+ * The refcount of the returned bridge is incremented, but the caller does
+ * not have to call drm_bridge_put() when done with the bridge. It will be
+ * done by devres when @dev is removed.
+ *
* RETURNS:
* A pointer to the bridge if successful, or an error pointer otherwise.
*/
@@ -1513,10 +1533,21 @@ struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *dev,
if (!bridge)
return ERR_PTR(-ENODEV);
+ ret = devm_add_action_or_reset(dev, devm_drm_bridge_put_void, bridge);
+ if (ret)
+ return ERR_PTR(ret);
+
return bridge;
}
EXPORT_SYMBOL(devm_drm_of_get_bridge_by_node);
+static void drmm_bridge_put_void(struct drm_device *drm, void *ptr)
+{
+ struct drm_bridge *bridge = ptr;
+
+ drm_bridge_put(bridge);
+}
+
/**
* drmm_of_get_bridge - Return next bridge in the chain
* @drm: device to tie the bridge lifetime to
@@ -1527,6 +1558,10 @@ EXPORT_SYMBOL(devm_drm_of_get_bridge_by_node);
* Given a DT node's port and endpoint number, finds the connected node
* and returns the associated bridge if any.
*
+ * The refcount of the returned bridge is incremented, but the caller does
+ * not have to call drm_bridge_put() when done with the bridge. It will be
+ * done by drmm when @dev is removed.
+ *
* Returns a drmm managed pointer to the bridge if successful, or an error
* pointer otherwise.
*/
@@ -1541,6 +1576,10 @@ struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
if (ret)
return ERR_PTR(ret);
+ ret = drmm_add_action_or_reset(drm, drmm_bridge_put_void, bridge);
+ if (ret)
+ return ERR_PTR(ret);
+
return bridge;
}
EXPORT_SYMBOL(drmm_of_get_bridge);
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 15/26] drm/bridge: devm_drm_of_get_bridge and drmm_of_get_bridge: automatically put the bridge
2025-02-06 18:14 ` [PATCH v6 15/26] drm/bridge: devm_drm_of_get_bridge and drmm_of_get_bridge: automatically put the bridge Luca Ceresoli
@ 2025-02-07 3:17 ` Dmitry Baryshkov
2025-02-07 10:44 ` Luca Ceresoli
0 siblings, 1 reply; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 3:17 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Thu, Feb 06, 2025 at 07:14:30PM +0100, Luca Ceresoli wrote:
> Add a devm/drmm action to these functions so the bridge reference is
> dropped automatically when the caller is removed.
I think the get() should go to the underlying of_drm_bridge_find() function.
Also it really feels like it's an overkill to keep the wrappers. After
getting bridge being handled by the panel code would it be possible to
drop all of them? Then this patch might introduce one new devm_
function? Or are drmm_ functions actually being used to store data in
the drmm-managed memory?
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> This patch was added in v6.
> ---
> drivers/gpu/drm/drm_bridge.c | 39 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 39 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index f694b32ca59cb91c32846bc00b43360df41cc1ad..497ec06dfcb05ab5dee8ea5e8f1eafb9c2807612 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -33,6 +33,7 @@
> #include <drm/drm_edid.h>
> #include <drm/drm_encoder.h>
> #include <drm/drm_file.h>
> +#include <drm/drm_managed.h>
> #include <drm/drm_of.h>
> #include <drm/drm_print.h>
>
> @@ -1459,6 +1460,13 @@ static int of_drm_find_bridge_by_endpoint(const struct device_node *np,
> return ret;
> }
>
> +static void devm_drm_bridge_put_void(void *data)
> +{
> + struct drm_bridge *bridge = (struct drm_bridge *)data;
> +
> + drm_bridge_put(bridge);
> +}
> +
> /**
> * devm_drm_of_get_bridge - Return next bridge in the chain
> * @dev: device to tie the bridge lifetime to
> @@ -1469,6 +1477,10 @@ static int of_drm_find_bridge_by_endpoint(const struct device_node *np,
> * Given a DT node's port and endpoint number, finds the connected node
> * and returns the associated bridge if any.
> *
> + * The refcount of the returned bridge is incremented, but the caller does
> + * not have to call drm_bridge_put() when done with the bridge. It will be
> + * done by devres when @dev is removed.
> + *
> * Returns a pointer to the bridge if successful, or an error pointer
> * otherwise.
> */
> @@ -1483,6 +1495,10 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
> if (ret)
> return ERR_PTR(ret);
>
> + ret = devm_add_action_or_reset(dev, devm_drm_bridge_put_void, bridge);
> + if (ret)
> + return ERR_PTR(ret);
> +
> return bridge;
> }
> EXPORT_SYMBOL(devm_drm_of_get_bridge);
> @@ -1497,6 +1513,10 @@ EXPORT_SYMBOL(devm_drm_of_get_bridge);
> * graph link search is not enough, e.g. for drivers that need to support
> * panels described only as subnodes.
> *
> + * The refcount of the returned bridge is incremented, but the caller does
> + * not have to call drm_bridge_put() when done with the bridge. It will be
> + * done by devres when @dev is removed.
> + *
> * RETURNS:
> * A pointer to the bridge if successful, or an error pointer otherwise.
> */
> @@ -1513,10 +1533,21 @@ struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *dev,
> if (!bridge)
> return ERR_PTR(-ENODEV);
>
> + ret = devm_add_action_or_reset(dev, devm_drm_bridge_put_void, bridge);
> + if (ret)
> + return ERR_PTR(ret);
> +
> return bridge;
> }
> EXPORT_SYMBOL(devm_drm_of_get_bridge_by_node);
>
> +static void drmm_bridge_put_void(struct drm_device *drm, void *ptr)
> +{
> + struct drm_bridge *bridge = ptr;
> +
> + drm_bridge_put(bridge);
> +}
> +
> /**
> * drmm_of_get_bridge - Return next bridge in the chain
> * @drm: device to tie the bridge lifetime to
> @@ -1527,6 +1558,10 @@ EXPORT_SYMBOL(devm_drm_of_get_bridge_by_node);
> * Given a DT node's port and endpoint number, finds the connected node
> * and returns the associated bridge if any.
> *
> + * The refcount of the returned bridge is incremented, but the caller does
> + * not have to call drm_bridge_put() when done with the bridge. It will be
> + * done by drmm when @dev is removed.
> + *
> * Returns a drmm managed pointer to the bridge if successful, or an error
> * pointer otherwise.
> */
> @@ -1541,6 +1576,10 @@ struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
> if (ret)
> return ERR_PTR(ret);
>
> + ret = drmm_add_action_or_reset(drm, drmm_bridge_put_void, bridge);
> + if (ret)
> + return ERR_PTR(ret);
> +
> return bridge;
> }
> EXPORT_SYMBOL(drmm_of_get_bridge);
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 15/26] drm/bridge: devm_drm_of_get_bridge and drmm_of_get_bridge: automatically put the bridge
2025-02-07 3:17 ` Dmitry Baryshkov
@ 2025-02-07 10:44 ` Luca Ceresoli
2025-02-07 19:57 ` Dmitry Baryshkov
0 siblings, 1 reply; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-07 10:44 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Fri, 7 Feb 2025 05:17:43 +0200
Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> On Thu, Feb 06, 2025 at 07:14:30PM +0100, Luca Ceresoli wrote:
> > Add a devm/drmm action to these functions so the bridge reference is
> > dropped automatically when the caller is removed.
>
> I think the get() should go to the underlying of_drm_bridge_find() function.
It is done in the following patch.
Indeed I could swap patches 15 and 16 for clarity. Or I could squash
together patches 14+15+16, as they are various parts or the refcounted
bridge implementation, but I felt like keeping them separated would
help reviewing.
> Also it really feels like it's an overkill to keep the wrappers. After
> getting bridge being handled by the panel code would it be possible to
> drop all of them?
Do you mean having only drm_of_get_bridge_by_node(), without any devm
or drmm variant? I'm not sure it is a good idea. Most DRM code (well,
all of it, technically) is currently unable of working with refcounted
bridges, but if they use the devm variant they will put the ref when
they disappear.
> Then this patch might introduce one new devm_
> function? Or are drmm_ functions actually being used to store data in
> the drmm-managed memory?
Which devm function are you thinking about? Sorry, I'm not following
here.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 15/26] drm/bridge: devm_drm_of_get_bridge and drmm_of_get_bridge: automatically put the bridge
2025-02-07 10:44 ` Luca Ceresoli
@ 2025-02-07 19:57 ` Dmitry Baryshkov
2025-02-10 18:00 ` Luca Ceresoli
0 siblings, 1 reply; 81+ messages in thread
From: Dmitry Baryshkov @ 2025-02-07 19:57 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
On Fri, Feb 07, 2025 at 11:44:01AM +0100, Luca Ceresoli wrote:
> On Fri, 7 Feb 2025 05:17:43 +0200
> Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
>
> > On Thu, Feb 06, 2025 at 07:14:30PM +0100, Luca Ceresoli wrote:
> > > Add a devm/drmm action to these functions so the bridge reference is
> > > dropped automatically when the caller is removed.
> >
> > I think the get() should go to the underlying of_drm_bridge_find() function.
>
> It is done in the following patch.
>
> Indeed I could swap patches 15 and 16 for clarity. Or I could squash
> together patches 14+15+16, as they are various parts or the refcounted
> bridge implementation, but I felt like keeping them separated would
> help reviewing.
I think most of refcounting should be introduced as a single patch,
otherwise you risk having memory leaks or disappearing bridges.
>
> > Also it really feels like it's an overkill to keep the wrappers. After
> > getting bridge being handled by the panel code would it be possible to
> > drop all of them?
>
> Do you mean having only drm_of_get_bridge_by_node(), without any devm
> or drmm variant? I'm not sure it is a good idea. Most DRM code (well,
> all of it, technically) is currently unable of working with refcounted
> bridges, but if they use the devm variant they will put the ref when
> they disappear.
>
> > Then this patch might introduce one new devm_
> > function? Or are drmm_ functions actually being used to store data in
> > the drmm-managed memory?
>
> Which devm function are you thinking about? Sorry, I'm not following
> here.
drmm_of_get_bridge() / devm_of_get_bridge(). I have a feeling (which of
course might be wrong), that they were used somewhat randomly in some
cases.
>
> Luca
>
> --
> Luca Ceresoli, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 81+ messages in thread
* Re: [PATCH v6 15/26] drm/bridge: devm_drm_of_get_bridge and drmm_of_get_bridge: automatically put the bridge
2025-02-07 19:57 ` Dmitry Baryshkov
@ 2025-02-10 18:00 ` Luca Ceresoli
0 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-10 18:00 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Alexandre Belloni, Hervé Codina, Thomas Petazzoni, linux-doc,
Catalin Marinas, Paul Kocialkowski, dri-devel, Claudiu Beznea,
Laurent Pinchart, Andrzej Hajda, David Airlie, Fabio Estevam,
Marek Szyprowski, Simona Vetter, Robert Foss, Jonathan Corbet,
Sam Ravnborg, Jernej Skrabec, Daniel Thompson, Jagan Teki,
Jessica Zhang, Thomas Zimmermann, Will Deacon, Jonas Karlman,
Sascha Hauer, Maarten Lankhorst, Maxime Ripard, Inki Dae,
linux-arm-kernel, Neil Armstrong, Boris Brezillon, linux-kernel,
Paul Kocialkowski, Pengutronix Kernel Team, Shawn Guo
Hi Dmitry,
On Fri, 7 Feb 2025 21:57:30 +0200
Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> On Fri, Feb 07, 2025 at 11:44:01AM +0100, Luca Ceresoli wrote:
> > On Fri, 7 Feb 2025 05:17:43 +0200
> > Dmitry Baryshkov <dmitry.baryshkov@linaro.org> wrote:
> >
> > > On Thu, Feb 06, 2025 at 07:14:30PM +0100, Luca Ceresoli wrote:
> > > > Add a devm/drmm action to these functions so the bridge reference is
> > > > dropped automatically when the caller is removed.
> > >
> > > I think the get() should go to the underlying of_drm_bridge_find() function.
> >
> > It is done in the following patch.
> >
> > Indeed I could swap patches 15 and 16 for clarity. Or I could squash
> > together patches 14+15+16, as they are various parts or the refcounted
> > bridge implementation, but I felt like keeping them separated would
> > help reviewing.
>
> I think most of refcounting should be introduced as a single patch,
> otherwise you risk having memory leaks or disappearing bridges.
In principle there is no need to add all of this atomically in a single
commit, provided that all patches adding refcounting in the
infrastructure code is applied before patches to drivers using
refcounting.
> > > Also it really feels like it's an overkill to keep the wrappers. After
> > > getting bridge being handled by the panel code would it be possible to
> > > drop all of them?
> >
> > Do you mean having only drm_of_get_bridge_by_node(), without any devm
> > or drmm variant? I'm not sure it is a good idea. Most DRM code (well,
> > all of it, technically) is currently unable of working with refcounted
> > bridges, but if they use the devm variant they will put the ref when
> > they disappear.
> >
> > > Then this patch might introduce one new devm_
> > > function? Or are drmm_ functions actually being used to store data in
> > > the drmm-managed memory?
> >
> > Which devm function are you thinking about? Sorry, I'm not following
> > here.
>
> drmm_of_get_bridge() / devm_of_get_bridge(). I have a feeling (which of
> course might be wrong), that they were used somewhat randomly in some
> cases.
Again not sure I get you, sorry. What's wrong in
devm_drm_of_get_bridge(), and what would the new function you proposed
be doing? I think a couple lines of pseudocode would clarify this.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread
* [PATCH v6 16/26] drm/bridge: increment refcount in of_drm_find_bridge()
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (14 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 15/26] drm/bridge: devm_drm_of_get_bridge and drmm_of_get_bridge: automatically put the bridge Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-06 18:14 ` [PATCH v6 17/26] drm/bridge: add devm_drm_put_bridge() and devm_drm_put_and_clear_bridge() Luca Ceresoli
` (10 subsequent siblings)
26 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
of_drm_find_bridge() returns a pointer to a bridge, so it has to get a
reference and the caller will be in charge of putting it.
Callers of of_drm_find_bridge() are:
1. drm_of_panel_bridge_remove()
2. of_drm_find_bridge_by_endpoint()
3. drm_of_find_panel_or_bridge()
4. various DRM drivers
Add the corresponding drm_bridge_put() call for 1 and propagate
documentation to 2 and 3.
Other callers (4) are to be adapted in following commits.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/bridge/panel.c | 3 +++
drivers/gpu/drm/drm_bridge.c | 7 +++++++
include/drm/drm_of.h | 1 +
3 files changed, 11 insertions(+)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 1230ae50b2020e7a9306cac83009dd600dd61d26..3c0e22e61c1092de1571d800ac440aad7b5c86bc 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -363,6 +363,9 @@ EXPORT_SYMBOL(of_drm_find_panel);
* return either the associated struct drm_panel or drm_bridge device. Either
* @panel or @bridge must not be NULL.
*
+ * If a bridge is returned in @bridge, the bridge refcount is
+ * incremented. Use drm_bridge_put() when done with the bridge.
+ *
* This function is deprecated and should not be used in new drivers. Use
* devm_drm_of_get_bridge() instead.
*
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 497ec06dfcb05ab5dee8ea5e8f1eafb9c2807612..fca860d582f86b35c9172b27be20060de086e38f 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -1396,6 +1396,9 @@ EXPORT_SYMBOL_GPL(drm_bridge_hpd_notify);
*
* @np: device node
*
+ * On return the bridge refcount is incremented (if the bridge is
+ * refcounted). Use drm_bridge_put() when done with the bridge.
+ *
* RETURNS:
* drm_bridge control struct on success, NULL on failure
*/
@@ -1407,6 +1410,7 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np)
list_for_each_entry(bridge, &bridge_list, list) {
if (bridge->of_node == np) {
+ drm_bridge_get(bridge);
mutex_unlock(&bridge_lock);
return bridge;
}
@@ -1427,6 +1431,9 @@ EXPORT_SYMBOL(of_drm_find_bridge);
* Given a DT node's port and endpoint number, find the connected node and
* return the associated struct drm_bridge.
*
+ * On success the returned @bridge refcount is incremented. Use
+ * drm_bridge_put() when done with the bridge.
+ *
* Returns zero if successful, or one of the standard error codes if it fails.
*/
static int of_drm_find_bridge_by_endpoint(const struct device_node *np,
diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h
index 7f0256dae3f13de1d4109e9265d66684ef2a08ee..948672c27d2eb3034b2519e0bba0fcb52d5c697b 100644
--- a/include/drm/drm_of.h
+++ b/include/drm/drm_of.h
@@ -172,6 +172,7 @@ static inline int drm_of_panel_bridge_remove(const struct device_node *np,
bridge = of_drm_find_bridge(remote);
drm_panel_bridge_remove(bridge);
+ drm_bridge_put(bridge);
return 0;
#else
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v6 17/26] drm/bridge: add devm_drm_put_bridge() and devm_drm_put_and_clear_bridge()
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (15 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 16/26] drm/bridge: increment refcount in of_drm_find_bridge() Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-06 18:14 ` [PATCH v6 18/26] drm/bridge: add documentation of refcounted bridges Luca Ceresoli
` (9 subsequent siblings)
26 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
Bridges obtained via devm_drm_of_get_bridge() will be put when the
requesting device is removed. However drivers which obtained them may need
to put the obtained reference way before being destroyed, especially in
case of hot-unplug of the bridge they hold a reference to.
Add devm_drm_put_bridge() to manually release a devm-obtained bridge.
Also add a macro to additionally clear the pointer in a safe way.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/drm_bridge.c | 36 ++++++++++++++++++++++++++++++++++++
include/drm/drm_bridge.h | 6 ++++++
2 files changed, 42 insertions(+)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index fca860d582f86b35c9172b27be20060de086e38f..92ce40adfaa59a278a972ac862bebee06970ff83 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -1548,6 +1548,42 @@ struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *dev,
}
EXPORT_SYMBOL(devm_drm_of_get_bridge_by_node);
+/**
+ * devm_drm_put_bridge - Release a bridge reference obtained via devm
+ * @dev: device that got the bridge via devm
+ * @bridge: pointer to a struct drm_bridge obtained via devm
+ *
+ * Same as drm_bridge_put() for bridge pointers obtained via devm functions
+ * such as devm_drm_of_get_bridge().
+ *
+ * See also devm_drm_put_and_clear_bridge() which is more handy in many
+ * cases.
+ */
+void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge)
+{
+ devm_release_action(dev, devm_drm_bridge_put_void, bridge);
+}
+EXPORT_SYMBOL(devm_drm_put_bridge);
+
+/**
+ * devm_drm_put_and_clear_bridge - Given a bridge pointer obained via devm,
+ * clear the pointer then put the bridge
+ *
+ * @dev: device that got the bridge via devm
+ * @bridge_pp: pointer to pointer to a struct drm_bridge obtained via devm
+ *
+ * Same as drm_bridge_put_and_clear() for bridge pointers obtained via devm
+ * functions such as devm_drm_of_get_bridge().
+ */
+void devm_drm_put_and_clear_bridge(struct device *dev, struct drm_bridge **bridge_pp)
+{
+ struct drm_bridge *bridge = *bridge_pp;
+
+ *bridge_pp = NULL;
+ devm_drm_put_bridge(dev, bridge);
+}
+EXPORT_SYMBOL(devm_drm_put_and_clear_bridge);
+
static void drmm_bridge_put_void(struct drm_device *drm, void *ptr)
{
struct drm_bridge *bridge = ptr;
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 43cef0f6ccd36034f64ad2babfebea62db1d9e43..b6b76161a3c6bb2a4df4b3331bc152a560823fd7 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -1230,6 +1230,8 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, struct device_node
u32 port, u32 endpoint);
struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *dev,
struct device_node *bridge_node);
+void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge);
+void devm_drm_put_and_clear_bridge(struct device *dev, struct drm_bridge **bridge_pp);
struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, struct device_node *node,
u32 port, u32 endpoint);
#else
@@ -1247,6 +1249,10 @@ static inline struct drm_bridge *devm_drm_of_get_bridge_by_node(struct device *d
return ERR_PTR(-ENODEV);
}
+static inline void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge) {}
+static inline void devm_drm_put_and_clear_bridge(struct device *dev,
+ struct drm_bridge **bridge_pp) {}
+
static inline struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
struct device_node *node,
u32 port,
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v6 18/26] drm/bridge: add documentation of refcounted bridges
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (16 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 17/26] drm/bridge: add devm_drm_put_bridge() and devm_drm_put_and_clear_bridge() Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-06 18:14 ` [PATCH v6 19/26] drm/tests: bridge: add KUnit tests for DRM bridges (init and destroy) Luca Ceresoli
` (8 subsequent siblings)
26 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
Document in detail the new refcounted bridges as well as the "legacy"
bridges.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
Changes in v6:
- update to the new devm_drm_bridge_alloc() API
- rewrite and improve various sentences for clarity
- fix typos (Randy Dunlap)
This patch was added in v5.
---
Documentation/gpu/drm-kms-helpers.rst | 6 ++
drivers/gpu/drm/drm_bridge.c | 118 ++++++++++++++++++++++++++++++++++
2 files changed, 124 insertions(+)
diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst
index 79c8d3e63f7e06136440ed38972697b5f057d5d1..027c6ab65aa5c3848c4afab6fbc8ab93f9a285ba 100644
--- a/Documentation/gpu/drm-kms-helpers.rst
+++ b/Documentation/gpu/drm-kms-helpers.rst
@@ -151,6 +151,12 @@ Overview
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: overview
+Bridge lifecycle
+----------------
+
+.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
+ :doc: bridge lifecycle
+
Display Driver Integration
--------------------------
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 92ce40adfaa59a278a972ac862bebee06970ff83..fc44a5d227a89a12b5c3299a29776cfddb36ce27 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -62,6 +62,124 @@
* encoder chain.
*/
+/**
+ * DOC: bridge lifecycle
+ *
+ * Allocation, initialization and teardown of a bridge can be implemented
+ * in one of two ways: *refcounted* mode and *legacy* mode.
+ *
+ * In **refcounted** mode:
+ *
+ * - each &struct drm_bridge is reference counted since its allocation
+ * - any code taking a pointer to a bridge has get and put APIs to refcount
+ * it and so ensure the bridge won't be deallocated while there is still
+ * a reference to it
+ * - the driver implementing the bridge also holds a reference, but the
+ * allocated struct can survive it
+ * - deallocation is done when the last put happens, dropping the refcount
+ * to zero
+ *
+ * A bridge using refcounted mode is called a *refcounted bridge*.
+ *
+ * In **legacy** mode the &struct drm_bridge lifetime is tied to the device
+ * instantiating it: it is allocated on probe and freed on removal. Any
+ * other kernel code holding a pointer to the bridge could incur in
+ * use-after-free in case the bridge is deallocated at runtime.
+ *
+ * Legacy mode used to be the only one until refcounted bridges were
+ * introduced, hence the name. It is still fine in case the bridges are a
+ * fixed part of the pipeline, i.e. if the bridges are removed only when
+ * tearing down the entire card. Refcounted bridges support both that case
+ * and the case of more dynamic hardware with bridges that can be removed
+ * at runtime without tearing down the entire card.
+ *
+ * Usage of refcounted bridges happens in two sides: the bridge *provider*
+ * and the bridge *consumers*. The bridge provider is the driver
+ * implementing the bridge. The bridge consumers are all parts of the
+ * kernel taking a &struct drm_bridge pointer, including other bridges,
+ * encoders and the DRM core.
+ *
+ * For bridge **providers**, in both refcounted and legacy modes the common
+ * and expected pattern is that the bridge driver declares a
+ * driver-specific struct embedding a &struct drm_bridge. E.g.::
+ *
+ * struct my_bridge {
+ * ...
+ * struct drm_bridge bridge;
+ * ...
+ * };
+ *
+ * When using refcounted mode, the driver should allocate and initialize
+ * ``struct my_bridge`` using devm_drm_bridge_alloc(), as in this example::
+ *
+ * static int my_bridge_probe(...)
+ * {
+ * struct device *dev = ...;
+ * struct my_bridge *mybr;
+ *
+ * mybr = devm_drm_bridge_alloc(dev, struct my_bridge, bridge, &my_bridge_funcs);
+ * if (IS_ERR(mybr))
+ * return PTR_ERR(mybr);
+ *
+ * // Get resources, initialize my_bridge members...
+ * drm_bridge_add();
+ * ...
+ * }
+ *
+ * static void my_bridge_remove()
+ * {
+ * struct my_bridge *mybr = ...;
+ *
+ * drm_bridge_remove(&mybr->bridge);
+ * // Free resources
+ * // ... NO kfree here!
+ * }
+ *
+ * In legacy mode, the driver can either use ``devm_`` allocation or
+ * equivalently free ``struct my_bridge`` in their remove function::
+ *
+ * static int my_bridge_probe(...)
+ * {
+ * struct device *dev = ...;
+ * struct my_bridge *mybr;
+ *
+ * mybr = devm_kzalloc(dev, sizeof(*mybr), GFP_KERNEL);
+ * if (!mybr)
+ * return -ENOMEM;
+ *
+ * // Get resources, initialize my_bridge members...
+ * mybr->funcs = &my_bridge_funcs;
+ * drm_bridge_add();
+ * ...
+ * }
+ *
+ * static void my_bridge_remove()
+ * {
+ * struct my_bridge *mybr = ...;
+ *
+ * drm_bridge_remove(&mybr->bridge);
+ * // Free resources
+ * // kfree(mybr) if not using devm_*() for allocation
+ * }
+ *
+ * Bridge **consumers** need to handle the case of a bridge being removed
+ * while they have a pointer to it. As this can happen at any time, such
+ * code can incur in use-after-free. To avoid that, consumers have to call
+ * drm_bridge_get() when taking a pointer and drm_bridge_put() after they
+ * are done using it. This will extend the allocation lifetime of the
+ * bridge struct until the last reference has been put, potentially a long
+ * time after the bridge device has been removed from the kernel.
+ *
+ * Functions that return a pointer to a bridge, such as
+ * of_drm_find_bridge(), internally call drm_bridge_get() on the bridge
+ * they are about to return, so in this case the consumers do not have to
+ * do it.
+ *
+ * Calling drm_bridge_get() and drm_bridge_put() on a bridge that is not
+ * refcounted does nothing, so code using these two APIs will work both on
+ * refcounted bridges and legacy bridges.
+ */
+
/**
* DOC: display driver integration
*
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v6 19/26] drm/tests: bridge: add KUnit tests for DRM bridges (init and destroy)
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (17 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 18/26] drm/bridge: add documentation of refcounted bridges Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-06 18:14 ` [PATCH v6 20/26] drm/debugfs: bridges_show: show refcount Luca Ceresoli
` (7 subsequent siblings)
26 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
Add a basic KUnit test for the newly introduced drm_bridge_alloc().
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
Changed in v6:
- update to new drm_bridge_alloc() API
- remove drm_test_drm_bridge_put test, not straightforward to write with
the new API and the current notification mechanism
- do not allocate a drm_device: a bridge is allocated without one
- rename some identifiers for easier code reading
This patch was added in v5.
---
drivers/gpu/drm/tests/Makefile | 1 +
drivers/gpu/drm/tests/drm_bridge_test.c | 72 +++++++++++++++++++++++++++++++++
2 files changed, 73 insertions(+)
diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile
index 56dab563abd7a7ee7c147bd6b4927e2436b82e1d..909f98a132bb1d057b2666e8b891683ffb11cca4 100644
--- a/drivers/gpu/drm/tests/Makefile
+++ b/drivers/gpu/drm/tests/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST_HELPERS) += \
drm_kunit_helpers.o
obj-$(CONFIG_DRM_KUNIT_TEST) += \
+ drm_bridge_test.o \
drm_buddy_test.o \
drm_cmdline_parser_test.o \
drm_connector_test.o \
diff --git a/drivers/gpu/drm/tests/drm_bridge_test.c b/drivers/gpu/drm/tests/drm_bridge_test.c
new file mode 100644
index 0000000000000000000000000000000000000000..dc5e9260cedaa29126fd6af25a6ba2f6eee05a87
--- /dev/null
+++ b/drivers/gpu/drm/tests/drm_bridge_test.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Kunit test for DRM bridges
+ */
+
+#include <drm/drm_bridge.h>
+
+#include <kunit/device.h>
+#include <kunit/test.h>
+
+struct drm_bridge_test_ctx {
+ struct device *dev;
+};
+
+/*
+ * Mimick the typical struct defined by a bridge driver, which embeds a
+ * bridge plus other fields.
+ */
+struct dummy_drm_bridge {
+ int dummy; // ensure we test non-zero @bridge offset
+ struct drm_bridge bridge;
+};
+
+static const struct drm_bridge_funcs drm_bridge_dummy_funcs = {
+};
+
+static int drm_test_bridge_init(struct kunit *test)
+{
+ struct drm_bridge_test_ctx *ctx;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ ctx->dev = kunit_device_register(test, "drm-bridge-dev");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->dev);
+
+ test->priv = ctx;
+ return 0;
+}
+
+/*
+ * Test that the allocation and initialization of a bridge works as
+ * expected and doesn't report any error.
+ */
+static void drm_test_drm_bridge_alloc(struct kunit *test)
+{
+ struct drm_bridge_test_ctx *ctx = test->priv;
+ struct dummy_drm_bridge *dummy;
+
+ dummy = devm_drm_bridge_alloc(ctx->dev, struct dummy_drm_bridge, bridge,
+ &drm_bridge_dummy_funcs);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy);
+}
+
+static struct kunit_case drm_bridge_alloc_tests[] = {
+ KUNIT_CASE(drm_test_drm_bridge_alloc),
+ { }
+};
+
+static struct kunit_suite drm_bridge_alloc_test_suite = {
+ .name = "drm_bridge_alloc",
+ .init = drm_test_bridge_init,
+ .test_cases = drm_bridge_alloc_tests,
+};
+
+kunit_test_suites(
+ &drm_bridge_alloc_test_suite,
+);
+
+MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
+MODULE_DESCRIPTION("Kunit test for drm_bridge functions");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v6 20/26] drm/debugfs: bridges_show: show refcount
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (18 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 19/26] drm/tests: bridge: add KUnit tests for DRM bridges (init and destroy) Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-06 18:14 ` [PATCH v6 21/26] drm/bridge: add list of removed refcounted bridges Luca Ceresoli
` (6 subsequent siblings)
26 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
Now that bridges are optionally refcounted, it is useful to know about that
in debugfs.
Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/drm_debugfs.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index 7424d5237e7615d63de6bba572ee6050da6709d0..629074247ffec4fa18df7af2d9023255abed501c 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -743,6 +743,12 @@ void drm_debugfs_crtc_remove(struct drm_crtc *crtc)
static void bridge_print(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx)
{
drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs);
+
+ if (drm_bridge_is_refcounted(bridge))
+ drm_printf(p, "\trefcount: %u\n", kref_read(&bridge->refcount));
+ else
+ drm_printf(p, "\trefcount: N/A\n");
+
drm_printf(p, "\ttype: [%d] %s\n",
bridge->type,
drm_get_connector_type_name(bridge->type));
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v6 21/26] drm/bridge: add list of removed refcounted bridges
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (19 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 20/26] drm/debugfs: bridges_show: show refcount Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-06 18:14 ` [PATCH v6 22/26] drm/debugfs: show removed bridges Luca Ceresoli
` (5 subsequent siblings)
26 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
Between drm_bridge_add() and drm_bridge_remove() bridges "published" to the
DRM core via the global bridge_list and visible in
/sys/kernel/debug/dri/bridges. However between drm_bridge_remove() and the
last drm_bridge_put(), a refcounted bridge memory is still allocated even
though the bridge is not "published", i.e. not in bridges_list, so it is
invisible in debugfs. This prevents debugging refcounted bridges lifetime,
especially leaks doe to any missing drm_bridge_put().
In order to allow debugfs to also show the removed refcounted bridges, move
such bridges into a new ad-hoc list until they are freed.
Note this requires adding INIT_LIST_HEAD(&bridge->list) in the bridge
initialization code: the lack of such init was not exposing any bug so far,
but it would with the new code.
Document the new behaviour of drm_bridge_remove() and update the
drm_bridge_add() documentation to stay consistent.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/drm_bridge.c | 23 ++++++++++++++++++++---
drivers/gpu/drm/drm_internal.h | 1 +
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index fc44a5d227a89a12b5c3299a29776cfddb36ce27..b315c7c4e32cc27723c69c795efe4f3313134204 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -315,9 +315,10 @@
* driver.
*/
-/* Protect bridge_list */
+/* Protect bridge_list and bridge_removed_list */
DEFINE_MUTEX(bridge_lock);
LIST_HEAD(bridge_list);
+LIST_HEAD(bridge_removed_list);
/* Internal function (for refcounted bridges) */
void __drm_bridge_free(struct kref *kref)
@@ -327,6 +328,10 @@ void __drm_bridge_free(struct kref *kref)
DRM_DEBUG("bridge=%p, container=%p FREE\n", bridge, container);
+ mutex_lock(&bridge_lock);
+ list_del(&bridge->list);
+ mutex_unlock(&bridge_lock);
+
kfree(container);
}
EXPORT_SYMBOL(__drm_bridge_free);
@@ -355,6 +360,8 @@ void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
return ERR_PTR(-ENOMEM);
bridge = container + offset;
+
+ INIT_LIST_HEAD(&bridge->list);
bridge->container_offset = offset;
bridge->funcs = funcs;
kref_init(&bridge->refcount);
@@ -371,7 +378,10 @@ void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
EXPORT_SYMBOL(__devm_drm_bridge_alloc);
/**
- * drm_bridge_add - add the given bridge to the global bridge list
+ * drm_bridge_add - add to published bridges
+ *
+ * Adds the given bridge to the global bridge list, so it can be found by
+ * of_drm_find_bridge().
*
* @bridge: bridge control structure
*/
@@ -423,7 +433,12 @@ int devm_drm_bridge_add(struct device *dev, struct drm_bridge *bridge)
EXPORT_SYMBOL(devm_drm_bridge_add);
/**
- * drm_bridge_remove - remove the given bridge from the global bridge list
+ * drm_bridge_remove - remove from published bridges
+ *
+ * Remove the given bridge from the global bridge list, so it won't be
+ * found by of_drm_find_bridge(). If the bridge is refcounted it also adds
+ * it to the removed bridge list, to keep track of removed bridges until
+ * their allocated memory is finally freed.
*
* @bridge: bridge control structure
*/
@@ -435,6 +450,8 @@ void drm_bridge_remove(struct drm_bridge *bridge)
mutex_lock(&bridge_lock);
list_del_init(&bridge->list);
+ if (drm_bridge_is_refcounted(bridge))
+ list_add_tail(&bridge->list, &bridge_removed_list);
mutex_unlock(&bridge_lock);
list_for_each_entry_safe(br, tmp, &bridge_list, list)
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index b6e875d4b25faae6bb0bb952c3c12bd4819698ec..bbfc938b21c13bc69c36d3833f6cb6d5d22d1c54 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -51,6 +51,7 @@ struct drm_vblank_crtc;
// for drm_debugfs.c
extern struct mutex bridge_lock;
extern struct list_head bridge_list;
+extern struct list_head bridge_removed_list;
/* drm_client_event.c */
#if defined(CONFIG_DRM_CLIENT)
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v6 22/26] drm/debugfs: show removed bridges
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (20 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 21/26] drm/bridge: add list of removed refcounted bridges Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-06 18:14 ` [PATCH v6 23/26] drm/bridge: samsung-dsim: use refcounting for the out_bridge Luca Ceresoli
` (4 subsequent siblings)
26 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
The usefulness of /sys/kernel/debug/dri/bridges is limited as it only shows
bridges betweeb drm_bridge_add() and drm_bridge_remove(). However
refcounted bridges can stay allocated for way longer, and a memory leak due
to a missing drm_bridge_put() would not be visible in this debugfs file.
Add removed bridges to the /sys/kernel/debug/dri/bridges output.
Now however if a bridge is added and removed multiple times (as in
hot-pluggable hardware) and not freed immediately, there would be multiple
identical entries for the same bridge model/driver. To distinguish between
each instance add the bridge pointer to the output.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch was added in v6.
---
drivers/gpu/drm/drm_debugfs.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index 629074247ffec4fa18df7af2d9023255abed501c..b04e1fba8faf7320794277c2017c97216320f017 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -740,12 +740,16 @@ void drm_debugfs_crtc_remove(struct drm_crtc *crtc)
crtc->debugfs_entry = NULL;
}
-static void bridge_print(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx)
+static void bridge_print(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx,
+ bool removed)
{
drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs);
+ drm_printf(p, "\taddr: %p\n", bridge);
+
if (drm_bridge_is_refcounted(bridge))
- drm_printf(p, "\trefcount: %u\n", kref_read(&bridge->refcount));
+ drm_printf(p, "\trefcount: %u%s\n", kref_read(&bridge->refcount),
+ removed ? " [removed]" : "");
else
drm_printf(p, "\trefcount: N/A\n");
@@ -753,7 +757,8 @@ static void bridge_print(struct drm_printer *p, struct drm_bridge *bridge, unsig
bridge->type,
drm_get_connector_type_name(bridge->type));
- if (bridge->of_node)
+ /* The OF node could be freed after drm_bridge_remove() */
+ if (bridge->of_node && !removed)
drm_printf(p, "\tOF: %pOFfc\n", bridge->of_node);
drm_printf(p, "\tops: [0x%x]", bridge->ops);
@@ -778,7 +783,7 @@ static int bridges_show(struct seq_file *m, void *data)
unsigned int idx = 0;
drm_for_each_bridge_in_chain(encoder, bridge)
- bridge_print(&p, bridge, idx++);
+ bridge_print(&p, bridge, idx++, false);
return 0;
}
@@ -822,7 +827,10 @@ static int allbridges_show(struct seq_file *m, void *data)
mutex_lock(&bridge_lock);
list_for_each_entry(bridge, &bridge_list, list)
- bridge_print(&p, bridge, idx++);
+ bridge_print(&p, bridge, idx++, false);
+
+ list_for_each_entry(bridge, &bridge_removed_list, list)
+ bridge_print(&p, bridge, idx++, true);
mutex_unlock(&bridge_lock);
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v6 23/26] drm/bridge: samsung-dsim: use refcounting for the out_bridge
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (21 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 22/26] drm/debugfs: show removed bridges Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-06 18:14 ` [PATCH v6 24/26] drm/bridge: panel: use dynamic lifetime management Luca Ceresoli
` (3 subsequent siblings)
26 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
In case the samsung-dsim is fixed and the following bridge is
hot-unplugged, we need to handle the dynamic lifetime of the following
bridge by putting the reference when disposing of it.
The devm functions used to get the next bridge reference will put it only
when this device is removed. Do it explicitly on detach and in the error
paths.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
Changed in v6:
- use new devm_drm_put[_and_clear]_bridge()
This patch was added in v5.
---
drivers/gpu/drm/bridge/samsung-dsim.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
index bbd0a4f5a3f52b61bf48f10d6e8ca741bffa5e46..8e94f099d67bee93655129625b40d4c1af023fcc 100644
--- a/drivers/gpu/drm/bridge/samsung-dsim.c
+++ b/drivers/gpu/drm/bridge/samsung-dsim.c
@@ -1732,13 +1732,13 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) {
ret = samsung_dsim_register_te_irq(dsi, &device->dev);
if (ret)
- return ret;
+ goto err_devm_put_bridge;
}
if (pdata->host_ops && pdata->host_ops->attach) {
ret = pdata->host_ops->attach(dsi, device);
if (ret)
- return ret;
+ goto err_devm_put_bridge;
}
dsi->lanes = device->lanes;
@@ -1748,6 +1748,10 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
dsi->out_bridge = out_bridge;
return 0;
+
+err_devm_put_bridge:
+ devm_drm_put_bridge(dev, out_bridge);
+ return ret;
}
static void samsung_dsim_unregister_te_irq(struct samsung_dsim *dsi)
@@ -1764,7 +1768,7 @@ static int samsung_dsim_host_detach(struct mipi_dsi_host *host,
struct samsung_dsim *dsi = host_to_dsi(host);
const struct samsung_dsim_plat_data *pdata = dsi->plat_data;
- dsi->out_bridge = NULL;
+ devm_drm_put_and_clear_bridge(dsi->dev, &dsi->out_bridge);
if (pdata->host_ops && pdata->host_ops->detach)
pdata->host_ops->detach(dsi, device);
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v6 24/26] drm/bridge: panel: use dynamic lifetime management
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (22 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 23/26] drm/bridge: samsung-dsim: use refcounting for the out_bridge Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-06 18:14 ` [PATCH v6 25/26] drm/bridge: ti-sn65dsi83: " Luca Ceresoli
` (2 subsequent siblings)
26 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
Allow this bridge to be removable without dangling pointers and
use-after-free, together with proper use of drm_bridge_get() and _put() by
consumers.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
Changed in v6:
- Update to use devm_drm_bridge_alloc(), remove .destroy
- Update the *_of_get_bridge() functions to put instead of kfree on
devm/drmm events
This patch was added in v5.
---
drivers/gpu/drm/bridge/panel.c | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 3c0e22e61c1092de1571d800ac440aad7b5c86bc..5950c752ff2469f7e5c28f9d5833141551b98227 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -912,15 +912,14 @@ struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
if (!panel)
return ERR_PTR(-EINVAL);
- panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge),
- GFP_KERNEL);
- if (!panel_bridge)
- return ERR_PTR(-ENOMEM);
+ panel_bridge = devm_drm_bridge_alloc(panel->dev, struct panel_bridge, bridge,
+ &panel_bridge_bridge_funcs);
+ if (IS_ERR(panel_bridge))
+ return (void *)panel_bridge;
panel_bridge->connector_type = connector_type;
panel_bridge->panel = panel;
- panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs;
panel_bridge->bridge.of_node = panel->dev->of_node;
panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES;
panel_bridge->bridge.type = connector_type;
@@ -939,8 +938,6 @@ EXPORT_SYMBOL(drm_panel_bridge_add_typed);
*/
void drm_panel_bridge_remove(struct drm_bridge *bridge)
{
- struct panel_bridge *panel_bridge;
-
if (!bridge)
return;
@@ -949,10 +946,7 @@ void drm_panel_bridge_remove(struct drm_bridge *bridge)
return;
}
- panel_bridge = drm_bridge_to_panel_bridge(bridge);
-
drm_bridge_remove(bridge);
- devm_kfree(panel_bridge->panel->dev, bridge);
}
EXPORT_SYMBOL(drm_panel_bridge_remove);
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v6 25/26] drm/bridge: ti-sn65dsi83: use dynamic lifetime management
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (23 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 24/26] drm/bridge: panel: use dynamic lifetime management Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-06 18:14 ` [PATCH v6 26/26] drm/bridge: hotplug-bridge: add driver to support hot-pluggable DSI bridges Luca Ceresoli
2025-02-07 9:01 ` [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
26 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
Allow this bridge to be removable without dangling pointers and
use-after-free, together with proper use of drm_bridge_get() and _put() by
consumers.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
Changed in v6:
- Update to use devm_drm_bridge_alloc(), remove .destroy
This patch was added in v5.
---
drivers/gpu/drm/bridge/ti-sn65dsi83.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
index e683b62d340564978bda37698f2cb1bffd961df5..14e991e999db857725f4c46de3e4c4e4861c3c8a 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
@@ -941,9 +941,9 @@ static int sn65dsi83_probe(struct i2c_client *client)
struct sn65dsi83 *ctx;
int ret;
- ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
+ ctx = devm_drm_bridge_alloc(dev, struct sn65dsi83, bridge, &sn65dsi83_funcs);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
ctx->dev = dev;
INIT_WORK(&ctx->reset_work, sn65dsi83_reset_work);
@@ -983,7 +983,6 @@ static int sn65dsi83_probe(struct i2c_client *client)
dev_set_drvdata(dev, ctx);
i2c_set_clientdata(client, ctx);
- ctx->bridge.funcs = &sn65dsi83_funcs;
ctx->bridge.of_node = dev->of_node;
ctx->bridge.pre_enable_prev_first = true;
drm_bridge_add(&ctx->bridge);
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* [PATCH v6 26/26] drm/bridge: hotplug-bridge: add driver to support hot-pluggable DSI bridges
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (24 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 25/26] drm/bridge: ti-sn65dsi83: " Luca Ceresoli
@ 2025-02-06 18:14 ` Luca Ceresoli
2025-02-07 9:01 ` [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
26 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-06 18:14 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski,
Luca Ceresoli
This driver implements the physical connector on a video pipeline allowing
to remove of all the following bridges up to the panel.
The DRM subsystem currently allows hotplug of the monitor but not preceding
components. However there are embedded devices where the "tail" of the DRM
pipeline, including one or more bridges, can be physically removed:
.------------------------.
| DISPLAY CONTROLLER |
| .---------. .------. |
| | ENCODER |<--| CRTC | |
| '---------' '------' |
'------|-----------------'
|
| HOTPLUG
V CONNECTOR
.---------. .--. .-. .---------. .-------.
| 0 to N | | _| _| | | 1 to N | | |
| BRIDGES |--DSI-->||_ |_ |--DSI-->| BRIDGES |--LVDS-->| PANEL |
| | | | | | | | | |
'---------' '--' '-' '---------' '-------'
[--- fixed components --] [----------- removable add-on -----------]
This driver supports such a device, where the final segment of a MIPI DSI
bus, including one or more bridges, can be physically disconnected and
reconnected at runtime, possibly with a different model.
The add-on supported by this driver has a MIPI DSI bus traversing the
hotplug connector and a DSI to LVDS bridge and an LVDS panel on the add-on.
Hovever this driver is designed to be as far as possible generic and
extendable to other busses that have no native hotplug and model ID
discovery.
This driver does not itself add and remove the bridges or panel on the
add-on: this needs to be done by other means, e.g. device tree overlay
runtime insertion and removal. The hotplug-bridge gets notified by the DRM
bridge core after a removable bridge gets added or before it is removed.
The hotplug-bridge role is to implement the "hot-pluggable connector" in
the bridge chain. In this position, what the hotplug-bridge should ideally
do is:
* communicate with the previous component (bridge or encoder) so that it
believes it always has a connected bridge following it and the DRM card
is always present
* be notified of the addition and removal of the following bridge and
attach/detach to/from it
* communicate with the following bridge so that it will attach and detach
using the normal procedure (as if the entire pipeline were being created
or destroyed, not only the tail)
* add/remove an LVDS connector on hot(un)plug, similarly to what the
DisplayPort MST code does
However some aspects make it a bit more complex than that. Most notably:
* the next bridge can be probed and removed at any moment and all probing
sequences need to be handled
* the DSI host/device registration process, which adds to the DRM bridge
attach process, makes the initial card registration tricky
* the need to register and deregister the following bridges at runtime
without tearing down the whole DRM card prevents using some of the
functions that are normally recommended
* the automatic mechanism to call the appropriate .get_modes operation
(typically provided by the panel bridge) cannot work as the panel can
disappear and reappear as a different model, so an ad-hoc lookup is
needed
The code handling these and other tricky aspects is accurately documented
by comments in the code.
Additionally this driver is adding a DSI connector representing the video
lines of the hotplug connector; the status is always "disconnected" (no
panel is ever attached directly to it). This is supposed to disappear but
it is still present for now. For this reason the userspace representation
when the add-on is not connected is:
# modetest -c | grep -i '^[a-z0-9]'
Connectors:
id encoder status name size (mm) modes encoders
38 0 disconnected DSI-1 0x0 0 37
And when it is connected, when the new connector appears, it becomes:
# modetest -c | grep -i '^[a-z0-9]'
Connectors:
id encoder status name size (mm) modes encoders
38 0 disconnected DSI-1 0x0 0 37
39 0 connected LVDS-1 344x194 1 37
Co-developed-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
*NOTE* In the grand plan, the "DSI-1" connector is not going to be present
and this driver might be removed entirely.
Changed in v6:
- update to use devm_drm_bridge_alloc(), remove .destroy
- use the devm APIs to get/put the next bridge pointer, remove open-coded
search code
- stop managing a panel bridge: the following component is not necessarily
a panel
Changed in v5:
- use drm_bridge dynamic lifetime management
- refcount bridge_modes and next_bridge via drm_bridge_get()/drm_bridge_put()
- fix dynconn removal by using drm_connector_put() and making the .destroy
callback work
- add hotplug_bridge_grab() in hotplug_bridge_attach() to handle the case
where the encoder driver is probed last
- migrate platform driver from .remove_new to .remove
Changed in v4:
- convert from generic notifiers to the new bridge_event_notify bridge
func
- improved and updated commit message, adding 'modetest' output
with/without add-on
- select DRM_DISPLAY_HELPER and DRM_BRIDGE_CONNECTOR, required after
commit 9da7ec9b19d8 ("drm/bridge-connector: move to DRM_DISPLAY_HELPER
module")
Changed in v3:
- dynamically add/remove the LVDS connector on hot(un)plug
- take the firmware node normally via dev->of_node instead of using
device_set_node(); this makes code more self-contained and generic
- minor rewordings and cleanups
Changed in v2:
- change to be a platform device instantiated from the connector driver
instead of a self-standing OF driver
- add missing error handling for devm_drm_bridge_add()
- various cleanups and style improvements
- fix typo in comment
---
MAINTAINERS | 5 +
drivers/gpu/drm/bridge/Kconfig | 17 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/hotplug-bridge.c | 666 ++++++++++++++++++++++++++++++++
4 files changed, 689 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 090153a4f40aa3ec4982c85c809a97dca0396ab8..992e1421cd2450b81dbc0d436fbddf4d4af97e0c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7268,6 +7268,11 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml
F: drivers/gpu/drm/panel/panel-himax-hx8394.c
+DRM DRIVER FOR HOTPLUG VIDEO CONNECTOR BRIDGE
+M: Luca Ceresoli <luca.ceresoli@bootlin.com>
+S: Maintained
+F: drivers/gpu/drm/bridge/hotplug-bridge.c
+
DRM DRIVER FOR HX8357D PANELS
S: Orphan
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 393214e6ed6656674d2ffbfc36d45541253bc31d..98436ab7a9da50fb09ded823b19cb0c8f4d4e484 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -90,6 +90,23 @@ config DRM_FSL_LDB
help
Support for i.MX8MP DPI-to-LVDS on-SoC encoder.
+config DRM_HOTPLUG_BRIDGE
+ tristate "Hotplug DRM bridge support"
+ depends on OF
+ select DRM_PANEL_BRIDGE
+ select DRM_MIPI_DSI
+ select DRM_KMS_HELPER
+ select DRM_DISPLAY_HELPER
+ select DRM_BRIDGE_CONNECTOR
+ help
+ Driver for a DRM bridge representing a physical connector that
+ splits a DRM pipeline into a fixed part and a physically
+ removable part. The fixed part includes up to the encoder and
+ zero or more bridges. The removable part includes any following
+ bridges up to the connector and panel and can be physically
+ removed and connected at runtime, possibly with different
+ components.
+
config DRM_ITE_IT6263
tristate "ITE IT6263 LVDS/HDMI bridge"
depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 97304b429a530c108dcbff906965cda091b0a7a2..2f6ae1a97d15045316ee191c04dbc65650162bab 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o
obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
obj-$(CONFIG_DRM_FSL_LDB) += fsl-ldb.o
+obj-$(CONFIG_DRM_HOTPLUG_BRIDGE) += hotplug-bridge.o
obj-$(CONFIG_DRM_ITE_IT6263) += ite-it6263.o
obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o
diff --git a/drivers/gpu/drm/bridge/hotplug-bridge.c b/drivers/gpu/drm/bridge/hotplug-bridge.c
new file mode 100644
index 0000000000000000000000000000000000000000..0912397dabb90605d219d7aa59251d39c846d0cb
--- /dev/null
+++ b/drivers/gpu/drm/bridge/hotplug-bridge.c
@@ -0,0 +1,666 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A DRM bridge representing the split point between a fixed part of the
+ * DRM pipeline and a physically removable part. The fixed part includes up
+ * to the encoder and zero or more bridges. Insertion and removal of the
+ * "downstream" components happens via device driver probe/removal.
+ *
+ * Copyright (C) 2024, GE HealthCare
+ *
+ * Authors:
+ * Luca Ceresoli <luca.ceresoli@bootlin.com>
+ * Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ */
+
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+
+/*
+ * Internal hotplug-bridge data.
+ *
+ * We have two 'struct drm_connector' here:
+ * - fixconn represents the DSI video lines on the hotplug connector where
+ * the removable part attaches. It is thus always instantiated.
+ * - dynconn represents the LVDS video lines where the panel is attached.
+ * It is part of the removable part of the video pipeline and as such is
+ * added and removed dynamically based on when the downstream devices
+ * appear and disappear.
+ */
+struct hotplug_bridge {
+ struct device *dev;
+
+ /* Local bridge */
+ struct drm_bridge bridge;
+
+ /* Always-present connector (where the removal part will connect to) */
+ struct drm_connector *fixconn;
+
+ /* Downstream bridge (next in the chain) */
+ struct drm_bridge *next_bridge;
+ /* Protect next_bridge */
+ struct mutex next_bridge_mutex;
+
+ /* Pointer to the last bridge exposing OP_MODES */
+ struct drm_bridge *bridge_modes;
+
+ /* The "tail" connector that gets added/removed at runtime */
+ struct drm_connector dynconn;
+
+ /* Local DSI host, for the downstream DSI device to attach to */
+ struct mipi_dsi_host dsi_host;
+ /* Local DSI device, attached to the upstream DSI host */
+ struct mipi_dsi_device *dsi_dev;
+ /* Upstream DSI host (the actual DSI controller) */
+ struct mipi_dsi_host *prev_dsi_host;
+
+ struct work_struct hpd_work;
+};
+
+static struct hotplug_bridge *hotplug_bridge_from_drm_bridge(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct hotplug_bridge, bridge);
+}
+
+/* --------------------------------------------------------------------------
+ * dynconn implementation
+ */
+static struct hotplug_bridge *hotplug_bridge_from_dynconn(struct drm_connector *conn)
+{
+ return container_of(conn, struct hotplug_bridge, dynconn);
+}
+
+static int hotplug_bridge_dynconn_get_modes(struct drm_connector *connector)
+{
+ struct hotplug_bridge *hpb = hotplug_bridge_from_dynconn(connector);
+
+ if (hpb->bridge_modes)
+ return hpb->bridge_modes->funcs->get_modes(hpb->bridge_modes, connector);
+
+ return 0;
+}
+
+static const struct drm_connector_helper_funcs hotplug_bridge_dynconn_connector_helper_funcs = {
+ .get_modes = hotplug_bridge_dynconn_get_modes,
+};
+
+static void hotplug_bridge_dynconn_destroy(struct drm_connector *connector)
+{
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs dynconn_funcs = {
+ .destroy = hotplug_bridge_dynconn_destroy,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+};
+
+/*
+ * In non-removable pipelines using a "bridge connector",
+ * drm_bridge_connector_init() stores in the bridge_connector a pointer to
+ * the last bridge having OP_MODES (typically the panel bridge), so the
+ * .get_modes op will automatically be called on that bridge (it also takes
+ * pointers to other bridges which we don't care about). The "bridge
+ * connector" is too restrictive for our use case so we cannot use it. But
+ * we need a pointer to the modes-providing bridge, so we need to replicate
+ * that bit of its logic.
+ *
+ * If no modes bridge is found, nothing is done. This is allowed.
+ *
+ * Also get the modes bridge to tie its lifetime to ours.
+ */
+static void hotplug_bridge_dynconn_bridge_modes_get(struct hotplug_bridge *hpb)
+{
+ struct drm_bridge *bridge;
+
+ if (WARN_ON(!hpb->next_bridge || !hpb->bridge.encoder))
+ return;
+
+ drm_for_each_bridge_in_chain(hpb->bridge.encoder, bridge)
+ if (bridge->ops & DRM_BRIDGE_OP_MODES) {
+ drm_bridge_get(bridge);
+ hpb->bridge_modes = bridge;
+ }
+}
+
+static void hotplug_bridge_dynconn_bridge_modes_put(struct hotplug_bridge *hpb)
+{
+ if (hpb->bridge_modes)
+ drm_bridge_put_and_clear(&hpb->bridge_modes);
+}
+
+static int hotplug_bridge_dynconn_add(struct hotplug_bridge *hpb)
+{
+ int err;
+
+ err = drm_connector_init(hpb->bridge.dev, &hpb->dynconn, &dynconn_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+ if (err)
+ return err;
+
+ drm_atomic_helper_connector_reset(&hpb->dynconn);
+
+ drm_connector_helper_add(&hpb->dynconn,
+ &hotplug_bridge_dynconn_connector_helper_funcs);
+
+ drm_connector_attach_encoder(&hpb->dynconn, hpb->bridge.encoder);
+ if (err)
+ goto err_cleanup;
+
+ hotplug_bridge_dynconn_bridge_modes_get(hpb);
+
+ err = drm_connector_register(&hpb->dynconn);
+ if (err)
+ goto err_cleanup;
+
+ return 0;
+
+err_cleanup:
+ drm_connector_cleanup(&hpb->dynconn);
+ hotplug_bridge_dynconn_bridge_modes_put(hpb);
+ return err;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * Attach the remote bridge to the encoder and to the next bridge in the
+ * chain, if possible. For this to succeed, we need to know:
+ *
+ * - the encoder, which is set at the first drm_bridge_attach() time
+ * - the next bridge, which is obtained via a notifier whenever the next
+ * bridge is (re)probed, or at probe time in case it was probed before us
+ *
+ * In order to handle different execution sequences, this function can be
+ * called from multiple places and needs to check all the prerequisites
+ * every time, and it will act only if both are met.
+ *
+ * Must be called with hpb->next_bridge_mutex held.
+ *
+ * Returns 0 if the encoder was attached successfully, -ENODEV if any of
+ * the two prerequisites above is not met (no encoder or no next bridge),
+ * the error returned by drm_bridge_attach() otherwise.
+ */
+static int hotplug_bridge_attach_to_encoder_chain(struct hotplug_bridge *hpb)
+{
+ int ret;
+
+ if (!hpb->next_bridge || !hpb->bridge.encoder)
+ return -ENODEV;
+
+ ret = drm_bridge_attach(hpb->bridge.encoder, hpb->next_bridge, &hpb->bridge,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (ret)
+ return dev_err_probe(hpb->dev, ret, "drm_bridge_attach failed\n");
+
+ dev_dbg(hpb->dev, "attached to encoder chain\n");
+
+ return 0;
+}
+
+/*
+ * Stop the video pipeline and detach next_bridge.
+ *
+ * Must be called with hpb->next_bridge_mutex held.
+ */
+static void hotplug_bridge_detach_from_encoder_chain(struct hotplug_bridge *hpb)
+{
+ WARN_ON_ONCE(!hpb->next_bridge);
+
+ dev_dbg(hpb->dev, "detaching from encoder chain\n");
+
+ drm_atomic_helper_shutdown(hpb->bridge.dev);
+
+ drm_encoder_cleanup_from(hpb->bridge.encoder, hpb->next_bridge);
+}
+
+static void hotplug_bridge_grab(struct hotplug_bridge *hpb)
+{
+ struct device *dev = hpb->dev;
+ struct drm_bridge *bridge;
+ int err;
+
+ mutex_lock(&hpb->next_bridge_mutex);
+
+ if (hpb->next_bridge)
+ goto out_unlock;
+
+ bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
+ if (IS_ERR(bridge))
+ goto out_unlock;
+
+ hpb->next_bridge = bridge;
+
+ dev_dbg(dev, "grabbed next bridge (%pOFn)\n", hpb->next_bridge->of_node);
+
+ hpb->bridge.pre_enable_prev_first = hpb->next_bridge->pre_enable_prev_first;
+
+ err = hotplug_bridge_attach_to_encoder_chain(hpb);
+ if (err)
+ goto err_panel_bridge_remove;
+
+ err = hotplug_bridge_dynconn_add(hpb);
+ if (err)
+ goto err_detach_from_encoder_chain;
+
+ queue_work(system_wq, &hpb->hpd_work);
+ goto out_unlock;
+
+err_detach_from_encoder_chain:
+ hotplug_bridge_detach_from_encoder_chain(hpb);
+err_panel_bridge_remove:
+ devm_drm_put_and_clear_bridge(dev, &hpb->next_bridge);
+out_unlock:
+ mutex_unlock(&hpb->next_bridge_mutex);
+}
+
+/*
+ * Detach from the next bridge and remove the panel bridge, either on
+ * release or when the downstream bridge is being removed.
+ *
+ * Can be called in these ways:
+ *
+ * - bridge_being_removed is NULL: detach unconditionally
+ * (this is useful on .remove() to teardown everything)
+ * - bridge_being_removed == hpb->next_bridge: detach
+ * (the downstream bridge is being removed)
+ * - bridge_being_removed != hpb->next_bridge: do nothing
+ * (the bridge being removed is not the downstream bridge)
+ *
+ * In all cases, does nothing when there is no downstream bridge.
+ */
+static void hotplug_bridge_release(struct hotplug_bridge *hpb,
+ struct drm_bridge *bridge_being_removed)
+{
+ struct device *dev = hpb->dev;
+
+ mutex_lock(&hpb->next_bridge_mutex);
+
+ if (!hpb->next_bridge)
+ goto out;
+
+ if (bridge_being_removed && bridge_being_removed != hpb->next_bridge)
+ goto out;
+
+ if (hpb->bridge_modes)
+ hotplug_bridge_dynconn_bridge_modes_put(hpb);
+
+ dev_dbg(dev, "releasing next bridge (%pOFn)\n", hpb->next_bridge->of_node);
+ hotplug_bridge_detach_from_encoder_chain(hpb);
+
+ dev_dbg(dev, "removing %s connector\n", hpb->dynconn.name);
+ drm_connector_put(&hpb->dynconn);
+
+ devm_drm_put_and_clear_bridge(dev, &hpb->next_bridge);
+
+ queue_work(system_wq, &hpb->hpd_work);
+
+out:
+ mutex_unlock(&hpb->next_bridge_mutex);
+}
+
+static void hotplug_bridge_bridge_event_notify(struct drm_bridge *bridge,
+ enum drm_bridge_event_type event,
+ struct drm_bridge *event_bridge)
+{
+ struct hotplug_bridge *hpb = container_of(bridge, struct hotplug_bridge, bridge);
+
+ switch (event) {
+ case DRM_EVENT_BRIDGE_ADD:
+ hotplug_bridge_grab(hpb);
+ break;
+ case DRM_EVENT_BRIDGE_REMOVE:
+ hotplug_bridge_release(hpb, event_bridge);
+ break;
+ }
+}
+
+static int hotplug_bridge_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct hotplug_bridge *hpb = hotplug_bridge_from_drm_bridge(bridge);
+ struct device *dev = hpb->dev;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder = hpb->bridge.encoder;
+ int err;
+
+ /* Encoder was not yet provided to our bridge */
+ if (!encoder)
+ return -ENODEV;
+
+ /* Connector was already created */
+ if (hpb->fixconn)
+ return dev_err_probe(dev, -EBUSY, "connector already created\n");
+
+ connector = drm_bridge_connector_init(bridge->dev, encoder);
+ if (IS_ERR(connector))
+ return dev_err_probe(dev, PTR_ERR(connector), "failed to initialize connector\n");
+
+ drm_connector_attach_encoder(connector, encoder);
+
+ hpb->fixconn = connector;
+
+ drm_connector_register(connector);
+
+ mutex_lock(&hpb->next_bridge_mutex);
+ err = hotplug_bridge_attach_to_encoder_chain(hpb);
+ mutex_unlock(&hpb->next_bridge_mutex);
+
+ /* -ENODEV is acceptable, in case next_bridge is not yet known */
+ if (err == -ENODEV)
+ err = 0;
+
+ /*
+ * If the encoder driver is probed last, the
+ * hotplug_bridge_attach_to_encoder_chain() call in
+ * hotplug_bridge_grab() fails because hpb->bridge.encoder is still
+ * NULL, and hotplug_bridge_grab() will not have another chance to
+ * execute. So call it now, at the end of the encoder attach
+ * process.
+ */
+ hotplug_bridge_grab(hpb);
+
+ return err;
+}
+
+static void hotplug_bridge_detach(struct drm_bridge *bridge)
+{
+ struct hotplug_bridge *hpb = hotplug_bridge_from_drm_bridge(bridge);
+
+ mutex_lock(&hpb->next_bridge_mutex);
+ hotplug_bridge_detach_from_encoder_chain(hpb);
+ mutex_unlock(&hpb->next_bridge_mutex);
+
+ if (hpb->fixconn) {
+ drm_connector_unregister(hpb->fixconn);
+ drm_connector_cleanup(hpb->fixconn);
+ hpb->fixconn = NULL;
+ }
+}
+
+/*
+ * The fixed connector is never attached to a panel, so it should always be
+ * reported as disconnected.
+ */
+static enum drm_connector_status hotplug_bridge_detect(struct drm_bridge *bridge)
+{
+ return connector_status_disconnected;
+}
+
+static void hotplug_bridge_hpd_work_func(struct work_struct *work)
+{
+ struct hotplug_bridge *hpb = container_of(work, struct hotplug_bridge, hpd_work);
+
+ if (hpb->bridge.dev)
+ drm_helper_hpd_irq_event(hpb->bridge.dev);
+}
+
+static const struct drm_bridge_funcs hotplug_bridge_funcs = {
+ .attach = hotplug_bridge_attach,
+ .detach = hotplug_bridge_detach,
+ .detect = hotplug_bridge_detect,
+ .bridge_event_notify = hotplug_bridge_bridge_event_notify,
+};
+
+static int hotplug_bridge_dsi_detach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *device_remote)
+{
+ struct hotplug_bridge *hpb = dev_get_drvdata(host->dev);
+
+ if (!hpb->dsi_dev)
+ return -ENODEV;
+
+ mipi_dsi_detach(hpb->dsi_dev);
+ mipi_dsi_device_unregister(hpb->dsi_dev);
+ hpb->dsi_dev = NULL;
+
+ return 0;
+}
+
+/*
+ * Attach the local DSI device to the upstream DSI host, possibly with a
+ * "null" format.
+ *
+ * In "normal" bridges this function should be _only_ used as the .attach
+ * callback of hotplug_bridge_dsi_ops. But "normal" bridges have their
+ * downstream DSI device always connected, which we don't. When booting
+ * without anything connected downstream, our upstream bridge could be not
+ * even calling drm_bridge_add() until we do attach ourselves as a DSI
+ * device, preventing the whole DRM card from being instantiated.
+ *
+ * In order to always have a DRM card after boot, we do call this same
+ * function while probing in order to attach as a DSI device to the DSI
+ * master. However during probe we don't know the bus format yet. It would
+ * be nice to be able to update the format afterwards when a downstream DSI
+ * device is attaching to our local host, but there is no callback for
+ * that. To overcome this limitation, this function can be called in two
+ * ways:
+ *
+ * - during probe, to make the upstream bridge happy, when there is no
+ * next_dsi_dev yet and thus the lanes/format/etc are unknown
+ * - as the mipi_dsi_host_ops.attach callback proper, as soon as the
+ * next_dsi_dev is known
+ *
+ * The resulting call sequence is:
+ *
+ * 1. hotplug_bridge_dsi_attach() called by hotplug_bridge_probe() with
+ * next_dsi_dev == NULL: we attach to the host but with a fake format
+ * so the DRM card can be populated. hpb->dsi_dev becomes non-NULL.
+ * 2. hotplug_bridge_dsi_attach() called as .attach callback from a
+ * downstream device when it becomes available: we need to detach in
+ * order to re-attach with the format of the device. hpb->dsi_dev
+ * is found non-NULL, then reused so it will be non-NULL again.
+ * 3. hotplug_bridge_dsi_detach() called as the .detach callback by a
+ * downstream device: cleans up everything normally. hpb->dsi_dev goes
+ * from non-NULL to NULL.
+ * 4. hotplug_bridge_dsi_attach() called by a downstream device: attaches
+ * normally to the upstream DSI host. hpb->dsi_dev goes from NULL to
+ * non-NULL.
+ *
+ * Steps 3 and 4 are the "normal" attach/detach steps as on "normal"
+ * bridges.
+ *
+ * Steps 1 and 2 happen only the first time, steps 3 and 4 will happen
+ * every time the downstream bridge disconnects and reconnects.
+ */
+static int hotplug_bridge_dsi_attach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *next_dsi_dev)
+{
+ struct device *dev = host->dev;
+ struct hotplug_bridge *hpb = dev_get_drvdata(dev);
+ struct mipi_dsi_device *dsi_dev;
+ const struct mipi_dsi_device_info dsi_info = {
+ .type = "hotplug-bridge",
+ .channel = 0,
+ .node = NULL,
+ };
+ int err;
+
+ /*
+ * Step 2 only (first time we are called for an actual device
+ * attaching): clean up the fake attach done at step 1
+ */
+ if (hpb->dsi_dev)
+ hotplug_bridge_dsi_detach(&hpb->dsi_host, NULL);
+
+ /* Register a local DSI device with the remote DSI host */
+ dsi_dev = mipi_dsi_device_register_full(hpb->prev_dsi_host,
+ &dsi_info);
+ if (IS_ERR(dsi_dev))
+ return PTR_ERR(dsi_dev);
+
+ /* At step 1 we have no downstream device to get the format from */
+ if (next_dsi_dev) {
+ dsi_dev->channel = next_dsi_dev->channel;
+ dsi_dev->lanes = next_dsi_dev->lanes;
+ dsi_dev->format = next_dsi_dev->format;
+ dsi_dev->mode_flags = next_dsi_dev->mode_flags;
+ }
+
+ /* Attach our local DSI device to the remote DSI host */
+ err = mipi_dsi_attach(dsi_dev);
+ if (err) {
+ mipi_dsi_device_unregister(dsi_dev);
+ return dev_err_probe(dev, err, "failed to attach hotplug dsi device to host\n");
+ }
+
+ hpb->dsi_dev = dsi_dev;
+
+ return 0;
+}
+
+/*
+ * Propagate mipi_dsi_device_transfer() to the upstream DSI host.
+ *
+ * Reimplements identically the minimal needed part of
+ * mipi_dsi_device_transfer(), including the -ENOSYS return value.
+ */
+static ssize_t hotplug_bridge_dsi_transfer(struct mipi_dsi_host *host,
+ const struct mipi_dsi_msg *msg)
+{
+ struct hotplug_bridge *hpb = dev_get_drvdata(host->dev);
+ const struct mipi_dsi_host_ops *ops;
+
+ if (!hpb->dsi_dev)
+ return -ENODEV;
+
+ ops = hpb->dsi_dev->host->ops;
+
+ if (!ops || !ops->transfer)
+ return -ENOSYS;
+
+ return ops->transfer(hpb->dsi_dev->host, msg);
+}
+
+static const struct mipi_dsi_host_ops hotplug_bridge_dsi_ops = {
+ .attach = hotplug_bridge_dsi_attach,
+ .detach = hotplug_bridge_dsi_detach,
+ .transfer = hotplug_bridge_dsi_transfer,
+};
+
+/*
+ * Find the upstream DSI host and register our downstream-facing DSI host.
+ */
+static int hotplug_bridge_dsi_setup(struct hotplug_bridge *hpb)
+{
+ struct device *dev = hpb->dev;
+ struct device_node *endpoint;
+ struct device_node *node;
+
+ endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
+ node = of_graph_get_remote_port_parent(endpoint);
+
+ hpb->prev_dsi_host = of_find_mipi_dsi_host_by_node(node);
+
+ of_node_put(node);
+ of_node_put(endpoint);
+
+ if (!hpb->prev_dsi_host)
+ return -EPROBE_DEFER;
+
+ hpb->dsi_host.dev = dev;
+ hpb->dsi_host.ops = &hotplug_bridge_dsi_ops;
+
+ return mipi_dsi_host_register(&hpb->dsi_host);
+}
+
+static void hotplug_bridge_dsi_cleanup(struct hotplug_bridge *hpb)
+{
+ mipi_dsi_host_unregister(&hpb->dsi_host);
+}
+
+static int hotplug_bridge_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct hotplug_bridge *hpb;
+ struct drm_bridge *bridge;
+ int err;
+
+ hpb = devm_drm_bridge_alloc(dev, struct hotplug_bridge, bridge, &hotplug_bridge_funcs);
+ if (IS_ERR(hpb))
+ return PTR_ERR(hpb);
+
+ hpb->dev = dev;
+
+ mutex_init(&hpb->next_bridge_mutex);
+ INIT_WORK(&hpb->hpd_work, hotplug_bridge_hpd_work_func);
+
+ err = hotplug_bridge_dsi_setup(hpb);
+ if (err)
+ return dev_err_probe(dev, err, "failed to setup DSI\n");
+
+ bridge = &hpb->bridge;
+ bridge->of_node = dev->of_node;
+ bridge->type = DRM_MODE_CONNECTOR_DSI;
+ bridge->ops |= DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_HPD;
+
+ platform_set_drvdata(pdev, hpb);
+
+ err = devm_drm_bridge_add(dev, bridge);
+ if (err) {
+ dev_err_probe(dev, err, "failed adding bridge\n");
+ goto err_dsi_cleanup;
+ }
+
+ err = hotplug_bridge_dsi_attach(&hpb->dsi_host, NULL);
+ if (err) {
+ dev_err_probe(dev, err, "failed first attach to upstream DSI host\n");
+ goto err_dsi_cleanup;
+ }
+
+ /*
+ * Since devm_drm_bridge_add() we can be notified of any bridges
+ * appearing, but also check now, in case the next bridge was
+ * probed earlier
+ */
+ hotplug_bridge_grab(hpb);
+
+ return 0;
+
+err_dsi_cleanup:
+ hotplug_bridge_dsi_cleanup(hpb);
+ return err;
+}
+
+static void hotplug_bridge_remove(struct platform_device *pdev)
+{
+ struct hotplug_bridge *hpb = platform_get_drvdata(pdev);
+
+ cancel_work_sync(&hpb->hpd_work);
+
+ hotplug_bridge_release(hpb, NULL);
+
+ hotplug_bridge_dsi_cleanup(hpb);
+}
+
+static const struct platform_device_id hotplug_bridge_platform_ids[] = {
+ { .name = "hotplug-dsi-bridge" },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, hotplug_bridge_platform_ids);
+
+static struct platform_driver hotplug_bridge_driver = {
+ .probe = hotplug_bridge_probe,
+ .remove = hotplug_bridge_remove,
+ .id_table = hotplug_bridge_platform_ids,
+ .driver = {
+ .name = "hotplug-drm-bridge",
+ },
+};
+
+module_platform_driver(hotplug_bridge_driver);
+
+MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
+MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
+MODULE_DESCRIPTION("Hotplug DRM Bridge");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related [flat|nested] 81+ messages in thread
* Re: [PATCH v6 00/26] Add support for hot-pluggable DRM bridges
2025-02-06 18:14 [PATCH v6 00/26] Add support for hot-pluggable DRM bridges Luca Ceresoli
` (25 preceding siblings ...)
2025-02-06 18:14 ` [PATCH v6 26/26] drm/bridge: hotplug-bridge: add driver to support hot-pluggable DSI bridges Luca Ceresoli
@ 2025-02-07 9:01 ` Luca Ceresoli
26 siblings, 0 replies; 81+ messages in thread
From: Luca Ceresoli @ 2025-02-07 9:01 UTC (permalink / raw)
To: Simona Vetter, Inki Dae, Jagan Teki, Marek Szyprowski,
Catalin Marinas, Will Deacon, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Daniel Thompson,
Andrzej Hajda, Jonathan Corbet, Sam Ravnborg, Boris Brezillon,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Jessica Zhang
Cc: Paul Kocialkowski, Maxime Ripard, Dmitry Baryshkov,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Maarten Lankhorst, Thomas Zimmermann,
David Airlie, Hervé Codina, Thomas Petazzoni, linux-kernel,
dri-devel, linux-doc, linux-arm-kernel, Paul Kocialkowski
On Thu, 06 Feb 2025 19:14:15 +0100
Luca Ceresoli <luca.ceresoli@bootlin.com> wrote:
> Patch series overview
> =====================
And I messed up with my patch series overview.
Please ignore all lines from here...
> * 2 Preliminary patches (maybe to be removed as this work progresses):
> - drm/bridge: allow bridges to be informed about added and removed bridges
> - drm/encoder: add drm_encoder_cleanup_from()
>
> * Implement refcounting in the drm_bridge core:
> - drm/bridge: add support for refcounted DRM bridges
> - drm/tests: bridge: add KUnit tests for DRM bridges (init and destroy)
> - drm/bridge: add documentation of refcounted bridges
>
> * Adapt some existing bridges to be refcounted and/or to refcount bridges
> they take a pointer to:
> - drm/bridge: ti-sn65dsi83: use dynamic lifetime management
> - drm/bridge: panel: use dynamic lifetime management
> - drm/bridge: samsung-dsim: use supporting variable for out_bridge
> - drm/bridge: samsung-dsim: refcount the out_bridge
>
> * Add hotplug-bridge (may be removed as this work progresses):
> - drm/bridge: hotplug-bridge: add driver to support hot-pluggable DSI bridges
...to here, as they belong to v5
The lines that follow are the correct ones for v6:
> * Prelimiary, simple cleanups:
> - drm/debugfs: fix printk format for bridge index
> - drm: of: drm_of_find_panel_or_bridge: move misplaced comment
> - drm/bridge: panel: use drm_bridge_is_panel() instead of open code
> - drm/bridge: panel: drm_panel_bridge_remove: warn when called on non-panel bridge
>
> * debugfs improvements to show bridges, part 1:
> - drm/debugfs: add top-level 'bridges' file showing all added bridges
>
> * Restructure panel code to always add a panel_bridge:
> - drm/panel: move all code into bridge/panel.c
> - drm/bridge: panel: forbid initializing a panel with unknown connector type
> - drm/bridge: panel: add a panel_bridge to every panel
> - drm/bridge: move devm_drm_of_get_bridge and drmm_of_get_bridge to drm_bridge.c
>
> * Add new get_bridge variant for drivers supporting non-graph DT:
> - drm/bridge: add devm_drm_of_get_bridge_by_node()
> - drm/bridge: samsung-dsim: use devm_drm_of_get_bridge[_by_node] to find the out_bridge
>
> * Preliminary patches (maybe to be removed as this work progresses):
> - drm/bridge: allow bridges to be informed about added and removed bridges
> - drm/encoder: add drm_encoder_cleanup_from()
>
> * Implement refcounting in the drm_bridge core:
> - drm/bridge: add support for refcounted DRM bridges
> - drm/bridge: devm_drm_of_get_bridge and drmm_of_get_bridge: automatically put the bridge
> - drm/bridge: increment refcount in of_drm_find_bridge()
> - drm/bridge: add devm_drm_put_bridge() and devm_drm_put_and_clear_bridge()
> - drm/bridge: add documentation of refcounted bridges
> - drm/tests: bridge: add KUnit tests for DRM bridges (init and destroy)
>
> * debugfs improvements to show bridges, part 2:
> - drm/debugfs: bridges_show: show refcount
> - drm/bridge: add list of removed refcounted bridges
> - drm/debugfs: show removed bridges
>
> * Adapt some existing bridges to be refcounted and/or to refcount bridges
> they take a pointer to:
> - drm/bridge: samsung-dsim: use refcounting for the out_bridge
> - drm/bridge: panel: use dynamic lifetime management
> - drm/bridge: ti-sn65dsi83: use dynamic lifetime management
>
> * Add hotplug-bridge (may be removed as this work progresses):
> - drm/bridge: hotplug-bridge: add driver to support hot-pluggable DSI bridges (HEAD -> ge/luca/wip)
Apologies for the mess.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 81+ messages in thread