drm/exynos: mic: Add runtime PM support
authorMarek Szyprowski <m.szyprowski@samsung.com>
Fri, 13 Jan 2017 08:30:00 +0000 (09:30 +0100)
committerInki Dae <inki.dae@samsung.com>
Mon, 30 Jan 2017 23:49:38 +0000 (08:49 +0900)
This patch adds runtime support calls to notify device core when MIC
device is really in use. Runtime PM is implemented by enabling and
disabling clocks like in other Exynos DRM subdrivers. Adding runtime
PM support is needed to let power domain with this device to be turned
off when display is not used.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Inki Dae <inki.dae@samsung.com>
drivers/gpu/drm/exynos/exynos_drm_mic.c

index cf9361ab0eb766239b0ae8b7b09e857bb3490bb3..2ef43d403eaae42e5ab876fc4bca01a4c5153deb 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/of_graph.h>
 #include <linux/clk.h>
 #include <linux/component.h>
+#include <linux/pm_runtime.h>
 #include <drm/drmP.h>
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
@@ -286,7 +287,6 @@ static void mic_disable(struct drm_bridge *bridge) { }
 static void mic_post_disable(struct drm_bridge *bridge)
 {
        struct exynos_mic *mic = bridge->driver_private;
-       int i;
 
        mutex_lock(&mic_mutex);
        if (!mic->enabled)
@@ -294,9 +294,7 @@ static void mic_post_disable(struct drm_bridge *bridge)
 
        mic_set_path(mic, 0);
 
-       for (i = NUM_CLKS - 1; i > -1; i--)
-               clk_disable_unprepare(mic->clks[i]);
-
+       pm_runtime_put(mic->dev);
        mic->enabled = 0;
 
 already_disabled:
@@ -317,27 +315,22 @@ static void mic_mode_set(struct drm_bridge *bridge,
 static void mic_pre_enable(struct drm_bridge *bridge)
 {
        struct exynos_mic *mic = bridge->driver_private;
-       int ret, i;
+       int ret;
 
        mutex_lock(&mic_mutex);
        if (mic->enabled)
-               goto already_enabled;
+               goto unlock;
 
-       for (i = 0; i < NUM_CLKS; i++) {
-               ret = clk_prepare_enable(mic->clks[i]);
-               if (ret < 0) {
-                       DRM_ERROR("Failed to enable clock (%s)\n",
-                                                       clk_names[i]);
-                       goto turn_off_clks;
-               }
-       }
+       ret = pm_runtime_get_sync(mic->dev);
+       if (ret < 0)
+               goto unlock;
 
        mic_set_path(mic, 1);
 
        ret = mic_sw_reset(mic);
        if (ret) {
                DRM_ERROR("Failed to reset\n");
-               goto turn_off_clks;
+               goto turn_off;
        }
 
        if (!mic->i80_mode)
@@ -350,10 +343,9 @@ static void mic_pre_enable(struct drm_bridge *bridge)
 
        return;
 
-turn_off_clks:
-       while (--i > -1)
-               clk_disable_unprepare(mic->clks[i]);
-already_enabled:
+turn_off:
+       pm_runtime_put(mic->dev);
+unlock:
        mutex_unlock(&mic_mutex);
 }
 
@@ -387,14 +379,12 @@ static void exynos_mic_unbind(struct device *dev, struct device *master,
                              void *data)
 {
        struct exynos_mic *mic = dev_get_drvdata(dev);
-       int i;
 
        mutex_lock(&mic_mutex);
        if (!mic->enabled)
                goto already_disabled;
 
-       for (i = NUM_CLKS - 1; i > -1; i--)
-               clk_disable_unprepare(mic->clks[i]);
+       pm_runtime_put(mic->dev);
 
 already_disabled:
        mutex_unlock(&mic_mutex);
@@ -407,6 +397,41 @@ static const struct component_ops exynos_mic_component_ops = {
        .unbind = exynos_mic_unbind,
 };
 
+#ifdef CONFIG_PM
+static int exynos_mic_suspend(struct device *dev)
+{
+       struct exynos_mic *mic = dev_get_drvdata(dev);
+       int i;
+
+       for (i = NUM_CLKS - 1; i > -1; i--)
+               clk_disable_unprepare(mic->clks[i]);
+
+       return 0;
+}
+
+static int exynos_mic_resume(struct device *dev)
+{
+       struct exynos_mic *mic = dev_get_drvdata(dev);
+       int ret, i;
+
+       for (i = 0; i < NUM_CLKS; i++) {
+               ret = clk_prepare_enable(mic->clks[i]);
+               if (ret < 0) {
+                       DRM_ERROR("Failed to enable clock (%s)\n",
+                                                       clk_names[i]);
+                       while (--i > -1)
+                               clk_disable_unprepare(mic->clks[i]);
+                       return ret;
+               }
+       }
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops exynos_mic_pm_ops = {
+       SET_RUNTIME_PM_OPS(exynos_mic_suspend, exynos_mic_resume, NULL)
+};
+
 static int exynos_mic_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -459,9 +484,18 @@ static int exynos_mic_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, mic);
 
+       pm_runtime_enable(dev);
+
+       ret = component_add(dev, &exynos_mic_component_ops);
+       if (ret)
+               goto err_pm;
+
        DRM_DEBUG_KMS("MIC has been probed\n");
-       return component_add(dev, &exynos_mic_component_ops);
 
+       return 0;
+
+err_pm:
+       pm_runtime_disable(dev);
 err:
        return ret;
 }
@@ -469,6 +503,7 @@ err:
 static int exynos_mic_remove(struct platform_device *pdev)
 {
        component_del(&pdev->dev, &exynos_mic_component_ops);
+       pm_runtime_disable(&pdev->dev);
        return 0;
 }
 
@@ -483,6 +518,7 @@ struct platform_driver mic_driver = {
        .remove         = exynos_mic_remove,
        .driver         = {
                .name   = "exynos-mic",
+               .pm     = &exynos_mic_pm_ops,
                .owner  = THIS_MODULE,
                .of_match_table = exynos_mic_of_match,
        },