]> err.no Git - linux-2.6/blobdiff - sound/soc/codecs/tlv320aic3x.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/hskinnemoen/avr32-2.6
[linux-2.6] / sound / soc / codecs / tlv320aic3x.c
index 738b3b634d74e3e901fa77df5b8ce67559732227..b1dce5f459db6d40663112ecb2e3f8eb13bb30a9 100644 (file)
@@ -29,7 +29,7 @@
  *  ---------------------------------------
  *
  *  Hence the machine layer should disable unsupported inputs/outputs by
- *  snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0), etc.
+ *  snd_soc_dapm_disable_pin(codec, "MONO_LOUT"), etc.
  */
 
 #include <linux/module.h>
@@ -138,6 +138,20 @@ static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg,
                return -EIO;
 }
 
+/*
+ * read from the aic3x register space
+ */
+static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
+                     u8 *value)
+{
+       *value = reg & 0xff;
+       if (codec->hw_read(codec->control_data, value, 1) != 1)
+               return -EIO;
+
+       aic3x_write_reg_cache(codec, reg, *value);
+       return 0;
+}
+
 #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_volsw, \
@@ -192,7 +206,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
                }
 
                if (found)
-                       snd_soc_dapm_sync_endpoints(widget->codec);
+                       snd_soc_dapm_sync(widget->codec);
        }
 
        ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
@@ -209,6 +223,8 @@ static const char *aic3x_right_hpcom_mux[] =
     { "differential of HPROUT", "constant VCM", "single-ended",
       "differential of HPLCOM", "external feedback" };
 static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" };
+static const char *aic3x_adc_hpf[] =
+    { "Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" };
 
 #define LDAC_ENUM      0
 #define RDAC_ENUM      1
@@ -218,6 +234,7 @@ static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" };
 #define LINE1R_ENUM    5
 #define LINE2L_ENUM    6
 #define LINE2R_ENUM    7
+#define ADC_HPF_ENUM   8
 
 static const struct soc_enum aic3x_enum[] = {
        SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux),
@@ -228,6 +245,7 @@ static const struct soc_enum aic3x_enum[] = {
        SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
        SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
        SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
+       SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf),
 };
 
 static const struct snd_kcontrol_new aic3x_snd_controls[] = {
@@ -278,6 +296,8 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
        /* Input */
        SOC_DOUBLE_R("PGA Capture Volume", LADC_VOL, RADC_VOL, 0, 0x7f, 0),
        SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1),
+
+       SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
 };
 
 /* add non dapm controls */
@@ -441,11 +461,34 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
        SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
                         &aic3x_right_line2_mux_controls),
 
+       /*
+        * Not a real mic bias widget but similar function. This is for dynamic
+        * control of GPIO1 digital mic modulator clock output function when
+        * using digital mic.
+        */
+       SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "GPIO1 dmic modclk",
+                        AIC3X_GPIO1_REG, 4, 0xf,
+                        AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK,
+                        AIC3X_GPIO1_FUNC_DISABLED),
+
+       /*
+        * Also similar function like mic bias. Selects digital mic with
+        * configurable oversampling rate instead of ADC converter.
+        */
+       SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 128",
+                        AIC3X_ASD_INTF_CTRLA, 0, 3, 1, 0),
+       SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 64",
+                        AIC3X_ASD_INTF_CTRLA, 0, 3, 2, 0),
+       SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 32",
+                        AIC3X_ASD_INTF_CTRLA, 0, 3, 3, 0),
+
        /* Mic Bias */
-       SND_SOC_DAPM_MICBIAS("Mic Bias 2V", MICBIAS_CTRL, 6, 0),
-       SND_SOC_DAPM_MICBIAS("Mic Bias 2.5V", MICBIAS_CTRL, 7, 0),
-       SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 6, 0),
-       SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 7, 0),
+       SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2V",
+                        MICBIAS_CTRL, 6, 3, 1, 0),
+       SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2.5V",
+                        MICBIAS_CTRL, 6, 3, 2, 0),
+       SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias AVDD",
+                        MICBIAS_CTRL, 6, 3, 3, 0),
 
        /* Left PGA to Left Output bypass */
        SND_SOC_DAPM_MIXER("Left PGA Bypass Mixer", SND_SOC_NOPM, 0, 0,
@@ -483,7 +526,7 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
        SND_SOC_DAPM_INPUT("LINE2R"),
 };
 
-static const char *intercon[][3] = {
+static const struct snd_soc_dapm_route intercon[] = {
        /* Left Output */
        {"Left DAC Mux", "DAC_L1", "Left DAC"},
        {"Left DAC Mux", "DAC_L2", "Left DAC"},
@@ -554,6 +597,7 @@ static const char *intercon[][3] = {
        {"Left PGA Mixer", "Mic3L Switch", "MIC3L"},
 
        {"Left ADC", NULL, "Left PGA Mixer"},
+       {"Left ADC", NULL, "GPIO1 dmic modclk"},
 
        /* Right Input */
        {"Right Line1R Mux", "single-ended", "LINE1R"},
@@ -567,6 +611,7 @@ static const char *intercon[][3] = {
        {"Right PGA Mixer", "Mic3R Switch", "MIC3R"},
 
        {"Right ADC", NULL, "Right PGA Mixer"},
+       {"Right ADC", NULL, "GPIO1 dmic modclk"},
 
        /* Left PGA Bypass */
        {"Left PGA Bypass Mixer", "Line Switch", "Left PGA Mixer"},
@@ -628,21 +673,22 @@ static const char *intercon[][3] = {
        {"Mono Out", NULL, "Right Line2 Bypass Mixer"},
        {"Right HP Out", NULL, "Right Line2 Bypass Mixer"},
 
-       /* terminator */
-       {NULL, NULL, NULL},
+       /*
+        * Logical path between digital mic enable and GPIO1 modulator clock
+        * output function
+        */
+       {"GPIO1 dmic modclk", NULL, "DMic Rate 128"},
+       {"GPIO1 dmic modclk", NULL, "DMic Rate 64"},
+       {"GPIO1 dmic modclk", NULL, "DMic Rate 32"},
 };
 
 static int aic3x_add_widgets(struct snd_soc_codec *codec)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++)
-               snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]);
+       snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets,
+                                 ARRAY_SIZE(aic3x_dapm_widgets));
 
        /* set up audio path interconnects */
-       for (i = 0; intercon[i][0] != NULL; i++)
-               snd_soc_dapm_connect_input(codec, intercon[i][0],
-                                          intercon[i][1], intercon[i][2]);
+       snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
 
        snd_soc_dapm_new_widgets(codec);
        return 0;
@@ -768,7 +814,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int aic3x_mute(struct snd_soc_codec_dai *dai, int mute)
+static int aic3x_mute(struct snd_soc_dai *dai, int mute)
 {
        struct snd_soc_codec *codec = dai->codec;
        u8 ldac_reg = aic3x_read_reg_cache(codec, LDAC_VOL) & ~MUTE_ON;
@@ -785,7 +831,7 @@ static int aic3x_mute(struct snd_soc_codec_dai *dai, int mute)
        return 0;
 }
 
-static int aic3x_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int aic3x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
                                int clk_id, unsigned int freq, int dir)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
@@ -795,13 +841,15 @@ static int aic3x_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
        return 0;
 }
 
-static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
                             unsigned int fmt)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
        struct aic3x_priv *aic3x = codec->private_data;
-       u8 iface_areg = 0;
-       u8 iface_breg = 0;
+       u8 iface_areg, iface_breg;
+
+       iface_areg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
+       iface_breg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
 
        /* set master/slave audio interface */
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -840,13 +888,14 @@ static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
        return 0;
 }
 
-static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
+static int aic3x_set_bias_level(struct snd_soc_codec *codec,
+                               enum snd_soc_bias_level level)
 {
        struct aic3x_priv *aic3x = codec->private_data;
        u8 reg;
 
-       switch (event) {
-       case SNDRV_CTL_POWER_D0:
+       switch (level) {
+       case SND_SOC_BIAS_ON:
                /* all power is driven by DAPM system */
                if (aic3x->master) {
                        /* enable pll */
@@ -855,10 +904,9 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
                                    reg | PLL_ENABLE);
                }
                break;
-       case SNDRV_CTL_POWER_D1:
-       case SNDRV_CTL_POWER_D2:
+       case SND_SOC_BIAS_PREPARE:
                break;
-       case SNDRV_CTL_POWER_D3hot:
+       case SND_SOC_BIAS_STANDBY:
                /*
                 * all power is driven by DAPM system,
                 * so output power is safe if bypass was set
@@ -870,7 +918,7 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
                                    reg & ~PLL_ENABLE);
                }
                break;
-       case SNDRV_CTL_POWER_D3cold:
+       case SND_SOC_BIAS_OFF:
                /* force all power off */
                reg = aic3x_read_reg_cache(codec, LINE1L_2_LADC_CTRL);
                aic3x_write(codec, LINE1L_2_LADC_CTRL, reg & ~LADC_PWR_ON);
@@ -906,16 +954,43 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
                }
                break;
        }
-       codec->dapm_state = event;
+       codec->bias_level = level;
 
        return 0;
 }
 
+void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state)
+{
+       u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG;
+       u8 bit = gpio ? 3: 0;
+       u8 val = aic3x_read_reg_cache(codec, reg) & ~(1 << bit);
+       aic3x_write(codec, reg, val | (!!state << bit));
+}
+EXPORT_SYMBOL_GPL(aic3x_set_gpio);
+
+int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio)
+{
+       u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG;
+       u8 val, bit = gpio ? 2: 1;
+
+       aic3x_read(codec, reg, &val);
+       return (val >> bit) & 1;
+}
+EXPORT_SYMBOL_GPL(aic3x_get_gpio);
+
+int aic3x_headset_detected(struct snd_soc_codec *codec)
+{
+       u8 val;
+       aic3x_read(codec, AIC3X_RT_IRQ_FLAGS_REG, &val);
+       return (val >> 2) & 1;
+}
+EXPORT_SYMBOL_GPL(aic3x_headset_detected);
+
 #define AIC3X_RATES    SNDRV_PCM_RATE_8000_96000
 #define AIC3X_FORMATS  (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
                         SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
 
-struct snd_soc_codec_dai aic3x_dai = {
+struct snd_soc_dai aic3x_dai = {
        .name = "aic3x",
        .playback = {
                .stream_name = "Playback",
@@ -945,7 +1020,7 @@ static int aic3x_suspend(struct platform_device *pdev, pm_message_t state)
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_codec *codec = socdev->codec;
 
-       aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+       aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
        return 0;
 }
@@ -965,7 +1040,7 @@ static int aic3x_resume(struct platform_device *pdev)
                codec->hw_write(codec->control_data, data, 2);
        }
 
-       aic3x_dapm_event(codec, codec->suspend_dapm_state);
+       aic3x_set_bias_level(codec, codec->suspend_bias_level);
 
        return 0;
 }
@@ -977,16 +1052,17 @@ static int aic3x_resume(struct platform_device *pdev)
 static int aic3x_init(struct snd_soc_device *socdev)
 {
        struct snd_soc_codec *codec = socdev->codec;
+       struct aic3x_setup_data *setup = socdev->codec_data;
        int reg, ret = 0;
 
        codec->name = "aic3x";
        codec->owner = THIS_MODULE;
        codec->read = aic3x_read_reg_cache;
        codec->write = aic3x_write;
-       codec->dapm_event = aic3x_dapm_event;
+       codec->set_bias_level = aic3x_set_bias_level;
        codec->dai = &aic3x_dai;
        codec->num_dai = 1;
-       codec->reg_cache_size = sizeof(aic3x_reg);
+       codec->reg_cache_size = ARRAY_SIZE(aic3x_reg);
        codec->reg_cache = kmemdup(aic3x_reg, sizeof(aic3x_reg), GFP_KERNEL);
        if (codec->reg_cache == NULL)
                return -ENOMEM;
@@ -1065,7 +1141,11 @@ static int aic3x_init(struct snd_soc_device *socdev)
        aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
 
        /* off, with power on */
-       aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+       aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       /* setup GPIO functions */
+       aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4);
+       aic3x_write(codec, AIC3X_GPIO2_REG, (setup->gpio_func[1] & 0xf) << 4);
 
        aic3x_add_controls(codec);
        aic3x_add_widgets(codec);
@@ -1174,6 +1254,12 @@ static struct i2c_client client_template = {
        .name = "AIC3X",
        .driver = &aic3x_i2c_driver,
 };
+
+static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len)
+{
+       value[0] = i2c_smbus_read_byte_data(client, value[0]);
+       return (len == 1);
+}
 #endif
 
 static int aic3x_probe(struct platform_device *pdev)
@@ -1208,6 +1294,7 @@ static int aic3x_probe(struct platform_device *pdev)
        if (setup->i2c_address) {
                normal_i2c[0] = setup->i2c_address;
                codec->hw_write = (hw_write_t) i2c_master_send;
+               codec->hw_read = (hw_read_t) aic3x_i2c_read;
                ret = i2c_add_driver(&aic3x_i2c_driver);
                if (ret != 0)
                        printk(KERN_ERR "can't add i2c driver");
@@ -1225,7 +1312,7 @@ static int aic3x_remove(struct platform_device *pdev)
 
        /* power down chip */
        if (codec->control_data)
-               aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3);
+               aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
        snd_soc_free_pcms(socdev);
        snd_soc_dapm_free(socdev);