/*
* 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.
* 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>
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
-#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
-static int snd_ca0106_shared_spdif_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
- return 0;
-}
+#define snd_ca0106_shared_spdif_info snd_ctl_boolean_mono_info
static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
- ucontrol->value.enumerated.item[0] = emu->spdif_enable;
+ ucontrol->value.integer.value[0] = emu->spdif_enable;
return 0;
}
int change = 0;
u32 mask;
- val = ucontrol->value.enumerated.item[0] ;
+ val = !!ucontrol->value.integer.value[0];
change = (emu->spdif_enable != val);
if (change) {
emu->spdif_enable = val;
- if (val == 1) {
+ if (val) {
/* Digital */
snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
u32 source;
val = ucontrol->value.enumerated.item[0] ;
+ if (val >= 6)
+ return -EINVAL;
change = (emu->capture_source != val);
if (change) {
emu->capture_source = val;
* for the particular source.
*/
source_id = ucontrol->value.enumerated.item[0] ;
+ if (source_id >= 4)
+ return -EINVAL;
change = (emu->i2c_capture_source != source_id);
if (change) {
snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
u32 tmp;
val = ucontrol->value.enumerated.item[0] ;
+ if (val > 1)
+ return -EINVAL;
change = (emu->capture_mic_line_in != val);
if (change) {
emu->capture_mic_line_in = val;
ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
ngain = ucontrol->value.integer.value[0];
if (ngain > 0xff)
- return 0;
+ return -EINVAL;
if (ogain != ngain) {
if (emu->i2c_capture_source == source_id)
snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
ngain = ucontrol->value.integer.value[1];
if (ngain > 0xff)
- return 0;
+ return -EINVAL;
if (ogain != ngain) {
if (emu->i2c_capture_source == source_id)
snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
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 ? -EINVAL : 1;
+}
+
#define CA_VOLUME(xname,chid,reg) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
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) | (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;
return -ENOENT;
}
+#define ADD_CTLS(emu, ctls) \
+ do { \
+ int i, err; \
+ for (i = 0; i < ARRAY_SIZE(ctls); i++) { \
+ err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
+ if (err < 0) \
+ return err; \
+ } \
+ } while (0)
+
int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
{
- int i, err;
+ int err;
struct snd_card *card = emu->card;
char **c;
static char *ca0106_remove_ctls[] = {
rename_ctl(card, c[0], c[1]);
#endif
- for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_ctls); i++) {
- err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_ctls[i], emu));
- if (err < 0)
- return err;
- }
+ ADD_CTLS(emu, snd_ca0106_volume_ctls);
if (emu->details->i2c_adc == 1) {
- for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_i2c_adc_ctls); i++) {
- err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_i2c_adc_ctls[i], emu));
- if (err < 0)
- return err;
- }
+ ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls);
if (emu->details->gpio_type == 1)
err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
else /* gpio_type == 2 */
if (err < 0)
return err;
}
+ if (emu->details->spi_dac == 1)
+ ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls);
return 0;
}