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 <m.szyprowski@samsung.com>
Signed-off-by: Inki Dae <inki.dae@samsung.com>
This commit is contained in:
Marek Szyprowski 2017-01-13 09:30:00 +01:00 committed by Inki Dae
parent 328c057ca4
commit 4e8ba5cc88
1 changed files with 60 additions and 24 deletions

View 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);
DRM_DEBUG_KMS("MIC has been probed\n");
return component_add(dev, &exynos_mic_component_ops);
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 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,
},