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:
Родитель
328c057ca4
Коммит
4e8ba5cc88
|
@ -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,
|
||||
},
|
||||
|
|
Загрузка…
Ссылка в новой задаче