Merge branch 'tracing-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / sound / soc / codecs / wm8580.c
index 86c4b24..6bded8c 100644 (file)
@@ -24,6 +24,8 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -187,82 +189,22 @@ struct pll_state {
        unsigned int out;
 };
 
+#define WM8580_NUM_SUPPLIES 3
+static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = {
+       "AVDD",
+       "DVDD",
+       "PVDD",
+};
+
 /* codec private data */
 struct wm8580_priv {
        struct snd_soc_codec codec;
+       struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES];
        u16 reg_cache[WM8580_MAX_REGISTER + 1];
        struct pll_state a;
        struct pll_state b;
 };
 
-
-/*
- * read wm8580 register cache
- */
-static inline unsigned int wm8580_read_reg_cache(struct snd_soc_codec *codec,
-       unsigned int reg)
-{
-       u16 *cache = codec->reg_cache;
-       BUG_ON(reg >= ARRAY_SIZE(wm8580_reg));
-       return cache[reg];
-}
-
-/*
- * write wm8580 register cache
- */
-static inline void wm8580_write_reg_cache(struct snd_soc_codec *codec,
-       unsigned int reg, unsigned int value)
-{
-       u16 *cache = codec->reg_cache;
-
-       cache[reg] = value;
-}
-
-/*
- * write to the WM8580 register space
- */
-static int wm8580_write(struct snd_soc_codec *codec, unsigned int reg,
-       unsigned int value)
-{
-       u8 data[2];
-
-       BUG_ON(reg >= ARRAY_SIZE(wm8580_reg));
-
-       /* Registers are 9 bits wide */
-       value &= 0x1ff;
-
-       switch (reg) {
-       case WM8580_RESET:
-               /* Uncached */
-               break;
-       default:
-               if (value == wm8580_read_reg_cache(codec, reg))
-                       return 0;
-       }
-
-       /* data is
-        *   D15..D9 WM8580 register offset
-        *   D8...D0 register data
-        */
-       data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-       data[1] = value & 0x00ff;
-
-       wm8580_write_reg_cache(codec, reg, value);
-       if (codec->hw_write(codec->control_data, data, 2) == 2)
-               return 0;
-       else
-               return -EIO;
-}
-
-static inline unsigned int wm8580_read(struct snd_soc_codec *codec,
-                                      unsigned int reg)
-{
-       switch (reg) {
-       default:
-               return wm8580_read_reg_cache(codec, reg);
-       }
-}
-
 static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
 
 static int wm8580_out_vu(struct snd_kcontrol *kcontrol,
@@ -271,25 +213,22 @@ static int wm8580_out_vu(struct snd_kcontrol *kcontrol,
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u16 *reg_cache = codec->reg_cache;
        unsigned int reg = mc->reg;
        unsigned int reg2 = mc->rreg;
        int ret;
-       u16 val;
 
        /* Clear the register cache so we write without VU set */
-       wm8580_write_reg_cache(codec, reg, 0);
-       wm8580_write_reg_cache(codec, reg2, 0);
+       reg_cache[reg] = 0;
+       reg_cache[reg2] = 0;
 
        ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
        if (ret < 0)
                return ret;
 
        /* Now write again with the volume update bit set */
-       val = wm8580_read_reg_cache(codec, reg);
-       wm8580_write(codec, reg, val | 0x0100);
-
-       val = wm8580_read_reg_cache(codec, reg2);
-       wm8580_write(codec, reg2, val | 0x0100);
+       snd_soc_update_bits(codec, reg, 0x100, 0x100);
+       snd_soc_update_bits(codec, reg2, 0x100, 0x100);
 
        return 0;
 }
@@ -512,27 +451,27 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
        /* Always disable the PLL - it is not safe to leave it running
         * while reprogramming it.
         */
-       reg = wm8580_read(codec, WM8580_PWRDN2);
-       wm8580_write(codec, WM8580_PWRDN2, reg | pwr_mask);
+       reg = snd_soc_read(codec, WM8580_PWRDN2);
+       snd_soc_write(codec, WM8580_PWRDN2, reg | pwr_mask);
 
        if (!freq_in || !freq_out)
                return 0;
 
-       wm8580_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff);
-       wm8580_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0xff);
-       wm8580_write(codec, WM8580_PLLA3 + offset,
+       snd_soc_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff);
+       snd_soc_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0x1ff);
+       snd_soc_write(codec, WM8580_PLLA3 + offset,
                     (pll_div.k >> 18 & 0xf) | (pll_div.n << 4));
 
-       reg = wm8580_read(codec, WM8580_PLLA4 + offset);
-       reg &= ~0x3f;
+       reg = snd_soc_read(codec, WM8580_PLLA4 + offset);
+       reg &= ~0x1b;
        reg |= pll_div.prescale | pll_div.postscale << 1 |
                pll_div.freqmode << 3;
 
-       wm8580_write(codec, WM8580_PLLA4 + offset, reg);
+       snd_soc_write(codec, WM8580_PLLA4 + offset, reg);
 
        /* All done, turn it on */
-       reg = wm8580_read(codec, WM8580_PWRDN2);
-       wm8580_write(codec, WM8580_PWRDN2, reg & ~pwr_mask);
+       reg = snd_soc_read(codec, WM8580_PWRDN2);
+       snd_soc_write(codec, WM8580_PWRDN2, reg & ~pwr_mask);
 
        return 0;
 }
@@ -547,7 +486,7 @@ static int wm8580_paif_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;
-       u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->id);
+       u16 paifb = snd_soc_read(codec, WM8580_PAIF3 + dai->id);
 
        paifb &= ~WM8580_AIF_LENGTH_MASK;
        /* bit size */
@@ -567,7 +506,7 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       wm8580_write(codec, WM8580_PAIF3 + dai->id, paifb);
+       snd_soc_write(codec, WM8580_PAIF3 + dai->id, paifb);
        return 0;
 }
 
@@ -579,8 +518,8 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
        unsigned int aifb;
        int can_invert_lrclk;
 
-       aifa = wm8580_read(codec, WM8580_PAIF1 + codec_dai->id);
-       aifb = wm8580_read(codec, WM8580_PAIF3 + codec_dai->id);
+       aifa = snd_soc_read(codec, WM8580_PAIF1 + codec_dai->id);
+       aifb = snd_soc_read(codec, WM8580_PAIF3 + codec_dai->id);
 
        aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP);
 
@@ -646,8 +585,8 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
                return -EINVAL;
        }
 
-       wm8580_write(codec, WM8580_PAIF1 + codec_dai->id, aifa);
-       wm8580_write(codec, WM8580_PAIF3 + codec_dai->id, aifb);
+       snd_soc_write(codec, WM8580_PAIF1 + codec_dai->id, aifa);
+       snd_soc_write(codec, WM8580_PAIF3 + codec_dai->id, aifb);
 
        return 0;
 }
@@ -660,7 +599,7 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
 
        switch (div_id) {
        case WM8580_MCLK:
-               reg = wm8580_read(codec, WM8580_PLLB4);
+               reg = snd_soc_read(codec, WM8580_PLLB4);
                reg &= ~WM8580_PLLB4_MCLKOUTSRC_MASK;
 
                switch (div) {
@@ -682,11 +621,11 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
                default:
                        return -EINVAL;
                }
-               wm8580_write(codec, WM8580_PLLB4, reg);
+               snd_soc_write(codec, WM8580_PLLB4, reg);
                break;
 
        case WM8580_DAC_CLKSEL:
-               reg = wm8580_read(codec, WM8580_CLKSEL);
+               reg = snd_soc_read(codec, WM8580_CLKSEL);
                reg &= ~WM8580_CLKSEL_DAC_CLKSEL_MASK;
 
                switch (div) {
@@ -704,11 +643,11 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
                default:
                        return -EINVAL;
                }
-               wm8580_write(codec, WM8580_CLKSEL, reg);
+               snd_soc_write(codec, WM8580_CLKSEL, reg);
                break;
 
        case WM8580_CLKOUTSRC:
-               reg = wm8580_read(codec, WM8580_PLLB4);
+               reg = snd_soc_read(codec, WM8580_PLLB4);
                reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK;
 
                switch (div) {
@@ -730,7 +669,7 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
                default:
                        return -EINVAL;
                }
-               wm8580_write(codec, WM8580_PLLB4, reg);
+               snd_soc_write(codec, WM8580_PLLB4, reg);
                break;
 
        default:
@@ -745,14 +684,14 @@ static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute)
        struct snd_soc_codec *codec = codec_dai->codec;
        unsigned int reg;
 
-       reg = wm8580_read(codec, WM8580_DAC_CONTROL5);
+       reg = snd_soc_read(codec, WM8580_DAC_CONTROL5);
 
        if (mute)
                reg |= WM8580_DAC_CONTROL5_MUTEALL;
        else
                reg &= ~WM8580_DAC_CONTROL5_MUTEALL;
 
-       wm8580_write(codec, WM8580_DAC_CONTROL5, reg);
+       snd_soc_write(codec, WM8580_DAC_CONTROL5, reg);
 
        return 0;
 }
@@ -769,20 +708,20 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec,
        case SND_SOC_BIAS_STANDBY:
                if (codec->bias_level == SND_SOC_BIAS_OFF) {
                        /* Power up and get individual control of the DACs */
-                       reg = wm8580_read(codec, WM8580_PWRDN1);
+                       reg = snd_soc_read(codec, WM8580_PWRDN1);
                        reg &= ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD);
-                       wm8580_write(codec, WM8580_PWRDN1, reg);
+                       snd_soc_write(codec, WM8580_PWRDN1, reg);
 
                        /* Make VMID high impedence */
-                       reg = wm8580_read(codec,  WM8580_ADC_CONTROL1);
+                       reg = snd_soc_read(codec,  WM8580_ADC_CONTROL1);
                        reg &= ~0x100;
-                       wm8580_write(codec, WM8580_ADC_CONTROL1, reg);
+                       snd_soc_write(codec, WM8580_ADC_CONTROL1, reg);
                }
                break;
 
        case SND_SOC_BIAS_OFF:
-               reg = wm8580_read(codec, WM8580_PWRDN1);
-               wm8580_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN);
+               reg = snd_soc_read(codec, WM8580_PWRDN1);
+               snd_soc_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN);
                break;
        }
        codec->bias_level = level;
@@ -893,7 +832,8 @@ struct snd_soc_codec_device soc_codec_dev_wm8580 = {
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
 
-static int wm8580_register(struct wm8580_priv *wm8580)
+static int wm8580_register(struct wm8580_priv *wm8580,
+                          enum snd_soc_control_type control)
 {
        int ret, i;
        struct snd_soc_codec *codec = &wm8580->codec;
@@ -911,8 +851,6 @@ static int wm8580_register(struct wm8580_priv *wm8580)
        codec->private_data = wm8580;
        codec->name = "WM8580";
        codec->owner = THIS_MODULE;
-       codec->read = wm8580_read_reg_cache;
-       codec->write = wm8580_write;
        codec->bias_level = SND_SOC_BIAS_OFF;
        codec->set_bias_level = wm8580_set_bias_level;
        codec->dai = wm8580_dai;
@@ -922,11 +860,34 @@ static int wm8580_register(struct wm8580_priv *wm8580)
 
        memcpy(codec->reg_cache, wm8580_reg, sizeof(wm8580_reg));
 
+       ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               goto err;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(wm8580->supplies); i++)
+               wm8580->supplies[i].supply = wm8580_supply_names[i];
+
+       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8580->supplies),
+                                wm8580->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+               goto err;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies),
+                                   wm8580->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+               goto err_regulator_get;
+       }
+
        /* Get the codec into a known state */
-       ret = wm8580_write(codec, WM8580_RESET, 0);
+       ret = snd_soc_write(codec, WM8580_RESET, 0);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
-               goto err;
+               goto err_regulator_enable;
        }
 
        for (i = 0; i < ARRAY_SIZE(wm8580_dai); i++)
@@ -939,7 +900,7 @@ static int wm8580_register(struct wm8580_priv *wm8580)
        ret = snd_soc_register_codec(codec);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to register codec: %d\n", ret);
-               goto err;
+               goto err_regulator_enable;
        }
 
        ret = snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
@@ -952,6 +913,10 @@ static int wm8580_register(struct wm8580_priv *wm8580)
 
 err_codec:
        snd_soc_unregister_codec(codec);
+err_regulator_enable:
+       regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
+err_regulator_get:
+       regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
 err:
        kfree(wm8580);
        return ret;
@@ -962,6 +927,8 @@ static void wm8580_unregister(struct wm8580_priv *wm8580)
        wm8580_set_bias_level(&wm8580->codec, SND_SOC_BIAS_OFF);
        snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
        snd_soc_unregister_codec(&wm8580->codec);
+       regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
+       regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
        kfree(wm8580);
        wm8580_codec = NULL;
 }
@@ -978,14 +945,13 @@ static int wm8580_i2c_probe(struct i2c_client *i2c,
                return -ENOMEM;
 
        codec = &wm8580->codec;
-       codec->hw_write = (hw_write_t)i2c_master_send;
 
        i2c_set_clientdata(i2c, wm8580);
        codec->control_data = i2c;
 
        codec->dev = &i2c->dev;
 
-       return wm8580_register(wm8580);
+       return wm8580_register(wm8580, SND_SOC_I2C);
 }
 
 static int wm8580_i2c_remove(struct i2c_client *client)
@@ -995,6 +961,21 @@ static int wm8580_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8580_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8580_i2c_resume(struct i2c_client *client)
+{
+       return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8580_i2c_suspend NULL
+#define wm8580_i2c_resume NULL
+#endif
+
 static const struct i2c_device_id wm8580_i2c_id[] = {
        { "wm8580", 0 },
        { }
@@ -1008,6 +989,8 @@ static struct i2c_driver wm8580_i2c_driver = {
        },
        .probe =    wm8580_i2c_probe,
        .remove =   wm8580_i2c_remove,
+       .suspend =  wm8580_i2c_suspend,
+       .resume =   wm8580_i2c_resume,
        .id_table = wm8580_i2c_id,
 };
 #endif