drm/tegra: Allocate resources at probe time
authorThierry Reding <treding@nvidia.com>
Mon, 14 Oct 2013 12:26:42 +0000 (14:26 +0200)
committerThierry Reding <treding@nvidia.com>
Thu, 31 Oct 2013 08:55:41 +0000 (09:55 +0100)
Since the .init() and .exit() functions are executed whenever the DRM
driver is loaded or unloaded, care must be taken not to use them for
resource allocation. Otherwise deferred probing cannot be used, since
the .init() and .exit() are not run at probe time. Similarly the code
that frees resources must be run at .remove() time. If it is run from
the .exit() function, it can release resources multiple times.

To handle this more consistently, rename the tegra_output_parse_dt()
function to tegra_output_probe() and introduce tegra_output_remove()
which can be used to free output-related resources.

Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/gpu/drm/tegra/dc.c
drivers/gpu/drm/tegra/drm.h
drivers/gpu/drm/tegra/hdmi.c
drivers/gpu/drm/tegra/output.c
drivers/gpu/drm/tegra/rgb.c

index 93ab5395c838f38e7ca673fa4d03eb820a33035f..054ca1b6bd31d6b220c7b1596fccfb47b3411a94 100644 (file)
@@ -1191,6 +1191,12 @@ static int tegra_dc_remove(struct platform_device *pdev)
                return err;
        }
 
+       err = tegra_dc_rgb_remove(dc);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to remove RGB output: %d\n", err);
+               return err;
+       }
+
        clk_disable_unprepare(dc->clk);
 
        return 0;
index 25522e23c7b87fe12497aad64e54babe744129b7..dc4b994fe6137f117adf3797546caafd46f21325 100644 (file)
@@ -236,11 +236,13 @@ void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device);
 
 /* from rgb.c */
 extern int tegra_dc_rgb_probe(struct tegra_dc *dc);
+extern int tegra_dc_rgb_remove(struct tegra_dc *dc);
 extern int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc);
 extern int tegra_dc_rgb_exit(struct tegra_dc *dc);
 
 /* from output.c */
-extern int tegra_output_parse_dt(struct tegra_output *output);
+extern int tegra_output_probe(struct tegra_output *output);
+extern int tegra_output_remove(struct tegra_output *output);
 extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
 extern int tegra_output_exit(struct tegra_output *output);
 
index f5663d15670b5310bbb1d428199757c7b0468400..3a00cb07804159562b02c0d591745c032a5455fc 100644 (file)
@@ -1226,7 +1226,7 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
 
        hdmi->output.dev = &pdev->dev;
 
-       err = tegra_output_parse_dt(&hdmi->output);
+       err = tegra_output_probe(&hdmi->output);
        if (err < 0)
                return err;
 
@@ -1272,6 +1272,12 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
                return err;
        }
 
+       err = tegra_output_remove(&hdmi->output);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to remove output: %d\n", err);
+               return err;
+       }
+
        clk_unprepare(hdmi->clk_parent);
        clk_unprepare(hdmi->clk);
 
index 591d3b0186d8a804f4bdd23851798fb54bbf46a3..708cbfffb059bd2da7c1c3d5e18f6da75f56fee6 100644 (file)
@@ -161,7 +161,7 @@ static irqreturn_t hpd_irq(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-int tegra_output_parse_dt(struct tegra_output *output)
+int tegra_output_probe(struct tegra_output *output)
 {
        enum of_gpio_flags flags;
        struct device_node *ddc;
@@ -191,14 +191,6 @@ int tegra_output_parse_dt(struct tegra_output *output)
        output->hpd_gpio = of_get_named_gpio_flags(output->of_node,
                                                   "nvidia,hpd-gpio", 0,
                                                   &flags);
-
-       return 0;
-}
-
-int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
-{
-       int connector, encoder, err;
-
        if (gpio_is_valid(output->hpd_gpio)) {
                unsigned long flags;
 
@@ -212,7 +204,8 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
                err = gpio_to_irq(output->hpd_gpio);
                if (err < 0) {
                        dev_err(output->dev, "gpio_to_irq(): %d\n", err);
-                       goto free_hpd;
+                       gpio_free(output->hpd_gpio);
+                       return err;
                }
 
                output->hpd_irq = err;
@@ -225,12 +218,33 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
                if (err < 0) {
                        dev_err(output->dev, "failed to request IRQ#%u: %d\n",
                                output->hpd_irq, err);
-                       goto free_hpd;
+                       gpio_free(output->hpd_gpio);
+                       return err;
                }
 
                output->connector.polled = DRM_CONNECTOR_POLL_HPD;
        }
 
+       return 0;
+}
+
+int tegra_output_remove(struct tegra_output *output)
+{
+       if (gpio_is_valid(output->hpd_gpio)) {
+               free_irq(output->hpd_irq, output);
+               gpio_free(output->hpd_gpio);
+       }
+
+       if (output->ddc)
+               put_device(&output->ddc->dev);
+
+       return 0;
+}
+
+int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
+{
+       int connector, encoder;
+
        switch (output->type) {
        case TEGRA_OUTPUT_RGB:
                connector = DRM_MODE_CONNECTOR_LVDS;
@@ -261,22 +275,9 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
        output->encoder.possible_crtcs = 0x3;
 
        return 0;
-
-free_hpd:
-       gpio_free(output->hpd_gpio);
-
-       return err;
 }
 
 int tegra_output_exit(struct tegra_output *output)
 {
-       if (gpio_is_valid(output->hpd_gpio)) {
-               free_irq(output->hpd_irq, output);
-               gpio_free(output->hpd_gpio);
-       }
-
-       if (output->ddc)
-               put_device(&output->ddc->dev);
-
        return 0;
 }
index e4d28411973dfa8a03ec5c2e64b29de43e379944..ba47ca4fb880cc239a9b5c454b3b6c5b5adcddc8 100644 (file)
@@ -147,7 +147,7 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
        rgb->output.dev = dc->dev;
        rgb->output.of_node = np;
 
-       err = tegra_output_parse_dt(&rgb->output);
+       err = tegra_output_probe(&rgb->output);
        if (err < 0)
                return err;
 
@@ -174,6 +174,20 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
        return 0;
 }
 
+int tegra_dc_rgb_remove(struct tegra_dc *dc)
+{
+       int err;
+
+       if (!dc->rgb)
+               return 0;
+
+       err = tegra_output_remove(dc->rgb);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
 int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
 {
        struct tegra_rgb *rgb = to_rgb(dc->rgb);