Merge branch 'for-2.6.36' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/asoc...
[pandora-kernel.git] / sound / soc / codecs / twl4030.c
index c667ca5..8d36bfa 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/i2c/twl.h>
+#include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -262,6 +263,17 @@ static inline void twl4030_check_defaults(struct snd_soc_codec *codec)
                 difference, difference ? "Not OK" : "OK");
 }
 
+static inline void twl4030_reset_registers(struct snd_soc_codec *codec)
+{
+       int i;
+
+       /* set all audio section registers to reasonable defaults */
+       for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
+               if (i != TWL4030_REG_APLL_CTL)
+                       twl4030_write(codec, i, twl4030_reg[i]);
+
+}
+
 static void twl4030_init_chip(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -275,6 +287,10 @@ static void twl4030_init_chip(struct platform_device *pdev)
        if (setup && setup->check_defaults)
                twl4030_check_defaults(codec);
 
+       /* Reset registers, if no setup data or if instructed to do so */
+       if (!setup || (setup && setup->reset_registers))
+               twl4030_reset_registers(codec);
+
        /* Refresh APLL_CTL register from HW */
        twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
                            TWL4030_REG_APLL_CTL);
@@ -1818,13 +1834,6 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       if (mode != old_mode) {
-               /* change rate and set CODECPDZ */
-               twl4030_codec_enable(codec, 0);
-               twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
-               twl4030_codec_enable(codec, 1);
-       }
-
        /* sample size */
        old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
        format = old_format;
@@ -1842,16 +1851,20 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       if (format != old_format) {
-
-               /* clear CODECPDZ before changing format (codec requirement) */
-               twl4030_codec_enable(codec, 0);
-
-               /* change format */
-               twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
-
-               /* set CODECPDZ afterwards */
-               twl4030_codec_enable(codec, 1);
+       if (format != old_format || mode != old_mode) {
+               if (twl4030->codec_powered) {
+                       /*
+                        * If the codec is powered, than we need to toggle the
+                        * codec power.
+                        */
+                       twl4030_codec_enable(codec, 0);
+                       twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+                       twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+                       twl4030_codec_enable(codec, 1);
+               } else {
+                       twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+                       twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+               }
        }
 
        /* Store the important parameters for the DAI configuration and set
@@ -1901,6 +1914,7 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
                             unsigned int fmt)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
+       struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
        u8 old_format, format;
 
        /* get format */
@@ -1935,15 +1949,17 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
        }
 
        if (format != old_format) {
-
-               /* clear CODECPDZ before changing format (codec requirement) */
-               twl4030_codec_enable(codec, 0);
-
-               /* change format */
-               twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
-
-               /* set CODECPDZ afterwards */
-               twl4030_codec_enable(codec, 1);
+               if (twl4030->codec_powered) {
+                       /*
+                        * If the codec is powered, than we need to toggle the
+                        * codec power.
+                        */
+                       twl4030_codec_enable(codec, 0);
+                       twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+                       twl4030_codec_enable(codec, 1);
+               } else {
+                       twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+               }
        }
 
        return 0;
@@ -2035,6 +2051,7 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->card->codec;
+       struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
        u8 old_mode, mode;
 
        /* Enable voice digital filters */
@@ -2059,10 +2076,17 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
        }
 
        if (mode != old_mode) {
-               /* change rate and set CODECPDZ */
-               twl4030_codec_enable(codec, 0);
-               twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
-               twl4030_codec_enable(codec, 1);
+               if (twl4030->codec_powered) {
+                       /*
+                        * If the codec is powered, than we need to toggle the
+                        * codec power.
+                        */
+                       twl4030_codec_enable(codec, 0);
+                       twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+                       twl4030_codec_enable(codec, 1);
+               } else {
+                       twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+               }
        }
 
        return 0;
@@ -2092,6 +2116,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
                unsigned int fmt)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
+       struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
        u8 old_format, format;
 
        /* get format */
@@ -2123,10 +2148,17 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
        }
 
        if (format != old_format) {
-               /* change format and set CODECPDZ */
-               twl4030_codec_enable(codec, 0);
-               twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
-               twl4030_codec_enable(codec, 1);
+               if (twl4030->codec_powered) {
+                       /*
+                        * If the codec is powered, than we need to toggle the
+                        * codec power.
+                        */
+                       twl4030_codec_enable(codec, 0);
+                       twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
+                       twl4030_codec_enable(codec, 1);
+               } else {
+                       twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
+               }
        }
 
        return 0;
@@ -2235,7 +2267,6 @@ static int twl4030_soc_probe(struct platform_device *pdev)
        socdev->card->codec = codec;
 
        twl4030_init_chip(pdev);
-       twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
        /* register pcms */
        ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
@@ -2256,6 +2287,8 @@ static int twl4030_soc_remove(struct platform_device *pdev)
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_codec *codec = socdev->card->codec;
 
+       /* Reset registers to their chip default before leaving */
+       twl4030_reset_registers(codec);
        twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
        snd_soc_free_pcms(socdev);
        snd_soc_dapm_free(socdev);
@@ -2296,6 +2329,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
        codec->read = twl4030_read_reg_cache;
        codec->write = twl4030_write;
        codec->set_bias_level = twl4030_set_bias_level;
+       codec->idle_bias_off = 1;
        codec->dai = twl4030_dai;
        codec->num_dai = ARRAY_SIZE(twl4030_dai);
        codec->reg_cache_size = sizeof(twl4030_reg);