|
@@ -18,6 +18,7 @@
|
|
|
#include <linux/gcd.h>
|
|
|
#include <linux/gpio.h>
|
|
|
#include <linux/i2c.h>
|
|
|
+#include <linux/pm_runtime.h>
|
|
|
#include <linux/regulator/consumer.h>
|
|
|
#include <linux/regulator/fixed.h>
|
|
|
#include <linux/slab.h>
|
|
@@ -1261,54 +1262,6 @@ static const __devinitdata struct reg_default wm5100_reva_patches[] = {
|
|
|
{ WM5100_AUDIO_IF_3_19, 1 },
|
|
|
};
|
|
|
|
|
|
-static int wm5100_set_bias_level(struct snd_soc_codec *codec,
|
|
|
- enum snd_soc_bias_level level)
|
|
|
-{
|
|
|
- struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
|
|
|
- int ret;
|
|
|
-
|
|
|
- switch (level) {
|
|
|
- case SND_SOC_BIAS_ON:
|
|
|
- break;
|
|
|
-
|
|
|
- case SND_SOC_BIAS_PREPARE:
|
|
|
- break;
|
|
|
-
|
|
|
- case SND_SOC_BIAS_STANDBY:
|
|
|
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
|
|
|
- ret = regulator_bulk_enable(ARRAY_SIZE(wm5100->core_supplies),
|
|
|
- wm5100->core_supplies);
|
|
|
- if (ret != 0) {
|
|
|
- dev_err(codec->dev,
|
|
|
- "Failed to enable supplies: %d\n",
|
|
|
- ret);
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- if (wm5100->pdata.ldo_ena) {
|
|
|
- gpio_set_value_cansleep(wm5100->pdata.ldo_ena,
|
|
|
- 1);
|
|
|
- msleep(2);
|
|
|
- }
|
|
|
-
|
|
|
- regcache_cache_only(wm5100->regmap, false);
|
|
|
- regcache_sync(wm5100->regmap);
|
|
|
- }
|
|
|
- break;
|
|
|
-
|
|
|
- case SND_SOC_BIAS_OFF:
|
|
|
- regcache_cache_only(wm5100->regmap, true);
|
|
|
- if (wm5100->pdata.ldo_ena)
|
|
|
- gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
|
|
|
- regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies),
|
|
|
- wm5100->core_supplies);
|
|
|
- break;
|
|
|
- }
|
|
|
- codec->dapm.bias_level = level;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
static int wm5100_dai_to_base(struct snd_soc_dai *dai)
|
|
|
{
|
|
|
switch (dai->id) {
|
|
@@ -1836,6 +1789,8 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
|
|
|
|
|
|
if (!Fout) {
|
|
|
dev_dbg(codec->dev, "FLL%d disabled", fll_id);
|
|
|
+ if (fll->fout)
|
|
|
+ pm_runtime_put(codec->dev);
|
|
|
fll->fout = 0;
|
|
|
snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, 0);
|
|
|
return 0;
|
|
@@ -1880,6 +1835,8 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
|
|
|
/* Clear any pending completions */
|
|
|
try_wait_for_completion(&fll->lock);
|
|
|
|
|
|
+ pm_runtime_get_sync(codec->dev);
|
|
|
+
|
|
|
snd_soc_update_bits(codec, base + 1, WM5100_FLL1_ENA, WM5100_FLL1_ENA);
|
|
|
|
|
|
if (i2c->irq)
|
|
@@ -1914,6 +1871,7 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
|
|
|
}
|
|
|
if (i == timeout) {
|
|
|
dev_err(codec->dev, "FLL%d lock timed out\n", fll_id);
|
|
|
+ pm_runtime_put(codec->dev);
|
|
|
return -ETIMEDOUT;
|
|
|
}
|
|
|
|
|
@@ -2377,9 +2335,6 @@ static int wm5100_probe(struct snd_soc_codec *codec)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- regcache_cache_only(wm5100->regmap, true);
|
|
|
-
|
|
|
-
|
|
|
for (i = 0; i < ARRAY_SIZE(wm5100_dig_vu); i++)
|
|
|
snd_soc_update_bits(codec, wm5100_dig_vu[i], WM5100_OUT_VU,
|
|
|
WM5100_OUT_VU);
|
|
@@ -2405,14 +2360,6 @@ static int wm5100_probe(struct snd_soc_codec *codec)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /* We'll get woken up again when the system has something useful
|
|
|
- * for us to do.
|
|
|
- */
|
|
|
- if (wm5100->pdata.ldo_ena)
|
|
|
- gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
|
|
|
- regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies),
|
|
|
- wm5100->core_supplies);
|
|
|
-
|
|
|
return 0;
|
|
|
|
|
|
err_gpio:
|
|
@@ -2444,7 +2391,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5100 = {
|
|
|
|
|
|
.set_sysclk = wm5100_set_sysclk,
|
|
|
.set_pll = wm5100_set_fll,
|
|
|
- .set_bias_level = wm5100_set_bias_level,
|
|
|
.idle_bias_off = 1,
|
|
|
.reg_cache_size = WM5100_MAX_REGISTER,
|
|
|
.volatile_register = wm5100_soc_volatile,
|
|
@@ -2661,6 +2607,10 @@ static __devinit int wm5100_i2c_probe(struct i2c_client *i2c,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ pm_runtime_set_active(&i2c->dev);
|
|
|
+ pm_runtime_enable(&i2c->dev);
|
|
|
+ pm_request_idle(&i2c->dev);
|
|
|
+
|
|
|
ret = snd_soc_register_codec(&i2c->dev,
|
|
|
&soc_codec_dev_wm5100, wm5100_dai,
|
|
|
ARRAY_SIZE(wm5100_dai));
|
|
@@ -2714,6 +2664,51 @@ static __devexit int wm5100_i2c_remove(struct i2c_client *i2c)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_PM_RUNTIME
|
|
|
+static int wm5100_runtime_suspend(struct device *dev)
|
|
|
+{
|
|
|
+ struct wm5100_priv *wm5100 = dev_get_drvdata(dev);
|
|
|
+
|
|
|
+ regcache_cache_only(wm5100->regmap, true);
|
|
|
+ regcache_mark_dirty(wm5100->regmap);
|
|
|
+ if (wm5100->pdata.ldo_ena)
|
|
|
+ gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
|
|
|
+ regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies),
|
|
|
+ wm5100->core_supplies);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int wm5100_runtime_resume(struct device *dev)
|
|
|
+{
|
|
|
+ struct wm5100_priv *wm5100 = dev_get_drvdata(dev);
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm5100->core_supplies),
|
|
|
+ wm5100->core_supplies);
|
|
|
+ if (ret != 0) {
|
|
|
+ dev_err(dev, "Failed to enable supplies: %d\n",
|
|
|
+ ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (wm5100->pdata.ldo_ena) {
|
|
|
+ gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 1);
|
|
|
+ msleep(2);
|
|
|
+ }
|
|
|
+
|
|
|
+ regcache_cache_only(wm5100->regmap, false);
|
|
|
+ regcache_sync(wm5100->regmap);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+static struct dev_pm_ops wm5100_pm = {
|
|
|
+ SET_RUNTIME_PM_OPS(wm5100_runtime_suspend, wm5100_runtime_resume,
|
|
|
+ NULL)
|
|
|
+};
|
|
|
+
|
|
|
static const struct i2c_device_id wm5100_i2c_id[] = {
|
|
|
{ "wm5100", 0 },
|
|
|
{ }
|
|
@@ -2724,6 +2719,7 @@ static struct i2c_driver wm5100_i2c_driver = {
|
|
|
.driver = {
|
|
|
.name = "wm5100",
|
|
|
.owner = THIS_MODULE,
|
|
|
+ .pm = &wm5100_pm,
|
|
|
},
|
|
|
.probe = wm5100_i2c_probe,
|
|
|
.remove = __devexit_p(wm5100_i2c_remove),
|