Commit 75aa8868 authored by Zidan Wang's avatar Zidan Wang Committed by Mark Brown

ASoC: wm8960: Let wm8960 codec driver manage its own MCLK

When we want to use wm8960 codec, we should enable its MCLK in machine driver.
It's reasonable for wm8960 codec driver to manage its own MCLK.

When current bias_level is SND_SOC_BIAS_ON, it is preparing for a transition
away from ON. In this case, disable the codec mclk. When current bias_level
is not SND_SOC_BIAS_ON, it preparing for a transition to ON. In this case,
enable the codec mclk.
Signed-off-by: default avatarZidan Wang <b50113@freescale.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 97bf6af1
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/clk.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <sound/core.h> #include <sound/core.h>
...@@ -117,6 +118,7 @@ static bool wm8960_volatile(struct device *dev, unsigned int reg) ...@@ -117,6 +118,7 @@ static bool wm8960_volatile(struct device *dev, unsigned int reg)
} }
struct wm8960_priv { struct wm8960_priv {
struct clk *mclk;
struct regmap *regmap; struct regmap *regmap;
int (*set_bias_level)(struct snd_soc_codec *, int (*set_bias_level)(struct snd_soc_codec *,
enum snd_soc_bias_level level); enum snd_soc_bias_level level);
...@@ -618,16 +620,40 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, ...@@ -618,16 +620,40 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
enum snd_soc_bias_level level) enum snd_soc_bias_level level)
{ {
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
int ret;
switch (level) { switch (level) {
case SND_SOC_BIAS_ON: case SND_SOC_BIAS_ON:
break; break;
case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_PREPARE:
switch (codec->dapm.bias_level) {
case SND_SOC_BIAS_STANDBY:
if (!IS_ERR(wm8960->mclk)) {
ret = clk_prepare_enable(wm8960->mclk);
if (ret) {
dev_err(codec->dev,
"Failed to enable MCLK: %d\n",
ret);
return ret;
}
}
/* Set VMID to 2x50k */ /* Set VMID to 2x50k */
snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80); snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80);
break; break;
case SND_SOC_BIAS_ON:
if (!IS_ERR(wm8960->mclk))
clk_disable_unprepare(wm8960->mclk);
break;
default:
break;
}
break;
case SND_SOC_BIAS_STANDBY: case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
regcache_sync(wm8960->regmap); regcache_sync(wm8960->regmap);
...@@ -674,7 +700,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, ...@@ -674,7 +700,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
enum snd_soc_bias_level level) enum snd_soc_bias_level level)
{ {
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
int reg; int reg, ret;
switch (level) { switch (level) {
case SND_SOC_BIAS_ON: case SND_SOC_BIAS_ON:
...@@ -715,9 +741,22 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, ...@@ -715,9 +741,22 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
WM8960_VREF, WM8960_VREF); WM8960_VREF, WM8960_VREF);
msleep(100); msleep(100);
if (!IS_ERR(wm8960->mclk)) {
ret = clk_prepare_enable(wm8960->mclk);
if (ret) {
dev_err(codec->dev,
"Failed to enable MCLK: %d\n",
ret);
return ret;
}
}
break; break;
case SND_SOC_BIAS_ON: case SND_SOC_BIAS_ON:
if (!IS_ERR(wm8960->mclk))
clk_disable_unprepare(wm8960->mclk);
/* Enable anti-pop mode */ /* Enable anti-pop mode */
snd_soc_update_bits(codec, WM8960_APOP1, snd_soc_update_bits(codec, WM8960_APOP1,
WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_POBCTRL | WM8960_SOFT_ST |
...@@ -1002,6 +1041,12 @@ static int wm8960_i2c_probe(struct i2c_client *i2c, ...@@ -1002,6 +1041,12 @@ static int wm8960_i2c_probe(struct i2c_client *i2c,
if (wm8960 == NULL) if (wm8960 == NULL)
return -ENOMEM; return -ENOMEM;
wm8960->mclk = devm_clk_get(&i2c->dev, "mclk");
if (IS_ERR(wm8960->mclk)) {
if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
}
wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap); wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap);
if (IS_ERR(wm8960->regmap)) if (IS_ERR(wm8960->regmap))
return PTR_ERR(wm8960->regmap); return PTR_ERR(wm8960->regmap);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment