From 4e8ba5cc8863562cb948e26f614217a873f34bac Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 13 Jan 2017 09:30:00 +0100 Subject: [PATCH] drm/exynos: mic: Add runtime PM support 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 Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_mic.c | 82 ++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_mic.c b/drivers/gpu/drm/exynos/exynos_drm_mic.c index cf9361a..2ef43d4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_mic.c +++ b/drivers/gpu/drm/exynos/exynos_drm_mic.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -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, }, -- 2.7.4