|
@@ -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,
|
|
|
},
|