[ALSA] ca0106: Add analog mute controls for cards with SPI DAC
authorTrent Piepho <xyzzy@speakeasy.org>
Tue, 24 Jul 2007 10:06:16 +0000 (12:06 +0200)
committerJaroslav Kysela <perex@perex.cz>
Tue, 16 Oct 2007 13:57:55 +0000 (15:57 +0200)
Add four mute controls for the analog output channels for cards that use
an SPI DAC, like the SB0570 SB Live! 24-bit / Audigy SE.  The Wolfson DAC
doesn't support muting left/right so the controls are mono.
The chip state struct gets a 32-byte array to act as a shadow of the spi
dac registers.  Only two registers are used for mute, but more would be
needed for analog gain, de-emphasis, DAC power down, phase inversion, and
other features.

Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
sound/pci/ca0106/ca0106.h
sound/pci/ca0106/ca0106_main.c
sound/pci/ca0106/ca0106_mixer.c

index a0420bc63f0bdbf1cd28fa9dfb20f94be6ffd7dd..7ad03c6afd4223e9689997336a4bb136a165fd62 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
- *  Version: 0.0.21
+ *  Version: 0.0.22
  *
  *  FEATURES currently supported:
  *    See ca0106_main.c for features.
@@ -47,6 +47,8 @@
  *    Added GPIO info for SB Live 24bit.
  *  0.0.21
  *   Implement support for Line-in capture on SB Live 24bit.
+ *  0.0.22
+ *    Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
  *
  *
  *  This code was initally based on code from ALSA's emu10k1x.c which is:
 #define CONTROL_CENTER_LFE_CHANNEL 1
 #define CONTROL_UNKNOWN_CHANNEL 2
 
+
+/* Based on WM8768 Datasheet Rev 4.2 page 32 */
+#define SPI_REG_MASK   0x1ff   /* 16-bit SPI writes have a 7-bit address */
+#define SPI_REG_SHIFT  9       /* followed by 9 bits of data */
+
+/* They really do label the bit for the 4th channel "4" and not "3" */
+#define SPI_DMUTE0_REG         9
+#define SPI_DMUTE1_REG         9
+#define SPI_DMUTE2_REG         9
+#define SPI_DMUTE4_REG         15
+#define SPI_DMUTE0_BIT         3
+#define SPI_DMUTE1_BIT         4
+#define SPI_DMUTE2_BIT         5
+#define SPI_DMUTE4_BIT         2
+
+#define SPI_PHASE0_REG         3
+#define SPI_PHASE1_REG         3
+#define SPI_PHASE2_REG         3
+#define SPI_PHASE4_REG         15
+#define SPI_PHASE0_BIT         6
+#define SPI_PHASE1_BIT         7
+#define SPI_PHASE2_BIT         8
+#define SPI_PHASE4_BIT         3
+
+#define SPI_PDWN_REG           2       /* power down all DACs */
+#define SPI_PDWN_BIT           2
+#define SPI_DACD0_REG          10      /* power down individual DACs */
+#define SPI_DACD1_REG          10
+#define SPI_DACD2_REG          10
+#define SPI_DACD4_REG          15
+#define SPI_DACD0_BIT          1
+#define SPI_DACD1_BIT          2
+#define SPI_DACD2_BIT          3
+#define SPI_DACD4_BIT          1
+
+#define SPI_PWRDNALL_REG       10      /* power down everything */
+#define SPI_PWRDNALL_BIT       4
+
 #include "ca_midi.h"
 
 struct snd_ca0106;
@@ -611,6 +651,8 @@ struct snd_ca0106 {
 
        struct snd_ca_midi midi;
        struct snd_ca_midi midi2;
+
+       u16 spi_dac_reg[16];
 };
 
 int snd_ca0106_mixer(struct snd_ca0106 *emu);
@@ -627,4 +669,5 @@ void snd_ca0106_ptr_write(struct snd_ca0106 *emu,
 
 int snd_ca0106_i2c_write(struct snd_ca0106 *emu, u32 reg, u32 value);
 
-
+int snd_ca0106_spi_write(struct snd_ca0106 * emu,
+                                  unsigned int data);
index 58ffa0cacfb12b5cde8f2cdc8e567188ebdecfb2..512fda946c66302e86e627ece99483963dd8b8d3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
- *  Version: 0.0.23
+ *  Version: 0.0.24
  *
  *  FEATURES currently supported:
  *    Front, Rear and Center/LFE.
@@ -79,6 +79,8 @@
  *    Add support for MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97. From kiksen, bug #901
  *  0.0.23
  *    Implement support for Line-in capture on SB Live 24bit.
+ *  0.0.24
+ *    Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
  *
  *  BUGS:
  *    Some stability problems when unloading the snd-ca0106 kernel module.
@@ -1484,8 +1486,13 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
                int size, n;
 
                size = ARRAY_SIZE(spi_dac_init);
-               for (n=0; n < size; n++)
+               for (n = 0; n < size; n++) {
+                       int reg = spi_dac_init[n] >> SPI_REG_SHIFT;
+
                        snd_ca0106_spi_write(chip, spi_dac_init[n]);
+                       if (reg < ARRAY_SIZE(chip->spi_dac_reg))
+                               chip->spi_dac_reg[reg] = spi_dac_init[n];
+               }
        }
 
        if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
index a135b9c4c3c87ec2de0751cdac68310847fdc881..7fbfe17438b42dde36def37a24c9e9263db5058b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
- *  Version: 0.0.17
+ *  Version: 0.0.18
  *
  *  FEATURES currently supported:
  *    See ca0106_main.c for features.
@@ -39,6 +39,8 @@
  *    Modified Copyright message.
  *  0.0.17
  *    Implement Mic and Line in Capture.
+ *  0.0.18
+ *    Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
  *
  *  This code was initally based on code from ALSA's emu10k1x.c which is:
  *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
@@ -462,6 +464,42 @@ static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+#define spi_mute_info  snd_ctl_boolean_mono_info
+
+static int spi_mute_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
+       unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
+
+       ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit);
+       return 0;
+}
+
+static int spi_mute_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT;
+       unsigned int bit = kcontrol->private_value & SPI_REG_MASK;
+       int ret;
+
+       ret = emu->spi_dac_reg[reg] & bit;
+       if (ucontrol->value.integer.value[0]) {
+               if (!ret)       /* bit already cleared, do nothing */
+                       return 0;
+               emu->spi_dac_reg[reg] &= ~bit;
+       } else {
+               if (ret)        /* bit already set, do nothing */
+                       return 0;
+               emu->spi_dac_reg[reg] |= bit;
+       }
+
+       ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]);
+       return ret ? -1 : 1;
+}
+
 #define CA_VOLUME(xname,chid,reg) \
 {                                                              \
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,     \
@@ -554,6 +592,28 @@ static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata =
         I2C_VOLUME("Aux Capture Volume", 3),
 };
 
+#define SPI_SWITCH(xname,reg,bit) \
+{                                                              \
+       .iface  = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,    \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,              \
+       .info   = spi_mute_info,                                \
+       .get    = spi_mute_get,                                 \
+       .put    = spi_mute_put,                                 \
+       .private_value = (reg<<SPI_REG_SHIFT) | (1<<bit)        \
+}
+
+static struct snd_kcontrol_new snd_ca0106_volume_spi_dac_ctls[]
+__devinitdata = {
+       SPI_SWITCH("Analog Front Playback Switch",
+                  SPI_DMUTE4_REG, SPI_DMUTE4_BIT),
+       SPI_SWITCH("Analog Rear Playback Switch",
+                  SPI_DMUTE0_REG, SPI_DMUTE0_BIT),
+       SPI_SWITCH("Analog Center/LFE Playback Switch",
+                  SPI_DMUTE2_REG, SPI_DMUTE2_BIT),
+       SPI_SWITCH("Analog Side Playback Switch",
+                  SPI_DMUTE1_REG, SPI_DMUTE1_BIT),
+};
+
 static int __devinit remove_ctl(struct snd_card *card, const char *name)
 {
        struct snd_ctl_elem_id id;
@@ -650,6 +710,13 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
                if (err < 0)
                        return err;
        }
+       if (emu->details->spi_dac == 1) {
+               for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_spi_dac_ctls); i++) {
+                       err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_spi_dac_ctls[i], emu));
+                       if (err < 0)
+                               return err;
+               }
+       }
         return 0;
 }