drm: vc4_dsi: Fix DMA channel and memory leak in vc4 (#3012)
authorChris Miller <chris@mesl2.co.uk>
Wed, 26 Jun 2019 09:40:30 +0000 (10:40 +0100)
committerpopcornmix <popcornmix@gmail.com>
Wed, 1 Jul 2020 15:33:01 +0000 (16:33 +0100)
Signed-off-by: Chris G Miller <chris@creative-electronics.net>
drivers/gpu/drm/vc4/vc4_dsi.c

index c78fa81..3448b31 100644 (file)
@@ -1485,9 +1485,11 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
        /* DSI1 has a broken AXI slave that doesn't respond to writes
         * from the ARM.  It does handle writes from the DMA engine,
         * so set up a channel for talking to it.
+        * Where possible managed resource providers are used, but the DMA channel
+        * must - if acquired - be explicitly released prior to taking an error exit path.
         */
        if (dsi->port == 1) {
-               dsi->reg_dma_mem = dma_alloc_coherent(dev, 4,
+               dsi->reg_dma_mem = dmam_alloc_coherent(dev, 4,
                                                      &dsi->reg_dma_paddr,
                                                      GFP_KERNEL);
                if (!dsi->reg_dma_mem) {
@@ -1506,6 +1508,8 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
                        return ret;
                }
 
+               /* From here on, any error exits must release the dma channel */
+
                /* Get the physical address of the device's registers.  The
                 * struct resource for the regs gives us the bus address
                 * instead.
@@ -1532,7 +1536,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
        if (ret) {
                if (ret != -EPROBE_DEFER)
                        dev_err(dev, "Failed to get interrupt: %d\n", ret);
-               return ret;
+               goto rel_dma_exit;
        }
 
        dsi->escape_clock = devm_clk_get(dev, "escape");
@@ -1540,7 +1544,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
                ret = PTR_ERR(dsi->escape_clock);
                if (ret != -EPROBE_DEFER)
                        dev_err(dev, "Failed to get escape clock: %d\n", ret);
-               return ret;
+               goto rel_dma_exit;
        }
 
        dsi->pll_phy_clock = devm_clk_get(dev, "phy");
@@ -1548,7 +1552,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
                ret = PTR_ERR(dsi->pll_phy_clock);
                if (ret != -EPROBE_DEFER)
                        dev_err(dev, "Failed to get phy clock: %d\n", ret);
-               return ret;
+               goto rel_dma_exit;
        }
 
        dsi->pixel_clock = devm_clk_get(dev, "pixel");
@@ -1556,7 +1560,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
                ret = PTR_ERR(dsi->pixel_clock);
                if (ret != -EPROBE_DEFER)
                        dev_err(dev, "Failed to get pixel clock: %d\n", ret);
-               return ret;
+               goto rel_dma_exit;
        }
 
        ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0,
@@ -1571,26 +1575,28 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
                if (ret == -ENODEV)
                        return 0;
 
-               return ret;
+               goto rel_dma_exit;
        }
 
        if (panel) {
                dsi->bridge = devm_drm_panel_bridge_add(dev, panel,
                                                        DRM_MODE_CONNECTOR_DSI);
-               if (IS_ERR(dsi->bridge))
-                       return PTR_ERR(dsi->bridge);
+               if (IS_ERR(dsi->bridge)){
+                       ret = PTR_ERR(dsi->bridge);
+                       goto rel_dma_exit;
+               }
        }
 
        /* The esc clock rate is supposed to always be 100Mhz. */
        ret = clk_set_rate(dsi->escape_clock, 100 * 1000000);
        if (ret) {
                dev_err(dev, "Failed to set esc clock: %d\n", ret);
-               return ret;
+               goto rel_dma_exit;
        }
 
        ret = vc4_dsi_init_phy_clocks(dsi);
        if (ret)
-               return ret;
+               goto rel_dma_exit;
 
        if (dsi->port == 1)
                vc4->dsi1 = dsi;
@@ -1602,7 +1608,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
        ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL);
        if (ret) {
                dev_err(dev, "bridge attach failed: %d\n", ret);
-               return ret;
+               goto rel_dma_exit;
        }
        /* Disable the atomic helper calls into the bridge.  We
         * manually call the bridge pre_enable / enable / etc. calls
@@ -1619,6 +1625,11 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
        pm_runtime_enable(dev);
 
        return 0;
+
+rel_dma_exit:
+       dma_release_channel(dsi->reg_dma_chan);
+
+       return ret;
 }
 
 static void vc4_dsi_unbind(struct device *dev, struct device *master,
@@ -1633,6 +1644,8 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
 
        vc4_dsi_encoder_destroy(dsi->encoder);
 
+       dma_release_channel(dsi->reg_dma_chan);
+
        if (dsi->port == 1)
                vc4->dsi1 = NULL;
 }