[RFT PATCH 03/15] drm/ingenic: Call drm_atomic_helper_shutdown() at shutdown time

From: Douglas Anderson
Date: Fri Sep 01 2023 - 19:43:53 EST


Based on grepping through the source code this driver appears to be
missing a call to drm_atomic_helper_shutdown() at system shutdown
time. Among other things, this means that if a panel is in use that it
won't be cleanly powered off at system shutdown time.

The fact that we should call drm_atomic_helper_shutdown() in the case
of OS shutdown/restart comes straight out of the kernel doc "driver
instance overview" in drm_drv.c.

Since this driver uses the component model and shutdown happens at the
base driver, we communicate whether we have to call
drm_atomic_helper_shutdown() by seeing if drvdata is non-NULL.

Suggested-by: Maxime Ripard <mripard@xxxxxxxxxx>
Signed-off-by: Douglas Anderson <dianders@xxxxxxxxxxxx>
---
This commit is only compile-time tested.

NOTE: this patch touches a lot more than other similar patches since
the bind() function is long and we want to make sure that we unset the
drvdata if bind() fails.

While making this patch, I noticed that the bind() function of this
driver is using "devm" and thus assumes it doesn't need to do much
explicit error handling. That's actually a bug. As per kernel docs [1]
"the lifetime of the aggregate driver does not align with any of the
underlying struct device instances. Therefore devm cannot be used and
all resources acquired or allocated in this callback must be
explicitly released in the unbind callback". Fixing that is outside
the scope of this commit.

[1] https://docs.kernel.org/driver-api/component.html

drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 66 +++++++++++++++--------
1 file changed, 44 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index 8dbd4847d3a6..51995a0cd568 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -1130,7 +1130,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)

ret = drmm_mode_config_init(drm);
if (ret)
- return ret;
+ goto err_drvdata;

drm->mode_config.min_width = 0;
drm->mode_config.min_height = 0;
@@ -1142,7 +1142,8 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base)) {
dev_err(dev, "Failed to get memory resource\n");
- return PTR_ERR(base);
+ ret = PTR_ERR(base);
+ goto err_drvdata;
}

regmap_config = ingenic_drm_regmap_config;
@@ -1151,33 +1152,40 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
&regmap_config);
if (IS_ERR(priv->map)) {
dev_err(dev, "Failed to create regmap\n");
- return PTR_ERR(priv->map);
+ ret = PTR_ERR(priv->map);
+ goto err_drvdata;
}

irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
+ if (irq < 0) {
+ ret = irq;
+ goto err_drvdata;
+ }

if (soc_info->needs_dev_clk) {
priv->lcd_clk = devm_clk_get(dev, "lcd");
if (IS_ERR(priv->lcd_clk)) {
dev_err(dev, "Failed to get lcd clock\n");
- return PTR_ERR(priv->lcd_clk);
+ ret = PTR_ERR(priv->lcd_clk);
+ goto err_drvdata;
}
}

priv->pix_clk = devm_clk_get(dev, "lcd_pclk");
if (IS_ERR(priv->pix_clk)) {
dev_err(dev, "Failed to get pixel clock\n");
- return PTR_ERR(priv->pix_clk);
+ ret = PTR_ERR(priv->pix_clk);
+ goto err_drvdata;
}

priv->dma_hwdescs = dmam_alloc_coherent(dev,
sizeof(*priv->dma_hwdescs),
&priv->dma_hwdescs_phys,
GFP_KERNEL);
- if (!priv->dma_hwdescs)
- return -ENOMEM;
+ if (!priv->dma_hwdescs) {
+ ret = -ENOMEM;
+ goto err_drvdata;
+ }

/* Configure DMA hwdesc for foreground0 plane */
ingenic_drm_configure_hwdesc_plane(priv, 0);
@@ -1199,7 +1207,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) {
dev_err(dev, "Failed to register plane: %i\n", ret);
- return ret;
+ goto err_drvdata;
}

if (soc_info->map_noncoherent)
@@ -1211,7 +1219,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
NULL, &ingenic_drm_crtc_funcs, NULL);
if (ret) {
dev_err(dev, "Failed to init CRTC: %i\n", ret);
- return ret;
+ goto err_drvdata;
}

drm_crtc_enable_color_mgmt(&priv->crtc, 0, false,
@@ -1230,7 +1238,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
if (ret) {
dev_err(dev, "Failed to register overlay plane: %i\n",
ret);
- return ret;
+ goto err_drvdata;
}

if (soc_info->map_noncoherent)
@@ -1241,17 +1249,18 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to bind components: %i\n", ret);
- return ret;
+ goto err_drvdata;
}

ret = devm_add_action_or_reset(dev, ingenic_drm_unbind_all, priv);
if (ret)
- return ret;
+ goto err_drvdata;

priv->ipu_plane = drm_plane_from_index(drm, 2);
if (!priv->ipu_plane) {
dev_err(dev, "Failed to retrieve IPU plane\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_drvdata;
}
}
}
@@ -1263,7 +1272,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
break; /* we're done */
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get bridge handle\n");
- return ret;
+ goto err_drvdata;
}

if (panel)
@@ -1275,7 +1284,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
if (IS_ERR(ib)) {
ret = PTR_ERR(ib);
dev_err(dev, "Failed to init encoder: %d\n", ret);
- return ret;
+ goto err_drvdata;
}

encoder = &ib->encoder;
@@ -1290,13 +1299,14 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret) {
dev_err(dev, "Unable to attach bridge\n");
- return ret;
+ goto err_drvdata;
}

connector = drm_bridge_connector_init(drm, encoder);
if (IS_ERR(connector)) {
dev_err(dev, "Unable to init connector\n");
- return PTR_ERR(connector);
+ ret = PTR_ERR(connector);
+ goto err_drvdata;
}

drm_connector_attach_encoder(connector, encoder);
@@ -1313,13 +1323,13 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
ret = devm_request_irq(dev, irq, ingenic_drm_irq_handler, 0, drm->driver->name, drm);
if (ret) {
dev_err(dev, "Unable to install IRQ handler\n");
- return ret;
+ goto err_drvdata;
}

ret = drm_vblank_init(drm, 1);
if (ret) {
dev_err(dev, "Failed calling drm_vblank_init()\n");
- return ret;
+ goto err_drvdata;
}

drm_mode_config_reset(drm);
@@ -1327,7 +1337,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
ret = clk_prepare_enable(priv->pix_clk);
if (ret) {
dev_err(dev, "Unable to start pixel clock\n");
- return ret;
+ goto err_drvdata;
}

if (priv->lcd_clk) {
@@ -1402,6 +1412,8 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
clk_disable_unprepare(priv->lcd_clk);
err_pixclk_disable:
clk_disable_unprepare(priv->pix_clk);
+err_drvdata:
+ platform_set_drvdata(pdev, NULL);
return ret;
}

@@ -1422,6 +1434,7 @@ static void ingenic_drm_unbind(struct device *dev)

drm_dev_unregister(&priv->drm);
drm_atomic_helper_shutdown(&priv->drm);
+ dev_set_drvdata(dev, NULL);
}

static const struct component_master_ops ingenic_master_ops = {
@@ -1461,6 +1474,14 @@ static int ingenic_drm_remove(struct platform_device *pdev)
return 0;
}

+static void ingenic_drm_shutdown(struct platform_device *pdev)
+{
+ struct ingenic_drm *priv = platform_get_drvdata(pdev);
+
+ if (priv)
+ drm_atomic_helper_shutdown(&priv->drm);
+}
+
static int ingenic_drm_suspend(struct device *dev)
{
struct ingenic_drm *priv = dev_get_drvdata(dev);
@@ -1612,6 +1633,7 @@ static struct platform_driver ingenic_drm_driver = {
},
.probe = ingenic_drm_probe,
.remove = ingenic_drm_remove,
+ .shutdown = ingenic_drm_shutdown,
};

static int ingenic_drm_init(void)
--
2.42.0.283.g2d96d420d3-goog