ASoC: Implement suspend and resume for WM8993
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Sat, 2 Jan 2010 13:15:56 +0000 (13:15 +0000)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Mon, 4 Jan 2010 21:51:13 +0000 (21:51 +0000)
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
sound/soc/codecs/wm8993.c

index 5e32f2ed5fc295e1d7fbf17197c7f68d55714f41..cd2bc05f78cc3a8bdb963f0f38a04d04680903e8 100644 (file)
@@ -227,6 +227,7 @@ struct wm8993_priv {
        int class_w_users;
        unsigned int fll_fref;
        unsigned int fll_fout;
+       int fll_src;
 };
 
 static unsigned int wm8993_read_hw(struct snd_soc_codec *codec, u8 reg)
@@ -506,6 +507,7 @@ static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
 
        wm8993->fll_fref = Fref;
        wm8993->fll_fout = Fout;
+       wm8993->fll_src = source;
 
        return 0;
 }
@@ -1480,9 +1482,74 @@ static int wm8993_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8993_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct wm8993_priv *wm8993 = codec->private_data;
+       int fll_fout = wm8993->fll_fout;
+       int fll_fref  = wm8993->fll_fref;
+       int ret;
+
+       /* Stop the FLL in an orderly fashion */
+       ret = wm8993_set_fll(codec->dai, 0, 0, 0, 0);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Failed to stop FLL\n");
+               return ret;
+       }
+
+       wm8993->fll_fout = fll_fout;
+       wm8993->fll_fref = fll_fref;
+
+       wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int wm8993_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct wm8993_priv *wm8993 = codec->private_data;
+       u16 *cache = wm8993->reg_cache;
+       int i, ret;
+
+       /* Restore the register settings */
+       for (i = 1; i < WM8993_MAX_REGISTER; i++) {
+               if (cache[i] == wm8993_reg_defaults[i])
+                       continue;
+               snd_soc_write(codec, i, cache[i]);
+       }
+
+       wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       /* Restart the FLL? */
+       if (wm8993->fll_fout) {
+               int fll_fout = wm8993->fll_fout;
+               int fll_fref  = wm8993->fll_fref;
+
+               wm8993->fll_fref = 0;
+               wm8993->fll_fout = 0;
+
+               ret = wm8993_set_fll(codec->dai, 0, wm8993->fll_src,
+                                    fll_fref, fll_fout);
+               if (ret != 0)
+                       dev_err(codec->dev, "Failed to restart FLL\n");
+       }
+
+       return 0;
+}
+#else
+#define wm8993_suspend NULL
+#define wm8993_resume NULL
+#endif
+
 struct snd_soc_codec_device soc_codec_dev_wm8993 = {
        .probe =        wm8993_probe,
        .remove =       wm8993_remove,
+       .suspend =      wm8993_suspend,
+       .resume =       wm8993_resume,
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8993);