ALSA: virtuoso: fix Xonar DS input switches
[pandora-kernel.git] / sound / pci / oxygen / xonar_wm87x6.c
index dbc4b89..fb3f95c 100644 (file)
  * GPIO 6 -> route input jack to input 1/2 (1/0)
  * GPIO 7 -> enable output to speakers
  * GPIO 8 -> enable output to speakers
+ *
+ * WM8766:
+ *
+ * input 1 <- line
+ * input 2 <- mic
+ * input 3 <- front mic
+ * input 4 <- aux
  */
 
 #include <linux/pci.h>
@@ -53,6 +60,8 @@ struct xonar_wm87x6 {
        struct xonar_generic generic;
        u16 wm8776_regs[0x17];
        u16 wm8766_regs[0x10];
+       struct snd_kcontrol *line_adcmux_control;
+       struct snd_kcontrol *mic_adcmux_control;
        struct snd_kcontrol *lc_controls[13];
 };
 
@@ -95,8 +104,12 @@ static void wm8766_write(struct oxygen *chip,
                         (0 << OXYGEN_SPI_CODEC_SHIFT) |
                         OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
                         (reg << 9) | value);
-       if (reg < ARRAY_SIZE(data->wm8766_regs))
+       if (reg < ARRAY_SIZE(data->wm8766_regs)) {
+               if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) ||
+                   (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA))
+                       value &= ~WM8766_UPDATE;
                data->wm8766_regs[reg] = value;
+       }
 }
 
 static void wm8766_write_cached(struct oxygen *chip,
@@ -105,12 +118,8 @@ static void wm8766_write_cached(struct oxygen *chip,
        struct xonar_wm87x6 *data = chip->model_data;
 
        if (reg >= ARRAY_SIZE(data->wm8766_regs) ||
-           value != data->wm8766_regs[reg]) {
-               if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) ||
-                   (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA))
-                       value &= ~WM8766_UPDATE;
+           value != data->wm8766_regs[reg])
                wm8766_write(chip, reg, value);
-       }
 }
 
 static void wm8776_registers_init(struct oxygen *chip)
@@ -193,6 +202,7 @@ static void xonar_ds_init(struct oxygen *chip)
 static void xonar_ds_cleanup(struct oxygen *chip)
 {
        xonar_disable_output(chip);
+       wm8776_write(chip, WM8776_RESET, 0);
 }
 
 static void xonar_ds_suspend(struct oxygen *chip)
@@ -603,6 +613,7 @@ static int wm8776_input_mux_put(struct snd_kcontrol *ctl,
 {
        struct oxygen *chip = ctl->private_data;
        struct xonar_wm87x6 *data = chip->model_data;
+       struct snd_kcontrol *other_ctl;
        unsigned int mux_bit = ctl->private_value;
        u16 reg;
        int changed;
@@ -610,8 +621,18 @@ static int wm8776_input_mux_put(struct snd_kcontrol *ctl,
        mutex_lock(&chip->mutex);
        reg = data->wm8776_regs[WM8776_ADCMUX];
        if (value->value.integer.value[0]) {
-               reg &= ~0x003;
                reg |= mux_bit;
+               /* line-in and mic-in are exclusive */
+               mux_bit ^= 3;
+               if (reg & mux_bit) {
+                       reg &= ~mux_bit;
+                       if (mux_bit == 1)
+                               other_ctl = data->line_adcmux_control;
+                       else
+                               other_ctl = data->mic_adcmux_control;
+                       snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                                      &other_ctl->id);
+               }
        } else
                reg &= ~mux_bit;
        changed = reg != data->wm8776_regs[WM8776_ADCMUX];
@@ -882,7 +903,10 @@ static const struct snd_kcontrol_new ds_controls[] = {
                .put = wm8776_input_mux_put,
                .private_value = 1 << 1,
        },
-       WM8776_BIT_SWITCH("Aux", WM8776_ADCMUX, 1 << 2, 0, 0),
+       WM8776_BIT_SWITCH("Front Mic Capture Switch",
+                         WM8776_ADCMUX, 1 << 2, 0, 0),
+       WM8776_BIT_SWITCH("Aux Capture Switch",
+                         WM8776_ADCMUX, 1 << 3, 0, 0),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "ADC Filter Capture Enum",
@@ -963,7 +987,13 @@ static int xonar_ds_mixer_init(struct oxygen *chip)
                err = snd_ctl_add(chip->card, ctl);
                if (err < 0)
                        return err;
+               if (!strcmp(ctl->id.name, "Line Capture Switch"))
+                       data->line_adcmux_control = ctl;
+               else if (!strcmp(ctl->id.name, "Mic Capture Switch"))
+                       data->mic_adcmux_control = ctl;
        }
+       if (!data->line_adcmux_control || !data->mic_adcmux_control)
+               return -ENXIO;
        BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
        for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
                ctl = snd_ctl_new1(&lc_controls[i], chip);