ASoC: wm8971: Integrate capacitor charging into the DAPM sequence
[linux-2.6-microblaze.git] / sound / soc / codecs / wm8971.c
index 44baacd..4ab034d 100644 (file)
@@ -34,6 +34,8 @@
 /* codec private data */
 struct wm8971_priv {
        unsigned int sysclk;
+       struct delayed_work charge_work;
+       struct regmap *regmap;
 };
 
 /*
@@ -550,9 +552,19 @@ static int wm8971_mute(struct snd_soc_dai *dai, int mute)
        return 0;
 }
 
+static void wm8971_charge_work(struct work_struct *work)
+{
+       struct wm8971_priv *wm8971 =
+               container_of(work, struct wm8971_priv, charge_work.work);
+
+       /* Set to 500k */
+       regmap_update_bits(wm8971->regmap, WM8971_PWR1, 0x0180, 0x0100);
+}
+
 static int wm8971_set_bias_level(struct snd_soc_codec *codec,
        enum snd_soc_bias_level level)
 {
+       struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
        u16 pwr_reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
 
        switch (level) {
@@ -561,15 +573,24 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec,
                snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
                break;
        case SND_SOC_BIAS_PREPARE:
+               /* Wait until fully charged */
+               flush_delayed_work(&wm8971->charge_work);
                break;
        case SND_SOC_BIAS_STANDBY:
-               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
                        snd_soc_cache_sync(codec);
+                       /* charge output caps - set vmid to 5k for quick power up */
+                       snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x01c0);
+                       queue_delayed_work(system_power_efficient_wq,
+                               &wm8971->charge_work, msecs_to_jiffies(1000));
+               } else {
+                       /* mute dac and set vmid to 500k, enable VREF */
+                       snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
+               }
 
-               /* mute dac and set vmid to 500k, enable VREF */
-               snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
                break;
        case SND_SOC_BIAS_OFF:
+               cancel_delayed_work_sync(&wm8971->charge_work);
                snd_soc_write(codec, WM8971_PWR1, 0x0001);
                break;
        }
@@ -608,15 +629,6 @@ static struct snd_soc_dai_driver wm8971_dai = {
        .ops = &wm8971_dai_ops,
 };
 
-static void wm8971_work(struct work_struct *work)
-{
-       struct snd_soc_dapm_context *dapm =
-               container_of(work, struct snd_soc_dapm_context,
-                            delayed_work.work);
-       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
-       wm8971_set_bias_level(codec, codec->dapm.bias_level);
-}
-
 static int wm8971_suspend(struct snd_soc_codec *codec)
 {
        wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF);
@@ -625,39 +637,19 @@ static int wm8971_suspend(struct snd_soc_codec *codec)
 
 static int wm8971_resume(struct snd_soc_codec *codec)
 {
-       u16 reg;
-
        wm8971_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       /* charge wm8971 caps */
-       if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) {
-               reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
-               snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0);
-               codec->dapm.bias_level = SND_SOC_BIAS_ON;
-               queue_delayed_work(system_power_efficient_wq,
-                       &codec->dapm.delayed_work,
-                       msecs_to_jiffies(1000));
-       }
-
        return 0;
 }
 
 static int wm8971_probe(struct snd_soc_codec *codec)
 {
-       int ret = 0;
-       u16 reg;
+       struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
 
-       INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8971_work);
+       INIT_DELAYED_WORK(&wm8971->charge_work, wm8971_charge_work);
 
        wm8971_reset(codec);
 
-       /* charge output caps - set vmid to 5k for quick power up */
-       reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
-       snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0);
-       codec->dapm.bias_level = SND_SOC_BIAS_STANDBY;
-       queue_delayed_work(system_power_efficient_wq,
-               &codec->dapm.delayed_work,
-               msecs_to_jiffies(1000));
+       wm8971_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
        /* set the update bits */
        snd_soc_update_bits(codec, WM8971_LDAC, 0x0100, 0x0100);
@@ -669,7 +661,7 @@ static int wm8971_probe(struct snd_soc_codec *codec)
        snd_soc_update_bits(codec, WM8971_LINVOL, 0x0100, 0x0100);
        snd_soc_update_bits(codec, WM8971_RINVOL, 0x0100, 0x0100);
 
-       return ret;
+       return 0;
 }
 
 
@@ -710,7 +702,6 @@ static int wm8971_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
        struct wm8971_priv *wm8971;
-       struct regmap *regmap;
        int ret;
 
        wm8971 = devm_kzalloc(&i2c->dev, sizeof(struct wm8971_priv),
@@ -718,9 +709,9 @@ static int wm8971_i2c_probe(struct i2c_client *i2c,
        if (wm8971 == NULL)
                return -ENOMEM;
 
-       regmap = devm_regmap_init_i2c(i2c, &wm8971_regmap);
-       if (IS_ERR(regmap))
-               return PTR_ERR(regmap);
+       wm8971->regmap = devm_regmap_init_i2c(i2c, &wm8971_regmap);
+       if (IS_ERR(wm8971->regmap))
+               return PTR_ERR(wm8971->regmap);
 
        i2c_set_clientdata(i2c, wm8971);