diff options
Diffstat (limited to 'sound/pci/ice1712')
-rw-r--r-- | sound/pci/ice1712/Makefile | 2 | ||||
-rw-r--r-- | sound/pci/ice1712/ak4xxx.c | 1 | ||||
-rw-r--r-- | sound/pci/ice1712/amp.c | 1 | ||||
-rw-r--r-- | sound/pci/ice1712/aureon.c | 133 | ||||
-rw-r--r-- | sound/pci/ice1712/delta.c | 3 | ||||
-rw-r--r-- | sound/pci/ice1712/ews.c | 92 | ||||
-rw-r--r-- | sound/pci/ice1712/hoontech.c | 188 | ||||
-rw-r--r-- | sound/pci/ice1712/ice1712.c | 2 | ||||
-rw-r--r-- | sound/pci/ice1712/ice1712.h | 37 | ||||
-rw-r--r-- | sound/pci/ice1712/ice1724.c | 9 | ||||
-rw-r--r-- | sound/pci/ice1712/juli.c | 23 | ||||
-rw-r--r-- | sound/pci/ice1712/phase.c | 96 | ||||
-rw-r--r-- | sound/pci/ice1712/pontis.c | 1 | ||||
-rw-r--r-- | sound/pci/ice1712/prodigy192.c | 297 | ||||
-rw-r--r-- | sound/pci/ice1712/prodigy_hifi.c | 1210 | ||||
-rw-r--r-- | sound/pci/ice1712/prodigy_hifi.h | 38 | ||||
-rw-r--r-- | sound/pci/ice1712/revo.c | 24 | ||||
-rw-r--r-- | sound/pci/ice1712/se.c | 774 | ||||
-rw-r--r-- | sound/pci/ice1712/se.h | 15 | ||||
-rw-r--r-- | sound/pci/ice1712/vt1720_mobo.c | 1 | ||||
-rw-r--r-- | sound/pci/ice1712/wtm.c | 9 |
21 files changed, 2547 insertions, 409 deletions
diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile index 65ce66a..f99fe08 100644 --- a/sound/pci/ice1712/Makefile +++ b/sound/pci/ice1712/Makefile @@ -5,7 +5,7 @@ snd-ice17xx-ak4xxx-objs := ak4xxx.o snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o -snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o juli.o phase.o wtm.o +snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o # Toplevel Module Dependency obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o diff --git a/sound/pci/ice1712/ak4xxx.c b/sound/pci/ice1712/ak4xxx.c index a1aba0d..dab31b2 100644 --- a/sound/pci/ice1712/ak4xxx.c +++ b/sound/pci/ice1712/ak4xxx.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c index 6e13d75..3756430 100644 --- a/sound/pci/ice1712/amp.c +++ b/sound/pci/ice1712/amp.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index ec0699c..868ae29 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -47,7 +47,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -62,6 +61,15 @@ #include "aureon.h" #include <sound/tlv.h> +/* AC97 register cache for Aureon */ +struct aureon_spec { + unsigned short stac9744[64]; + unsigned int cs8415_mux; + unsigned short master[2]; + unsigned short vol[8]; + unsigned char pca9554_out; +}; + /* WM8770 registers */ #define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */ #define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */ @@ -205,7 +213,8 @@ static int aureon_universe_inmux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = ice->spec.aureon.pca9554_out; + struct aureon_spec *spec = ice->spec; + ucontrol->value.enumerated.item[0] = spec->pca9554_out; return 0; } @@ -213,16 +222,18 @@ static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; unsigned char oval, nval; int change; + nval = ucontrol->value.enumerated.item[0]; + if (nval >= 3) + return -EINVAL; snd_ice1712_save_gpio_status(ice); - - oval = ice->spec.aureon.pca9554_out; - nval = ucontrol->value.integer.value[0]; + oval = spec->pca9554_out; if ((change = (oval != nval))) { aureon_pca9554_write(ice, PCA9554_OUT, nval); - ice->spec.aureon.pca9554_out = nval; + spec->pca9554_out = nval; } snd_ice1712_restore_gpio_status(ice); @@ -233,6 +244,7 @@ static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol, static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg, unsigned short val) { + struct aureon_spec *spec = ice->spec; unsigned int tmp; /* Send address to XILINX chip */ @@ -280,12 +292,13 @@ static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg, udelay(10); /* Store the data in out private buffer */ - ice->spec.aureon.stac9744[(reg & 0x7F) >> 1] = val; + spec->stac9744[(reg & 0x7F) >> 1] = val; } static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short reg) { - return ice->spec.aureon.stac9744[(reg & 0x7F) >> 1]; + struct aureon_spec *spec = ice->spec; + return spec->stac9744[(reg & 0x7F) >> 1]; } /* @@ -293,6 +306,7 @@ static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short r */ static int aureon_ac97_init (struct snd_ice1712 *ice) { + struct aureon_spec *spec = ice->spec; int i; static const unsigned short ac97_defaults[] = { 0x00, 0x9640, @@ -330,9 +344,9 @@ static int aureon_ac97_init (struct snd_ice1712 *ice) snd_ice1712_gpio_write(ice, tmp); udelay(3); - memset(&ice->spec.aureon.stac9744, 0, sizeof(ice->spec.aureon.stac9744)); + memset(&spec->stac9744, 0, sizeof(spec->stac9744)); for (i=0; ac97_defaults[i] != (unsigned short)-1; i+=2) - ice->spec.aureon.stac9744[(ac97_defaults[i]) >> 1] = ac97_defaults[i+1]; + spec->stac9744[(ac97_defaults[i]) >> 1] = ac97_defaults[i+1]; aureon_ac97_write(ice, AC97_MASTER, 0x0000); // Unmute AC'97 master volume permanently - muting is done by WM8770 @@ -744,27 +758,33 @@ static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; int i; for (i=0; i<2; i++) - ucontrol->value.integer.value[i] = ice->spec.aureon.master[i] & ~WM_VOL_MUTE; + ucontrol->value.integer.value[i] = + spec->master[i] & ~WM_VOL_MUTE; return 0; } static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; int ch, change = 0; snd_ice1712_save_gpio_status(ice); for (ch = 0; ch < 2; ch++) { - if (ucontrol->value.integer.value[ch] != ice->spec.aureon.master[ch]) { + unsigned int vol = ucontrol->value.integer.value[ch]; + if (vol > WM_VOL_MAX) + continue; + vol |= spec->master[ch] & WM_VOL_MUTE; + if (vol != spec->master[ch]) { int dac; - ice->spec.aureon.master[ch] &= WM_VOL_MUTE; - ice->spec.aureon.master[ch] |= ucontrol->value.integer.value[ch]; + spec->master[ch] = vol; for (dac = 0; dac < ice->num_total_dacs; dac += 2) wm_set_vol(ice, WM_DAC_ATTEN + dac + ch, - ice->spec.aureon.vol[dac + ch], - ice->spec.aureon.master[ch]); + spec->vol[dac + ch], + spec->master[ch]); change = 1; } } @@ -788,18 +808,21 @@ static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info * static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; int i, ofs, voices; voices = kcontrol->private_value >> 8; ofs = kcontrol->private_value & 0xff; for (i = 0; i < voices; i++) - ucontrol->value.integer.value[i] = ice->spec.aureon.vol[ofs+i] & ~WM_VOL_MUTE; + ucontrol->value.integer.value[i] = + spec->vol[ofs+i] & ~WM_VOL_MUTE; return 0; } static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; int i, idx, ofs, voices; int change = 0; @@ -807,12 +830,15 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value * ofs = kcontrol->private_value & 0xff; snd_ice1712_save_gpio_status(ice); for (i = 0; i < voices; i++) { - idx = WM_DAC_ATTEN + ofs + i; - if (ucontrol->value.integer.value[i] != ice->spec.aureon.vol[ofs+i]) { - ice->spec.aureon.vol[ofs+i] &= WM_VOL_MUTE; - ice->spec.aureon.vol[ofs+i] |= ucontrol->value.integer.value[i]; - wm_set_vol(ice, idx, ice->spec.aureon.vol[ofs+i], - ice->spec.aureon.master[i]); + unsigned int vol = ucontrol->value.integer.value[i]; + if (vol > 0x7f) + continue; + vol |= spec->vol[ofs+i]; + if (vol != spec->vol[ofs+i]) { + spec->vol[ofs+i] = vol; + idx = WM_DAC_ATTEN + ofs + i; + wm_set_vol(ice, idx, spec->vol[ofs + i], + spec->master[i]); change = 1; } } @@ -834,19 +860,22 @@ static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; int voices, ofs, i; voices = kcontrol->private_value >> 8; ofs = kcontrol->private_value & 0xFF; for (i = 0; i < voices; i++) - ucontrol->value.integer.value[i] = (ice->spec.aureon.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1; + ucontrol->value.integer.value[i] = + (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1; return 0; } static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; int change = 0, voices, ofs, i; voices = kcontrol->private_value >> 8; @@ -854,13 +883,13 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value snd_ice1712_save_gpio_status(ice); for (i = 0; i < voices; i++) { - int val = (ice->spec.aureon.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1; + int val = (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1; if (ucontrol->value.integer.value[i] != val) { - ice->spec.aureon.vol[ofs + i] &= ~WM_VOL_MUTE; - ice->spec.aureon.vol[ofs + i] |= + spec->vol[ofs + i] &= ~WM_VOL_MUTE; + spec->vol[ofs + i] |= ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE; - wm_set_vol(ice, ofs + i, ice->spec.aureon.vol[ofs + i], - ice->spec.aureon.master[i]); + wm_set_vol(ice, ofs + i, spec->vol[ofs + i], + spec->master[i]); change = 1; } } @@ -877,29 +906,33 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; - ucontrol->value.integer.value[0] = (ice->spec.aureon.master[0] & WM_VOL_MUTE) ? 0 : 1; - ucontrol->value.integer.value[1] = (ice->spec.aureon.master[1] & WM_VOL_MUTE) ? 0 : 1; + ucontrol->value.integer.value[0] = + (spec->master[0] & WM_VOL_MUTE) ? 0 : 1; + ucontrol->value.integer.value[1] = + (spec->master[1] & WM_VOL_MUTE) ? 0 : 1; return 0; } static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; int change = 0, i; snd_ice1712_save_gpio_status(ice); for (i = 0; i < 2; i++) { - int val = (ice->spec.aureon.master[i] & WM_VOL_MUTE) ? 0 : 1; + int val = (spec->master[i] & WM_VOL_MUTE) ? 0 : 1; if (ucontrol->value.integer.value[i] != val) { int dac; - ice->spec.aureon.master[i] &= ~WM_VOL_MUTE; - ice->spec.aureon.master[i] |= + spec->master[i] &= ~WM_VOL_MUTE; + spec->master[i] |= ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE; for (dac = 0; dac < ice->num_total_dacs; dac += 2) wm_set_vol(ice, WM_DAC_ATTEN + dac + i, - ice->spec.aureon.vol[dac + i], - ice->spec.aureon.master[i]); + spec->vol[dac + i], + spec->master[i]); change = 1; } } @@ -940,8 +973,10 @@ static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val unsigned short ovol, nvol; int change = 0; - snd_ice1712_save_gpio_status(ice); nvol = ucontrol->value.integer.value[0]; + if (nvol > PCM_RES) + return -EINVAL; + snd_ice1712_save_gpio_status(ice); nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff; ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff; if (ovol != nvol) { @@ -1031,7 +1066,7 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val snd_ice1712_save_gpio_status(ice); for (i = 0; i < 2; i++) { idx = WM_ADC_GAIN + i; - nvol = ucontrol->value.integer.value[i]; + nvol = ucontrol->value.integer.value[i] & 0x1f; ovol = wm_get(ice, idx); if ((ovol & 0x1f) != nvol) { wm_put(ice, idx, nvol | (ovol & ~0x1f)); @@ -1143,10 +1178,11 @@ static int aureon_cs8415_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_ static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; //snd_ice1712_save_gpio_status(ice); //val = aureon_cs8415_get(ice, CS8415_CTRL2); - ucontrol->value.enumerated.item[0] = ice->spec.aureon.cs8415_mux; + ucontrol->value.enumerated.item[0] = spec->cs8415_mux; //snd_ice1712_restore_gpio_status(ice); return 0; } @@ -1154,6 +1190,7 @@ static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e static int aureon_cs8415_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; unsigned short oval, nval; int change; @@ -1165,7 +1202,7 @@ static int aureon_cs8415_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e if (change) aureon_cs8415_put(ice, CS8415_CTRL2, nval); snd_ice1712_restore_gpio_status(ice); - ice->spec.aureon.cs8415_mux = ucontrol->value.enumerated.item[0]; + spec->cs8415_mux = ucontrol->value.enumerated.item[0]; return change; } @@ -2001,10 +2038,16 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) 0x0605, /* slave, 24bit, MSB on second OSCLK, SDOUT for right channel when OLRCK is high */ (unsigned short)-1 }; + struct aureon_spec *spec; unsigned int tmp; const unsigned short *p; int err, i; + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) { ice->num_total_dacs = 6; ice->num_total_adcs = 2; @@ -2055,7 +2098,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) { for (p = cs_inits; *p != (unsigned short)-1; p++) aureon_spi_write(ice, AUREON_CS8415_CS, *p | 0x200000, 24); - ice->spec.aureon.cs8415_mux = 1; + spec->cs8415_mux = 1; aureon_set_headphone_amp(ice, 1); } @@ -2066,11 +2109,11 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) aureon_pca9554_write(ice, PCA9554_DIR, 0x00); aureon_pca9554_write(ice, PCA9554_OUT, 0x00); /* internal AUX */ - ice->spec.aureon.master[0] = WM_VOL_MUTE; - ice->spec.aureon.master[1] = WM_VOL_MUTE; + spec->master[0] = WM_VOL_MUTE; + spec->master[1] = WM_VOL_MUTE; for (i = 0; i < ice->num_total_dacs; i++) { - ice->spec.aureon.vol[i] = WM_VOL_MUTE; - wm_set_vol(ice, i, ice->spec.aureon.vol[i], ice->spec.aureon.master[i % 2]); + spec->vol[i] = WM_VOL_MUTE; + wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]); } return 0; diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c index 371f784..efd180b 100644 --- a/sound/pci/ice1712/delta.c +++ b/sound/pci/ice1712/delta.c @@ -22,7 +22,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -405,7 +404,7 @@ static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kco if (snd_i2c_sendbytes(ice->cs8427, ®, 1) != 1) snd_printk(KERN_ERR "unable to send register 0x%x byte to CS8427\n", reg); snd_i2c_readbytes(ice->cs8427, ®, 1); - ucontrol->value.integer.value[0] = (reg ? 1 : 0); + ucontrol->value.integer.value[0] = (reg & CS8427_UNLOCK) ? 1 : 0; return 0; } diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c index 75e4e5e..064760d 100644 --- a/sound/pci/ice1712/ews.c +++ b/sound/pci/ice1712/ews.c @@ -22,7 +22,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -45,6 +44,11 @@ enum { }; +/* additional i2c devices for EWS boards */ +struct ews_spec { + struct snd_i2c_device *i2cdevs[3]; +}; + /* * access via i2c mode (for EWX 24/96, EWS 88MT&D) */ @@ -142,15 +146,17 @@ static struct snd_i2c_bit_ops snd_ice1712_ewx_cs8427_bit_ops = { /* AK4524 chip select; address 0x48 bit 0-3 */ static int snd_ice1712_ews88mt_chip_select(struct snd_ice1712 *ice, int chip_mask) { + struct ews_spec *spec = ice->spec; unsigned char data, ndata; snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return -EINVAL); snd_i2c_lock(ice->i2c); - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) goto __error; ndata = (data & 0xf0) | chip_mask; if (ndata != data) - if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &ndata, 1) != 1) + if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF2], &ndata, 1) + != 1) goto __error; snd_i2c_unlock(ice->i2c); return 0; @@ -224,6 +230,7 @@ static void dmx6fire_ak4524_lock(struct snd_akm4xxx *ak, int chip) static void snd_ice1712_ews_cs8404_spdif_write(struct snd_ice1712 *ice, unsigned char bits) { + struct ews_spec *spec = ice->spec; unsigned char bytes[2]; snd_i2c_lock(ice->i2c); @@ -231,15 +238,18 @@ static void snd_ice1712_ews_cs8404_spdif_write(struct snd_ice1712 *ice, unsigned case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: case ICE1712_SUBDEVICE_PHASE88: - if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_CS8404], &bits, 1) != 1) + if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_CS8404], &bits, 1) + != 1) goto _error; break; case ICE1712_SUBDEVICE_EWS88D: - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], bytes, 2) != 2) + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], bytes, 2) + != 2) goto _error; if (bits != bytes[1]) { bytes[1] = bits; - if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_88D], bytes, 2) != 2) + if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_88D], + bytes, 2) != 2) goto _error; } break; @@ -412,6 +422,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) { int err; struct snd_akm4xxx *ak; + struct ews_spec *spec; /* set the analog DACs */ switch (ice->eeprom.subvendor) { @@ -436,6 +447,11 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) break; } + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + /* create i2c */ if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) { snd_printk(KERN_ERR "unable to create I2C bus\n"); @@ -447,7 +463,10 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) /* create i2c devices */ switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_DMX6FIRE: - if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", ICE1712_6FIRE_PCF9554_ADDR, &ice->spec.i2cdevs[EWS_I2C_6FIRE])) < 0) { + err = snd_i2c_device_create(ice->i2c, "PCF9554", + ICE1712_6FIRE_PCF9554_ADDR, + &spec->i2cdevs[EWS_I2C_6FIRE]); + if (err < 0) { snd_printk(KERN_ERR "PCF9554 initialization failed\n"); return err; } @@ -456,18 +475,30 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: case ICE1712_SUBDEVICE_PHASE88: - if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->spec.i2cdevs[EWS_I2C_CS8404])) < 0) + err = snd_i2c_device_create(ice->i2c, "CS8404", + ICE1712_EWS88MT_CS8404_ADDR, + &spec->i2cdevs[EWS_I2C_CS8404]); + if (err < 0) return err; - if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", ICE1712_EWS88MT_INPUT_ADDR, &ice->spec.i2cdevs[EWS_I2C_PCF1])) < 0) + err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", + ICE1712_EWS88MT_INPUT_ADDR, + &spec->i2cdevs[EWS_I2C_PCF1]); + if (err < 0) return err; - if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)", ICE1712_EWS88MT_OUTPUT_ADDR, &ice->spec.i2cdevs[EWS_I2C_PCF2])) < 0) + err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)", + ICE1712_EWS88MT_OUTPUT_ADDR, + &spec->i2cdevs[EWS_I2C_PCF2]); + if (err < 0) return err; /* Check if the front module is connected */ if ((err = snd_ice1712_ews88mt_chip_select(ice, 0x0f)) < 0) return err; break; case ICE1712_SUBDEVICE_EWS88D: - if ((err = snd_i2c_device_create(ice->i2c, "PCF8575", ICE1712_EWS88D_PCF_ADDR, &ice->spec.i2cdevs[EWS_I2C_88D])) < 0) + err = snd_i2c_device_create(ice->i2c, "PCF8575", + ICE1712_EWS88D_PCF_ADDR, + &spec->i2cdevs[EWS_I2C_88D]); + if (err < 0) return err; break; } @@ -507,7 +538,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) } /* analog section */ - ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); + ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); if (! ak) return -ENOMEM; ice->akm_codecs = 1; @@ -605,10 +636,11 @@ static struct snd_kcontrol_new snd_ice1712_ewx2496_controls[] __devinitdata = { static int snd_ice1712_ews88mt_output_sense_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct ews_spec *spec = ice->spec; unsigned char data; snd_i2c_lock(ice->i2c); - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) { + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) { snd_i2c_unlock(ice->i2c); return -EIO; } @@ -621,15 +653,17 @@ static int snd_ice1712_ews88mt_output_sense_get(struct snd_kcontrol *kcontrol, s static int snd_ice1712_ews88mt_output_sense_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct ews_spec *spec = ice->spec; unsigned char data, ndata; snd_i2c_lock(ice->i2c); - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) { + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) { snd_i2c_unlock(ice->i2c); return -EIO; } ndata = (data & ~ICE1712_EWS88MT_OUTPUT_SENSE) | (ucontrol->value.enumerated.item[0] ? ICE1712_EWS88MT_OUTPUT_SENSE : 0); - if (ndata != data && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &ndata, 1) != 1) { + if (ndata != data && snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF2], + &ndata, 1) != 1) { snd_i2c_unlock(ice->i2c); return -EIO; } @@ -641,12 +675,13 @@ static int snd_ice1712_ews88mt_output_sense_put(struct snd_kcontrol *kcontrol, s static int snd_ice1712_ews88mt_input_sense_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct ews_spec *spec = ice->spec; int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned char data; snd_assert(channel >= 0 && channel <= 7, return 0); snd_i2c_lock(ice->i2c); - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) { + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) { snd_i2c_unlock(ice->i2c); return -EIO; } @@ -660,17 +695,19 @@ static int snd_ice1712_ews88mt_input_sense_get(struct snd_kcontrol *kcontrol, st static int snd_ice1712_ews88mt_input_sense_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct ews_spec *spec = ice->spec; int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned char data, ndata; snd_assert(channel >= 0 && channel <= 7, return 0); snd_i2c_lock(ice->i2c); - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) { + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) { snd_i2c_unlock(ice->i2c); return -EIO; } ndata = (data & ~(1 << channel)) | (ucontrol->value.enumerated.item[0] ? 0 : (1 << channel)); - if (ndata != data && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &ndata, 1) != 1) { + if (ndata != data && snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF1], + &ndata, 1) != 1) { snd_i2c_unlock(ice->i2c); return -EIO; } @@ -705,12 +742,13 @@ static struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense __devinitdata = static int snd_ice1712_ews88d_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct ews_spec *spec = ice->spec; int shift = kcontrol->private_value & 0xff; int invert = (kcontrol->private_value >> 8) & 1; unsigned char data[2]; snd_i2c_lock(ice->i2c); - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) { + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) { snd_i2c_unlock(ice->i2c); return -EIO; } @@ -725,13 +763,14 @@ static int snd_ice1712_ews88d_control_get(struct snd_kcontrol *kcontrol, struct static int snd_ice1712_ews88d_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct ews_spec *spec = ice->spec; int shift = kcontrol->private_value & 0xff; int invert = (kcontrol->private_value >> 8) & 1; unsigned char data[2], ndata[2]; int change; snd_i2c_lock(ice->i2c); - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) { + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) { snd_i2c_unlock(ice->i2c); return -EIO; } @@ -744,7 +783,8 @@ static int snd_ice1712_ews88d_control_put(struct snd_kcontrol *kcontrol, struct ndata[shift >> 3] |= (1 << (shift & 7)); } change = (data[shift >> 3] != ndata[shift >> 3]); - if (change && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) { + if (change && + snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) { snd_i2c_unlock(ice->i2c); return -EIO; } @@ -778,11 +818,13 @@ static struct snd_kcontrol_new snd_ice1712_ews88d_controls[] __devinitdata = { static int snd_ice1712_6fire_read_pca(struct snd_ice1712 *ice, unsigned char reg) { unsigned char byte; + struct ews_spec *spec = ice->spec; + snd_i2c_lock(ice->i2c); byte = reg; - snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], &byte, 1); + snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1); byte = 0; - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) { + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) { snd_i2c_unlock(ice->i2c); printk(KERN_ERR "cannot read pca\n"); return -EIO; @@ -794,10 +836,12 @@ static int snd_ice1712_6fire_read_pca(struct snd_ice1712 *ice, unsigned char reg static int snd_ice1712_6fire_write_pca(struct snd_ice1712 *ice, unsigned char reg, unsigned char data) { unsigned char bytes[2]; + struct ews_spec *spec = ice->spec; + snd_i2c_lock(ice->i2c); bytes[0] = reg; bytes[1] = data; - if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], bytes, 2) != 2) { + if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], bytes, 2) != 2) { snd_i2c_unlock(ice->i2c); return -EIO; } diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c index abcfd1d..cf5c7c0 100644 --- a/sound/pci/ice1712/hoontech.c +++ b/sound/pci/ice1712/hoontech.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -34,6 +33,12 @@ #include "ice1712.h" #include "hoontech.h" +/* Hoontech-specific setting */ +struct hoontech_spec { + unsigned char boxbits[4]; + unsigned int config; + unsigned short boxconfig[4]; +}; static void __devinit snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, unsigned char byte) { @@ -50,169 +55,182 @@ static void __devinit snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, un static void __devinit snd_ice1712_stdsp24_darear(struct snd_ice1712 *ice, int activate) { + struct hoontech_spec *spec = ice->spec; mutex_lock(&ice->gpio_mutex); - ICE1712_STDSP24_0_DAREAR(ice->spec.hoontech.boxbits, activate); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]); + ICE1712_STDSP24_0_DAREAR(spec->boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]); mutex_unlock(&ice->gpio_mutex); } static void __devinit snd_ice1712_stdsp24_mute(struct snd_ice1712 *ice, int activate) { + struct hoontech_spec *spec = ice->spec; mutex_lock(&ice->gpio_mutex); - ICE1712_STDSP24_3_MUTE(ice->spec.hoontech.boxbits, activate); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]); + ICE1712_STDSP24_3_MUTE(spec->boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]); mutex_unlock(&ice->gpio_mutex); } static void __devinit snd_ice1712_stdsp24_insel(struct snd_ice1712 *ice, int activate) { + struct hoontech_spec *spec = ice->spec; mutex_lock(&ice->gpio_mutex); - ICE1712_STDSP24_3_INSEL(ice->spec.hoontech.boxbits, activate); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]); + ICE1712_STDSP24_3_INSEL(spec->boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]); mutex_unlock(&ice->gpio_mutex); } static void __devinit snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, int box, int chn, int activate) { + struct hoontech_spec *spec = ice->spec; + mutex_lock(&ice->gpio_mutex); /* select box */ - ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, box); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]); + ICE1712_STDSP24_0_BOX(spec->boxbits, box); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]); /* prepare for write */ if (chn == 3) - ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 0); - ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, activate); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]); - - ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); + ICE1712_STDSP24_2_CHN4(spec->boxbits, 0); + ICE1712_STDSP24_2_MIDI1(spec->boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]); + + ICE1712_STDSP24_1_CHN1(spec->boxbits, 1); + ICE1712_STDSP24_1_CHN2(spec->boxbits, 1); + ICE1712_STDSP24_1_CHN3(spec->boxbits, 1); + ICE1712_STDSP24_2_CHN4(spec->boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); udelay(100); if (chn == 3) { - ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 0); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); + ICE1712_STDSP24_2_CHN4(spec->boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); } else { switch (chn) { - case 0: ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 0); break; - case 1: ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 0); break; - case 2: ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 0); break; + case 0: ICE1712_STDSP24_1_CHN1(spec->boxbits, 0); break; + case 1: ICE1712_STDSP24_1_CHN2(spec->boxbits, 0); break; + case 2: ICE1712_STDSP24_1_CHN3(spec->boxbits, 0); break; } - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]); } udelay(100); - ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); + ICE1712_STDSP24_1_CHN1(spec->boxbits, 1); + ICE1712_STDSP24_1_CHN2(spec->boxbits, 1); + ICE1712_STDSP24_1_CHN3(spec->boxbits, 1); + ICE1712_STDSP24_2_CHN4(spec->boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); udelay(100); - ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, 0); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); + ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); mutex_unlock(&ice->gpio_mutex); } static void __devinit snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int box, int master) { + struct hoontech_spec *spec = ice->spec; + mutex_lock(&ice->gpio_mutex); /* select box */ - ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, box); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]); + ICE1712_STDSP24_0_BOX(spec->boxbits, box); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]); - ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, master); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]); + ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1); + ICE1712_STDSP24_2_MIDI1(spec->boxbits, master); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]); udelay(100); - ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 0); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); + ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); mdelay(10); - ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); + ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); mutex_unlock(&ice->gpio_mutex); } static void __devinit snd_ice1712_stdsp24_midi2(struct snd_ice1712 *ice, int activate) { + struct hoontech_spec *spec = ice->spec; mutex_lock(&ice->gpio_mutex); - ICE1712_STDSP24_3_MIDI2(ice->spec.hoontech.boxbits, activate); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]); + ICE1712_STDSP24_3_MIDI2(spec->boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]); mutex_unlock(&ice->gpio_mutex); } static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice) { + struct hoontech_spec *spec; int box, chn; ice->num_total_dacs = 8; ice->num_total_adcs = 8; - ice->spec.hoontech.boxbits[0] = - ice->spec.hoontech.boxbits[1] = - ice->spec.hoontech.boxbits[2] = - ice->spec.hoontech.boxbits[3] = 0; /* should be already */ - - ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 0); - ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 0, 1); - ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, 0); - ICE1712_STDSP24_0_DAREAR(ice->spec.hoontech.boxbits, 0); - - ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 1, 1); - ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1); + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + + ICE1712_STDSP24_SET_ADDR(spec->boxbits, 0); + ICE1712_STDSP24_CLOCK(spec->boxbits, 0, 1); + ICE1712_STDSP24_0_BOX(spec->boxbits, 0); + ICE1712_STDSP24_0_DAREAR(spec->boxbits, 0); + + ICE1712_STDSP24_SET_ADDR(spec->boxbits, 1); + ICE1712_STDSP24_CLOCK(spec->boxbits, 1, 1); + ICE1712_STDSP24_1_CHN1(spec->boxbits, 1); + ICE1712_STDSP24_1_CHN2(spec->boxbits, 1); + ICE1712_STDSP24_1_CHN3(spec->boxbits, 1); - ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 2); - ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 2, 1); - ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, 0); - - ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 3); - ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 3, 1); - ICE1712_STDSP24_3_MIDI2(ice->spec.hoontech.boxbits, 0); - ICE1712_STDSP24_3_MUTE(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_3_INSEL(ice->spec.hoontech.boxbits, 0); + ICE1712_STDSP24_SET_ADDR(spec->boxbits, 2); + ICE1712_STDSP24_CLOCK(spec->boxbits, 2, 1); + ICE1712_STDSP24_2_CHN4(spec->boxbits, 1); + ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1); + ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0); + + ICE1712_STDSP24_SET_ADDR(spec->boxbits, 3); + ICE1712_STDSP24_CLOCK(spec->boxbits, 3, 1); + ICE1712_STDSP24_3_MIDI2(spec->boxbits, 0); + ICE1712_STDSP24_3_MUTE(spec->boxbits, 1); + ICE1712_STDSP24_3_INSEL(spec->boxbits, 0); /* let's go - activate only functions in first box */ - ice->spec.hoontech.config = 0; + spec->config = 0; /* ICE1712_STDSP24_MUTE | ICE1712_STDSP24_INSEL | ICE1712_STDSP24_DAREAR; */ - ice->spec.hoontech.boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 | + spec->boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 | ICE1712_STDSP24_BOX_CHN2 | ICE1712_STDSP24_BOX_CHN3 | ICE1712_STDSP24_BOX_CHN4 | ICE1712_STDSP24_BOX_MIDI1 | ICE1712_STDSP24_BOX_MIDI2; - ice->spec.hoontech.boxconfig[1] = - ice->spec.hoontech.boxconfig[2] = - ice->spec.hoontech.boxconfig[3] = 0; - snd_ice1712_stdsp24_darear(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_DAREAR) ? 1 : 0); - snd_ice1712_stdsp24_mute(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_MUTE) ? 1 : 0); - snd_ice1712_stdsp24_insel(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_INSEL) ? 1 : 0); - for (box = 0; box < 4; box++) { + spec->boxconfig[1] = + spec->boxconfig[2] = + spec->boxconfig[3] = 0; + snd_ice1712_stdsp24_darear(ice, + (spec->config & ICE1712_STDSP24_DAREAR) ? 1 : 0); + snd_ice1712_stdsp24_mute(ice, + (spec->config & ICE1712_STDSP24_MUTE) ? 1 : 0); + snd_ice1712_stdsp24_insel(ice, + (spec->config & ICE1712_STDSP24_INSEL) ? 1 : 0); + for (box = 0; box < 1; box++) { + if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2) + snd_ice1712_stdsp24_midi2(ice, 1); for (chn = 0; chn < 4; chn++) - snd_ice1712_stdsp24_box_channel(ice, box, chn, (ice->spec.hoontech.boxconfig[box] & (1 << chn)) ? 1 : 0); + snd_ice1712_stdsp24_box_channel(ice, box, chn, + (spec->boxconfig[box] & (1 << chn)) ? 1 : 0); snd_ice1712_stdsp24_box_midi(ice, box, - (ice->spec.hoontech.boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0); - if (ice->spec.hoontech.boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2) - snd_ice1712_stdsp24_midi2(ice, 1); + (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0); } return 0; diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 052fc3c..df292af 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -47,7 +47,6 @@ */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -2491,6 +2490,7 @@ static int snd_ice1712_free(struct snd_ice1712 *ice) pci_release_regions(ice->pci); snd_ice1712_akm4xxx_free(ice); pci_disable_device(ice->pci); + kfree(ice->spec); kfree(ice); return 0; } diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index 58640af..303cffe 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -366,42 +366,7 @@ struct snd_ice1712 { struct mutex gpio_mutex; /* other board-specific data */ - union { - /* additional i2c devices for EWS boards */ - struct snd_i2c_device *i2cdevs[3]; - /* AC97 register cache for Aureon */ - struct aureon_spec { - unsigned short stac9744[64]; - unsigned int cs8415_mux; - unsigned short master[2]; - unsigned short vol[8]; - unsigned char pca9554_out; - } aureon; - /* AC97 register cache for Phase28 */ - struct phase28_spec { - unsigned short master[2]; - unsigned short vol[8]; - } phase28; - /* a non-standard I2C device for revo51 */ - struct revo51_spec { - struct snd_i2c_device *dev; - struct snd_pt2258 *pt2258; - } revo51; - /* Hoontech-specific setting */ - struct hoontech_spec { - unsigned char boxbits[4]; - unsigned int config; - unsigned short boxconfig[4]; - } hoontech; - struct { - struct ak4114 *ak4114; - unsigned int analog: 1; - } juli; - struct { - struct ak4114 *ak4114; - } prodigy192; - } spec; - + void *spec; }; diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 0b0bbb0..f533850 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -22,7 +22,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -48,9 +47,11 @@ #include "vt1720_mobo.h" #include "pontis.h" #include "prodigy192.h" +#include "prodigy_hifi.h" #include "juli.h" #include "phase.h" #include "wtm.h" +#include "se.h" MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)"); @@ -62,9 +63,11 @@ MODULE_SUPPORTED_DEVICE("{" VT1720_MOBO_DEVICE_DESC PONTIS_DEVICE_DESC PRODIGY192_DEVICE_DESC + PRODIGY_HIFI_DEVICE_DESC JULI_DEVICE_DESC PHASE_DEVICE_DESC WTM_DEVICE_DESC + SE_DEVICE_DESC "{VIA,VT1720}," "{VIA,VT1724}," "{ICEnsemble,Generic ICE1724}," @@ -1929,10 +1932,12 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = { snd_vt1724_aureon_cards, snd_vt1720_mobo_cards, snd_vt1720_pontis_cards, + snd_vt1724_prodigy_hifi_cards, snd_vt1724_prodigy192_cards, snd_vt1724_juli_cards, snd_vt1724_phase_cards, snd_vt1724_wtm_cards, + snd_vt1724_se_cards, NULL, }; @@ -1955,6 +1960,7 @@ unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice, unsigned char val; mutex_lock(&ice->i2c_mutex); + wait_i2c_busy(ice); outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR)); outb(dev & ~VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR)); wait_i2c_busy(ice); @@ -2170,6 +2176,7 @@ static int snd_vt1724_free(struct snd_ice1712 *ice) pci_release_regions(ice->pci); snd_ice1712_akm4xxx_free(ice); pci_disable_device(ice->pci); + kfree(ice->spec); kfree(ice); return 0; } diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index 1fbe3ef..e8038c0 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -33,6 +32,11 @@ #include "envy24ht.h" #include "juli.h" +struct juli_spec { + struct ak4114 *ak4114; + unsigned int analog: 1; +}; + /* * chip addresses on I2C bus */ @@ -138,12 +142,13 @@ static struct snd_akm4xxx akm_juli_dac __devinitdata = { static int __devinit juli_add_controls(struct snd_ice1712 *ice) { + struct juli_spec *spec = ice->spec; int err; err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) return err; /* only capture SPDIF over AK4114 */ - err = snd_ak4114_build(ice->spec.juli.ak4114, NULL, + err = snd_ak4114_build(spec->ak4114, NULL, ice->pcm_pro->streams[SNDRV_PCM_STREAM_CAPTURE].substream); if (err < 0) return err; @@ -167,13 +172,19 @@ static int __devinit juli_init(struct snd_ice1712 *ice) 0x41, 0x02, 0x2c, 0x00, 0x00 }; int err; + struct juli_spec *spec; struct snd_akm4xxx *ak; + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + err = snd_ak4114_create(ice->card, juli_ak4114_read, juli_ak4114_write, ak4114_init_vals, ak4114_init_txcsb, - ice, &ice->spec.juli.ak4114); + ice, &spec->ak4114); if (err < 0) return err; @@ -181,12 +192,12 @@ static int __devinit juli_init(struct snd_ice1712 *ice) /* it seems that the analog doughter board detection does not work reliably, so force the analog flag; it should be very rare to use Juli@ without the analog doughter board */ - ice->spec.juli.analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1; + spec->analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1; #else - ice->spec.juli.analog = 1; + spec->analog = 1; #endif - if (ice->spec.juli.analog) { + if (spec->analog) { printk(KERN_INFO "juli@: analog I/O detected\n"); ice->num_total_dacs = 2; ice->num_total_adcs = 2; diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c index 3ac2505..9ab4a9f 100644 --- a/sound/pci/ice1712/phase.c +++ b/sound/pci/ice1712/phase.c @@ -33,7 +33,6 @@ * CDTI may be completely blocked by 74HCT125's gate #1 controlled by GPIO 3 */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -48,6 +47,12 @@ #include "phase.h" #include <sound/tlv.h> +/* AC97 register cache for Phase28 */ +struct phase28_spec { + unsigned short master[2]; + unsigned short vol[8]; +} phase28; + /* WM8770 registers */ #define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */ #define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */ @@ -313,27 +318,32 @@ static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; int i; for (i=0; i<2; i++) - ucontrol->value.integer.value[i] = ice->spec.phase28.master[i] & ~WM_VOL_MUTE; + ucontrol->value.integer.value[i] = spec->master[i] & ~WM_VOL_MUTE; return 0; } static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; int ch, change = 0; snd_ice1712_save_gpio_status(ice); for (ch = 0; ch < 2; ch++) { - if (ucontrol->value.integer.value[ch] != ice->spec.phase28.master[ch]) { + unsigned int vol = ucontrol->value.integer.value[ch]; + if (vol > WM_VOL_MAX) + continue; + vol |= spec->master[ch] & WM_VOL_MUTE; + if (vol != spec->master[ch]) { int dac; - ice->spec.phase28.master[ch] &= WM_VOL_MUTE; - ice->spec.phase28.master[ch] |= ucontrol->value.integer.value[ch]; + spec->master[ch] = vol; for (dac = 0; dac < ice->num_total_dacs; dac += 2) wm_set_vol(ice, WM_DAC_ATTEN + dac + ch, - ice->spec.phase28.vol[dac + ch], - ice->spec.phase28.master[ch]); + spec->vol[dac + ch], + spec->master[ch]); change = 1; } } @@ -382,12 +392,18 @@ static int __devinit phase28_init(struct snd_ice1712 *ice) unsigned int tmp; struct snd_akm4xxx *ak; + struct phase28_spec *spec; const unsigned short *p; int i; ice->num_total_dacs = 8; ice->num_total_adcs = 2; + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + // Initialize analog chips ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); if (!ak) @@ -417,11 +433,11 @@ static int __devinit phase28_init(struct snd_ice1712 *ice) snd_ice1712_restore_gpio_status(ice); - ice->spec.phase28.master[0] = WM_VOL_MUTE; - ice->spec.phase28.master[1] = WM_VOL_MUTE; + spec->master[0] = WM_VOL_MUTE; + spec->master[1] = WM_VOL_MUTE; for (i = 0; i < ice->num_total_dacs; i++) { - ice->spec.phase28.vol[i] = WM_VOL_MUTE; - wm_set_vol(ice, i, ice->spec.phase28.vol[i], ice->spec.phase28.master[i % 2]); + spec->vol[i] = WM_VOL_MUTE; + wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]); } return 0; @@ -443,18 +459,21 @@ static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info * static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; int i, ofs, voices; voices = kcontrol->private_value >> 8; ofs = kcontrol->private_value & 0xff; for (i = 0; i < voices; i++) - ucontrol->value.integer.value[i] = ice->spec.phase28.vol[ofs+i] & ~WM_VOL_MUTE; + ucontrol->value.integer.value[i] = + spec->vol[ofs+i] & ~WM_VOL_MUTE; return 0; } static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; int i, idx, ofs, voices; int change = 0; @@ -462,12 +481,16 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value * ofs = kcontrol->private_value & 0xff; snd_ice1712_save_gpio_status(ice); for (i = 0; i < voices; i++) { - idx = WM_DAC_ATTEN + ofs + i; - if (ucontrol->value.integer.value[i] != ice->spec.phase28.vol[ofs+i]) { - ice->spec.phase28.vol[ofs+i] &= WM_VOL_MUTE; - ice->spec.phase28.vol[ofs+i] |= ucontrol->value.integer.value[i]; - wm_set_vol(ice, idx, ice->spec.phase28.vol[ofs+i], - ice->spec.phase28.master[i]); + unsigned int vol; + vol = ucontrol->value.integer.value[i]; + if (vol > 0x7f) + continue; + vol |= spec->vol[ofs+i] & WM_VOL_MUTE; + if (vol != spec->vol[ofs+i]) { + spec->vol[ofs+i] = vol; + idx = WM_DAC_ATTEN + ofs + i; + wm_set_vol(ice, idx, spec->vol[ofs+i], + spec->master[i]); change = 1; } } @@ -489,19 +512,22 @@ static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; int voices, ofs, i; voices = kcontrol->private_value >> 8; ofs = kcontrol->private_value & 0xFF; for (i = 0; i < voices; i++) - ucontrol->value.integer.value[i] = (ice->spec.phase28.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1; + ucontrol->value.integer.value[i] = + (spec->vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1; return 0; } static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; int change = 0, voices, ofs, i; voices = kcontrol->private_value >> 8; @@ -509,13 +535,13 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value snd_ice1712_save_gpio_status(ice); for (i = 0; i < voices; i++) { - int val = (ice->spec.phase28.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1; + int val = (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1; if (ucontrol->value.integer.value[i] != val) { - ice->spec.phase28.vol[ofs + i] &= ~WM_VOL_MUTE; - ice->spec.phase28.vol[ofs + i] |= + spec->vol[ofs + i] &= ~WM_VOL_MUTE; + spec->vol[ofs + i] |= ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE; - wm_set_vol(ice, ofs + i, ice->spec.phase28.vol[ofs + i], - ice->spec.phase28.master[i]); + wm_set_vol(ice, ofs + i, spec->vol[ofs + i], + spec->master[i]); change = 1; } } @@ -532,29 +558,33 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; - ucontrol->value.integer.value[0] = (ice->spec.phase28.master[0] & WM_VOL_MUTE) ? 0 : 1; - ucontrol->value.integer.value[1] = (ice->spec.phase28.master[1] & WM_VOL_MUTE) ? 0 : 1; + ucontrol->value.integer.value[0] = + (spec->master[0] & WM_VOL_MUTE) ? 0 : 1; + ucontrol->value.integer.value[1] = + (spec->master[1] & WM_VOL_MUTE) ? 0 : 1; return 0; } static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; int change = 0, i; snd_ice1712_save_gpio_status(ice); for (i = 0; i < 2; i++) { - int val = (ice->spec.phase28.master[i] & WM_VOL_MUTE) ? 0 : 1; + int val = (spec->master[i] & WM_VOL_MUTE) ? 0 : 1; if (ucontrol->value.integer.value[i] != val) { int dac; - ice->spec.phase28.master[i] &= ~WM_VOL_MUTE; - ice->spec.phase28.master[i] |= + spec->master[i] &= ~WM_VOL_MUTE; + spec->master[i] |= ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE; for (dac = 0; dac < ice->num_total_dacs; dac += 2) wm_set_vol(ice, WM_DAC_ATTEN + dac + i, - ice->spec.phase28.vol[dac + i], - ice->spec.phase28.master[i]); + spec->vol[dac + i], + spec->master[i]); change = 1; } } @@ -595,8 +625,10 @@ static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val unsigned short ovol, nvol; int change = 0; - snd_ice1712_save_gpio_status(ice); nvol = ucontrol->value.integer.value[0]; + if (nvol > PCM_RES) + return -EINVAL; + snd_ice1712_save_gpio_status(ice); nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff; ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff; if (ovol != nvol) { diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index faefd52..4945c81 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index 4180f97..48cf40a 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -54,7 +54,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -68,6 +67,12 @@ #include "stac946x.h" #include <sound/tlv.h> +struct prodigy192_spec { + struct ak4114 *ak4114; + /* rate change needs atomic mute/unmute of all dacs*/ + struct mutex mute_mutex; +}; + static inline void stac9460_put(struct snd_ice1712 *ice, int reg, unsigned char val) { snd_vt1724_write_i2c(ice, PRODIGY192_STAC9460_ADDR, reg, val); @@ -81,6 +86,24 @@ static inline unsigned char stac9460_get(struct snd_ice1712 *ice, int reg) /* * DAC mute control */ + +/* + * idx = STAC9460 volume register number, mute: 0 = mute, 1 = unmute + */ +static int stac9460_dac_mute(struct snd_ice1712 *ice, int idx, + unsigned char mute) +{ + unsigned char new, old; + int change; + old = stac9460_get(ice, idx); + new = (~mute << 7 & 0x80) | (old & ~0x80); + change = (new != old); + if (change) + /*printk ("Volume register 0x%02x: 0x%02x\n", idx, new);*/ + stac9460_put(ice, idx, new); + return change; +} + #define stac9460_dac_mute_info snd_ctl_boolean_mono_info static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -101,20 +124,19 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - unsigned char new, old; - int idx; - int change; + struct prodigy192_spec *spec = ice->spec; + int idx, change; if (kcontrol->private_value) idx = STAC946X_MASTER_VOLUME; else idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME; - old = stac9460_get(ice, idx); - new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | (old & ~0x80); - change = (new != old); - if (change) - stac9460_put(ice, idx, new); - + /* due to possible conflicts with stac9460_set_rate_val, mutexing */ + mutex_lock(&spec->mute_mutex); + /*printk("Mute put: reg 0x%02x, ctrl value: 0x%02x\n", idx, + ucontrol->value.integer.value[0]);*/ + change = stac9460_dac_mute(ice, idx, ucontrol->value.integer.value[0]); + mutex_unlock(&spec->mute_mutex); return change; } @@ -162,6 +184,8 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el ovol = 0x7f - (tmp & 0x7f); change = (ovol != nvol); if (change) { + ovol = (0x7f - nvol) | (tmp & 0x80); + /*printk("DAC Volume: reg 0x%02x: 0x%02x\n", idx, ovol);*/ stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80)); } return change; @@ -241,7 +265,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el for (i = 0; i < 2; ++i) { reg = STAC946X_MIC_L_VOLUME + i; - nvol = ucontrol->value.integer.value[i]; + nvol = ucontrol->value.integer.value[i] & 0x0f; ovol = 0x0f - stac9460_get(ice, reg); change = ((ovol & 0x0f) != nvol); if (change) @@ -251,121 +275,6 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el return change; } -#if 0 -/* - * Headphone Amplifier - */ -static int aureon_set_headphone_amp(struct snd_ice1712 *ice, int enable) -{ - unsigned int tmp, tmp2; - - tmp2 = tmp = snd_ice1712_gpio_read(ice); - if (enable) - tmp |= AUREON_HP_SEL; - else - tmp &= ~ AUREON_HP_SEL; - if (tmp != tmp2) { - snd_ice1712_gpio_write(ice, tmp); - return 1; - } - return 0; -} - -static int aureon_get_headphone_amp(struct snd_ice1712 *ice) -{ - unsigned int tmp = snd_ice1712_gpio_read(ice); - - return ( tmp & AUREON_HP_SEL )!= 0; -} - -#define aureon_bool_info snd_ctl_boolean_mono_info - -static int aureon_hpamp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = aureon_get_headphone_amp(ice); - return 0; -} - - -static int aureon_hpamp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - - return aureon_set_headphone_amp(ice,ucontrol->value.integer.value[0]); -} - -/* - * Deemphasis - */ -static int aureon_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf; - return 0; -} - -static int aureon_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - int temp, temp2; - temp2 = temp = wm_get(ice, WM_DAC_CTRL2); - if (ucontrol->value.integer.value[0]) - temp |= 0xf; - else - temp &= ~0xf; - if (temp != temp2) { - wm_put(ice, WM_DAC_CTRL2, temp); - return 1; - } - return 0; -} - -/* - * ADC Oversampling - */ -static int aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo) -{ - static char *texts[2] = { "128x", "64x" }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - - return 0; -} - -static int aureon_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8; - return 0; -} - -static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - int temp, temp2; - struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - - temp2 = temp = wm_get(ice, WM_MASTER); - - if (ucontrol->value.enumerated.item[0]) - temp |= 0x8; - else - temp &= ~0x8; - - if (temp != temp2) { - wm_put(ice, WM_MASTER, temp); - return 1; - } - return 0; -} -#endif static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -407,6 +316,57 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol, stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new); return change; } +/* + * Handler for setting correct codec rate - called when rate change is detected + */ +static void stac9460_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) +{ + unsigned char old, new; + int idx; + unsigned char changed[7]; + struct snd_ice1712 *ice = ak->private_data[0]; + struct prodigy192_spec *spec = ice->spec; + + if (rate == 0) /* no hint - S/PDIF input is master, simply return */ + return; + else if (rate <= 48000) + new = 0x08; /* 256x, base rate mode */ + else if (rate <= 96000) + new = 0x11; /* 256x, mid rate mode */ + else + new = 0x12; /* 128x, high rate mode */ + old = stac9460_get(ice, STAC946X_MASTER_CLOCKING); + if (old == new) + return; + /* change detected, setting master clock, muting first */ + /* due to possible conflicts with mute controls - mutexing */ + mutex_lock(&spec->mute_mutex); + /* we have to remember current mute status for each DAC */ + for (idx = 0; idx < 7 ; ++idx) + changed[idx] = stac9460_dac_mute(ice, + STAC946X_MASTER_VOLUME + idx, 0); + /*printk("Rate change: %d, new MC: 0x%02x\n", rate, new);*/ + stac9460_put(ice, STAC946X_MASTER_CLOCKING, new); + udelay(10); + /* unmuting - only originally unmuted dacs - + * i.e. those changed when muting */ + for (idx = 0; idx < 7 ; ++idx) { + if (changed[idx]) + stac9460_dac_mute(ice, STAC946X_MASTER_VOLUME + idx, 1); + } + mutex_unlock(&spec->mute_mutex); +} + +/* using akm infrastructure for setting rate of the codec */ +static struct snd_akm4xxx akmlike_stac9460 __devinitdata = { + .type = NON_AKM, /* special value */ + .num_adcs = 6, /* not used in any way, just for completeness */ + .num_dacs = 2, + .ops = { + .set_rate_val = stac9460_set_rate_val + } +}; + static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0); static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0); @@ -483,39 +443,8 @@ static struct snd_kcontrol_new stac_controls[] __devinitdata = { .put = stac9460_mic_sw_put, }, -#if 0 - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Route", - .info = wm_adc_mux_info, - .get = wm_adc_mux_get, - .put = wm_adc_mux_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Headphone Amplifier Switch", - .info = aureon_bool_info, - .get = aureon_hpamp_get, - .put = aureon_hpamp_put - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "DAC Deemphasis Switch", - .info = aureon_bool_info, - .get = aureon_deemp_get, - .put = aureon_deemp_put - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "ADC Oversampling", - .info = aureon_oversampling_info, - .get = aureon_oversampling_get, - .put = aureon_oversampling_put - }, -#endif }; - /* AK4114 - ICE1724 connections on Prodigy192 + MI/ODI/O */ /* CDTO (pin 32) -- GPIO11 pin 86 * CDTI (pin 33) -- GPIO10 pin 77 @@ -712,16 +641,39 @@ static int prodigy192_ak4114_init(struct snd_ice1712 *ice) static const unsigned char ak4114_init_txcsb[] = { 0x41, 0x02, 0x2c, 0x00, 0x00 }; + struct prodigy192_spec *spec = ice->spec; return snd_ak4114_create(ice->card, prodigy192_ak4114_read, prodigy192_ak4114_write, ak4114_init_vals, ak4114_init_txcsb, - ice, &ice->spec.prodigy192.ak4114); + ice, &spec->ak4114); +} + +static void stac9460_proc_regs_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data; + int reg, val; + /* registers 0x0 - 0x14 */ + for (reg = 0; reg <= 0x15; reg++) { + val = stac9460_get(ice, reg); + snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val); + } +} + + +static void stac9460_proc_init(struct snd_ice1712 *ice) +{ + struct snd_info_entry *entry; + if (!snd_card_proc_new(ice->card, "stac9460_codec", &entry)) + snd_info_set_text_ops(entry, ice, stac9460_proc_regs_read); } + static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice) { + struct prodigy192_spec *spec = ice->spec; unsigned int i; int err; @@ -731,7 +683,7 @@ static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice) if (err < 0) return err; } - if (ice->spec.prodigy192.ak4114) { + if (spec->ak4114) { /* ak4114 is connected */ for (i = 0; i < ARRAY_SIZE(ak4114_controls); i++) { err = snd_ctl_add(ice->card, @@ -740,12 +692,13 @@ static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice) if (err < 0) return err; } - err = snd_ak4114_build(ice->spec.prodigy192.ak4114, + err = snd_ak4114_build(spec->ak4114, NULL, /* ak4114 in MIO/DI/O handles no IEC958 output */ ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); if (err < 0) return err; } + stac9460_proc_init(ice); return 0; } @@ -778,6 +731,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice) { static const unsigned short stac_inits_prodigy[] = { STAC946X_RESET, 0, + STAC946X_MASTER_CLOCKING, 0x11, /* STAC946X_MASTER_VOLUME, 0, STAC946X_LF_VOLUME, 0, STAC946X_RF_VOLUME, 0, @@ -789,22 +743,39 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice) }; const unsigned short *p; int err = 0; + struct snd_akm4xxx *ak; + struct prodigy192_spec *spec; /* prodigy 192 */ ice->num_total_dacs = 6; ice->num_total_adcs = 2; ice->vt1720 = 0; /* ice1724, e.g. 23 GPIOs */ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + mutex_init(&spec->mute_mutex); + /* initialize codec */ p = stac_inits_prodigy; for (; *p != (unsigned short)-1; p += 2) stac9460_put(ice, p[0], p[1]); + /* reusing the akm codecs infrastructure, + * for setting rate on stac9460 */ + ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); + if (!ak) + return -ENOMEM; + ice->akm_codecs = 1; + err = snd_ice1712_akm4xxx_init(ak, &akmlike_stac9460, NULL, ice); + if (err < 0) + return err; /* MI/ODI/O add on card with AK4114 */ if (prodigy192_miodio_exists(ice)) { err = prodigy192_ak4114_init(ice); /* from this moment if err = 0 then - * ice->spec.prodigy192.ak4114 should not be null + * spec->ak4114 should not be null */ snd_printdd("AK4114 initialized with status %d\n", err); } else @@ -854,6 +825,10 @@ struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = { .build_controls = prodigy192_add_controls, .eeprom_size = sizeof(prodigy71_eeprom), .eeprom_data = prodigy71_eeprom, + /* the current MPU401 code loops infinitely + * when opening midi device + */ + .no_mpu401 = 1, }, { } /* terminator */ }; diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c new file mode 100644 index 0000000..043a938 --- /dev/null +++ b/sound/pci/ice1712/prodigy_hifi.c @@ -0,0 +1,1210 @@ +/* + * ALSA driver for ICEnsemble VT1724 (Envy24HT) + * + * Lowlevel functions for Audiotrak Prodigy 7.1 Hifi + * based on pontis.c + * + * Copyright (c) 2007 Julian Scheel <julian@jusst.de> + * Copyright (c) 2007 allank + * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/mutex.h> + +#include <sound/core.h> +#include <sound/info.h> +#include <sound/tlv.h> + +#include "ice1712.h" +#include "envy24ht.h" +#include "prodigy_hifi.h" + +struct prodigy_hifi_spec { + unsigned short master[2]; + unsigned short vol[8]; +}; + +/* I2C addresses */ +#define WM_DEV 0x34 + +/* WM8776 registers */ +#define WM_HP_ATTEN_L 0x00 /* headphone left attenuation */ +#define WM_HP_ATTEN_R 0x01 /* headphone left attenuation */ +#define WM_HP_MASTER 0x02 /* headphone master (both channels), + override LLR */ +#define WM_DAC_ATTEN_L 0x03 /* digital left attenuation */ +#define WM_DAC_ATTEN_R 0x04 +#define WM_DAC_MASTER 0x05 +#define WM_PHASE_SWAP 0x06 /* DAC phase swap */ +#define WM_DAC_CTRL1 0x07 +#define WM_DAC_MUTE 0x08 +#define WM_DAC_CTRL2 0x09 +#define WM_DAC_INT 0x0a +#define WM_ADC_INT 0x0b +#define WM_MASTER_CTRL 0x0c +#define WM_POWERDOWN 0x0d +#define WM_ADC_ATTEN_L 0x0e +#define WM_ADC_ATTEN_R 0x0f +#define WM_ALC_CTRL1 0x10 +#define WM_ALC_CTRL2 0x11 +#define WM_ALC_CTRL3 0x12 +#define WM_NOISE_GATE 0x13 +#define WM_LIMITER 0x14 +#define WM_ADC_MUX 0x15 +#define WM_OUT_MUX 0x16 +#define WM_RESET 0x17 + +/* Analog Recording Source :- Mic, LineIn, CD/Video, */ + +/* implement capture source select control for WM8776 */ + +#define WM_AIN1 "AIN1" +#define WM_AIN2 "AIN2" +#define WM_AIN3 "AIN3" +#define WM_AIN4 "AIN4" +#define WM_AIN5 "AIN5" + +/* GPIO pins of envy24ht connected to wm8766 */ +#define WM8766_SPI_CLK (1<<17) /* CLK, Pin97 on ICE1724 */ +#define WM8766_SPI_MD (1<<16) /* DATA VT1724 -> WM8766, Pin96 */ +#define WM8766_SPI_ML (1<<18) /* Latch, Pin98 */ + +/* WM8766 registers */ +#define WM8766_DAC_CTRL 0x02 /* DAC Control */ +#define WM8766_INT_CTRL 0x03 /* Interface Control */ +#define WM8766_DAC_CTRL2 0x09 +#define WM8766_DAC_CTRL3 0x0a +#define WM8766_RESET 0x1f +#define WM8766_LDA1 0x00 +#define WM8766_LDA2 0x04 +#define WM8766_LDA3 0x06 +#define WM8766_RDA1 0x01 +#define WM8766_RDA2 0x05 +#define WM8766_RDA3 0x07 +#define WM8766_MUTE1 0x0C +#define WM8766_MUTE2 0x0F + + +/* + * Prodigy HD2 + */ +#define AK4396_ADDR 0x00 +#define AK4396_CSN (1 << 8) /* CSN->GPIO8, pin 75 */ +#define AK4396_CCLK (1 << 9) /* CCLK->GPIO9, pin 76 */ +#define AK4396_CDTI (1 << 10) /* CDTI->GPIO10, pin 77 */ + +/* ak4396 registers */ +#define AK4396_CTRL1 0x00 +#define AK4396_CTRL2 0x01 +#define AK4396_CTRL3 0x02 +#define AK4396_LCH_ATT 0x03 +#define AK4396_RCH_ATT 0x04 + + +/* + * get the current register value of WM codec + */ +static unsigned short wm_get(struct snd_ice1712 *ice, int reg) +{ + reg <<= 1; + return ((unsigned short)ice->akm[0].images[reg] << 8) | + ice->akm[0].images[reg + 1]; +} + +/* + * set the register value of WM codec and remember it + */ +static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val) +{ + unsigned short cval; + cval = (reg << 9) | val; + snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff); +} + +static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val) +{ + wm_put_nocache(ice, reg, val); + reg <<= 1; + ice->akm[0].images[reg] = val >> 8; + ice->akm[0].images[reg + 1] = val; +} + +/* + * write data in the SPI mode + */ + +static void set_gpio_bit(struct snd_ice1712 *ice, unsigned int bit, int val) +{ + unsigned int tmp = snd_ice1712_gpio_read(ice); + if (val) + tmp |= bit; + else + tmp &= ~bit; + snd_ice1712_gpio_write(ice, tmp); +} + +/* + * SPI implementation for WM8766 codec - only writing supported, no readback + */ + +static void wm8766_spi_send_word(struct snd_ice1712 *ice, unsigned int data) +{ + int i; + for (i = 0; i < 16; i++) { + set_gpio_bit(ice, WM8766_SPI_CLK, 0); + udelay(1); + set_gpio_bit(ice, WM8766_SPI_MD, data & 0x8000); + udelay(1); + set_gpio_bit(ice, WM8766_SPI_CLK, 1); + udelay(1); + data <<= 1; + } +} + +static void wm8766_spi_write(struct snd_ice1712 *ice, unsigned int reg, + unsigned int data) +{ + unsigned int block; + + snd_ice1712_gpio_set_dir(ice, WM8766_SPI_MD| + WM8766_SPI_CLK|WM8766_SPI_ML); + snd_ice1712_gpio_set_mask(ice, ~(WM8766_SPI_MD| + WM8766_SPI_CLK|WM8766_SPI_ML)); + /* latch must be low when writing */ + set_gpio_bit(ice, WM8766_SPI_ML, 0); + block = (reg << 9) | (data & 0x1ff); + wm8766_spi_send_word(ice, block); /* REGISTER ADDRESS */ + /* release latch */ + set_gpio_bit(ice, WM8766_SPI_ML, 1); + udelay(1); + /* restore */ + snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask); + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); +} + + +/* + * serial interface for ak4396 - only writing supported, no readback + */ + +static void ak4396_send_word(struct snd_ice1712 *ice, unsigned int data) +{ + int i; + for (i = 0; i < 16; i++) { + set_gpio_bit(ice, AK4396_CCLK, 0); + udelay(1); + set_gpio_bit(ice, AK4396_CDTI, data & 0x8000); + udelay(1); + set_gpio_bit(ice, AK4396_CCLK, 1); + udelay(1); + data <<= 1; + } +} + +static void ak4396_write(struct snd_ice1712 *ice, unsigned int reg, + unsigned int data) +{ + unsigned int block; + + snd_ice1712_gpio_set_dir(ice, AK4396_CSN|AK4396_CCLK|AK4396_CDTI); + snd_ice1712_gpio_set_mask(ice, ~(AK4396_CSN|AK4396_CCLK|AK4396_CDTI)); + /* latch must be low when writing */ + set_gpio_bit(ice, AK4396_CSN, 0); + block = ((AK4396_ADDR & 0x03) << 14) | (1 << 13) | + ((reg & 0x1f) << 8) | (data & 0xff); + ak4396_send_word(ice, block); /* REGISTER ADDRESS */ + /* release latch */ + set_gpio_bit(ice, AK4396_CSN, 1); + udelay(1); + /* restore */ + snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask); + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); +} + + +/* + * ak4396 mixers + */ + + + +/* + * DAC volume attenuation mixer control (-64dB to 0dB) + */ + +static int ak4396_dac_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; /* mute */ + uinfo->value.integer.max = 0xFF; /* linear */ + return 0; +} + +static int ak4396_dac_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int i; + + for (i = 0; i < 2; i++) + ucontrol->value.integer.value[i] = spec->vol[i]; + + return 0; +} + +static int ak4396_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int i; + int change = 0; + + mutex_lock(&ice->gpio_mutex); + for (i = 0; i < 2; i++) { + if (ucontrol->value.integer.value[i] != spec->vol[i]) { + spec->vol[i] = ucontrol->value.integer.value[i]; + ak4396_write(ice, AK4396_LCH_ATT + i, + spec->vol[i] & 0xff); + change = 1; + } + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + +static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1); + +static struct snd_kcontrol_new prodigy_hd2_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Front Playback Volume", + .info = ak4396_dac_vol_info, + .get = ak4396_dac_vol_get, + .put = ak4396_dac_vol_put, + .tlv = { .p = db_scale_wm_dac }, + }, +}; + + +/* --------------- */ + +/* + * Logarithmic volume values for WM87*6 + * Computed as 20 * Log10(255 / x) + */ +static const unsigned char wm_vol[256] = { + 127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23, + 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, + 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, + 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 +}; + +#define WM_VOL_MAX (sizeof(wm_vol) - 1) +#define WM_VOL_MUTE 0x8000 + + +#define DAC_0dB 0xff +#define DAC_RES 128 +#define DAC_MIN (DAC_0dB - DAC_RES) + + +static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, + unsigned short vol, unsigned short master) +{ + unsigned char nvol; + + if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE)) + nvol = 0; + else { + nvol = (((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 128) + & WM_VOL_MAX; + nvol = (nvol ? (nvol + DAC_MIN) : 0) & 0xff; + } + + wm_put(ice, index, nvol); + wm_put_nocache(ice, index, 0x100 | nvol); +} + +static void wm8766_set_vol(struct snd_ice1712 *ice, unsigned int index, + unsigned short vol, unsigned short master) +{ + unsigned char nvol; + + if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE)) + nvol = 0; + else { + nvol = (((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 128) + & WM_VOL_MAX; + nvol = (nvol ? (nvol + DAC_MIN) : 0) & 0xff; + } + + wm8766_spi_write(ice, index, (0x0100 | nvol)); +} + + +/* + * DAC volume attenuation mixer control (-64dB to 0dB) + */ + +static int wm_dac_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; /* mute */ + uinfo->value.integer.max = DAC_RES; /* 0dB, 0.5dB step */ + return 0; +} + +static int wm_dac_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int i; + + for (i = 0; i < 2; i++) + ucontrol->value.integer.value[i] = + spec->vol[2 + i] & ~WM_VOL_MUTE; + return 0; +} + +static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int i, idx, change = 0; + + mutex_lock(&ice->gpio_mutex); + for (i = 0; i < 2; i++) { + if (ucontrol->value.integer.value[i] != spec->vol[2 + i]) { + idx = WM_DAC_ATTEN_L + i; + spec->vol[2 + i] &= WM_VOL_MUTE; + spec->vol[2 + i] |= ucontrol->value.integer.value[i]; + wm_set_vol(ice, idx, spec->vol[2 + i], spec->master[i]); + change = 1; + } + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + + +/* + * WM8766 DAC volume attenuation mixer control + */ +static int wm8766_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int voices = kcontrol->private_value >> 8; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = voices; + uinfo->value.integer.min = 0; /* mute */ + uinfo->value.integer.max = DAC_RES; /* 0dB */ + return 0; +} + +static int wm8766_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int i, ofs, voices; + + voices = kcontrol->private_value >> 8; + ofs = kcontrol->private_value & 0xff; + for (i = 0; i < voices; i++) + ucontrol->value.integer.value[i] = spec->vol[ofs + i]; + return 0; +} + +static int wm8766_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int i, idx, ofs, voices; + int change = 0; + + voices = kcontrol->private_value >> 8; + ofs = kcontrol->private_value & 0xff; + mutex_lock(&ice->gpio_mutex); + for (i = 0; i < voices; i++) { + if (ucontrol->value.integer.value[i] != spec->vol[ofs + i]) { + idx = WM8766_LDA1 + ofs + i; + spec->vol[ofs + i] &= WM_VOL_MUTE; + spec->vol[ofs + i] |= ucontrol->value.integer.value[i]; + wm8766_set_vol(ice, idx, + spec->vol[ofs + i], spec->master[i]); + change = 1; + } + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + +/* + * Master volume attenuation mixer control / applied to WM8776+WM8766 + */ +static int wm_master_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = DAC_RES; + return 0; +} + +static int wm_master_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int i; + for (i = 0; i < 2; i++) + ucontrol->value.integer.value[i] = spec->master[i]; + return 0; +} + +static int wm_master_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int ch, change = 0; + + mutex_lock(&ice->gpio_mutex); + for (ch = 0; ch < 2; ch++) { + if (ucontrol->value.integer.value[ch] != spec->master[ch]) { + spec->master[ch] = ucontrol->value.integer.value[ch]; + + /* Apply to front DAC */ + wm_set_vol(ice, WM_DAC_ATTEN_L + ch, + spec->vol[2 + ch], spec->master[ch]); + + wm8766_set_vol(ice, WM8766_LDA1 + ch, + spec->vol[0 + ch], spec->master[ch]); + + wm8766_set_vol(ice, WM8766_LDA2 + ch, + spec->vol[4 + ch], spec->master[ch]); + + wm8766_set_vol(ice, WM8766_LDA3 + ch, + spec->vol[6 + ch], spec->master[ch]); + change = 1; + } + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + + +/* KONSTI */ + +static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char* texts[32] = { + "NULL", WM_AIN1, WM_AIN2, WM_AIN1 "+" WM_AIN2, + WM_AIN3, WM_AIN1 "+" WM_AIN3, WM_AIN2 "+" WM_AIN3, + WM_AIN1 "+" WM_AIN2 "+" WM_AIN3, + WM_AIN4, WM_AIN1 "+" WM_AIN4, WM_AIN2 "+" WM_AIN4, + WM_AIN1 "+" WM_AIN2 "+" WM_AIN4, + WM_AIN3 "+" WM_AIN4, WM_AIN1 "+" WM_AIN3 "+" WM_AIN4, + WM_AIN2 "+" WM_AIN3 "+" WM_AIN4, + WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4, + WM_AIN5, WM_AIN1 "+" WM_AIN5, WM_AIN2 "+" WM_AIN5, + WM_AIN1 "+" WM_AIN2 "+" WM_AIN5, + WM_AIN3 "+" WM_AIN5, WM_AIN1 "+" WM_AIN3 "+" WM_AIN5, + WM_AIN2 "+" WM_AIN3 "+" WM_AIN5, + WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN5, + WM_AIN4 "+" WM_AIN5, WM_AIN1 "+" WM_AIN4 "+" WM_AIN5, + WM_AIN2 "+" WM_AIN4 "+" WM_AIN5, + WM_AIN1 "+" WM_AIN2 "+" WM_AIN4 "+" WM_AIN5, + WM_AIN3 "+" WM_AIN4 "+" WM_AIN5, + WM_AIN1 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5, + WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5, + WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5 + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 32; + if (uinfo->value.enumerated.item > 31) + uinfo->value.enumerated.item = 31; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int wm_adc_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + + mutex_lock(&ice->gpio_mutex); + ucontrol->value.integer.value[0] = wm_get(ice, WM_ADC_MUX) & 0x1f; + mutex_unlock(&ice->gpio_mutex); + return 0; +} + +static int wm_adc_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned short oval, nval; + int change = 0; + + mutex_lock(&ice->gpio_mutex); + oval = wm_get(ice, WM_ADC_MUX); + nval = (oval & 0xe0) | ucontrol->value.integer.value[0]; + if (nval != oval) { + wm_put(ice, WM_ADC_MUX, nval); + change = 1; + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + +/* KONSTI */ + +/* + * ADC gain mixer control (-64dB to 0dB) + */ + +#define ADC_0dB 0xcf +#define ADC_RES 128 +#define ADC_MIN (ADC_0dB - ADC_RES) + +static int wm_adc_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; /* mute (-64dB) */ + uinfo->value.integer.max = ADC_RES; /* 0dB, 0.5dB step */ + return 0; +} + +static int wm_adc_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned short val; + int i; + + mutex_lock(&ice->gpio_mutex); + for (i = 0; i < 2; i++) { + val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff; + val = val > ADC_MIN ? (val - ADC_MIN) : 0; + ucontrol->value.integer.value[i] = val; + } + mutex_unlock(&ice->gpio_mutex); + return 0; +} + +static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned short ovol, nvol; + int i, idx, change = 0; + + mutex_lock(&ice->gpio_mutex); + for (i = 0; i < 2; i++) { + nvol = ucontrol->value.integer.value[i]; + nvol = nvol ? (nvol + ADC_MIN) : 0; + idx = WM_ADC_ATTEN_L + i; + ovol = wm_get(ice, idx) & 0xff; + if (ovol != nvol) { + wm_put(ice, idx, nvol); + change = 1; + } + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + +/* + * ADC input mux mixer control + */ +#define wm_adc_mux_info snd_ctl_boolean_mono_info + +static int wm_adc_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + int bit = kcontrol->private_value; + + mutex_lock(&ice->gpio_mutex); + ucontrol->value.integer.value[0] = + (wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0; + mutex_unlock(&ice->gpio_mutex); + return 0; +} + +static int wm_adc_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + int bit = kcontrol->private_value; + unsigned short oval, nval; + int change; + + mutex_lock(&ice->gpio_mutex); + nval = oval = wm_get(ice, WM_ADC_MUX); + if (ucontrol->value.integer.value[0]) + nval |= (1 << bit); + else + nval &= ~(1 << bit); + change = nval != oval; + if (change) { + wm_put(ice, WM_ADC_MUX, nval); + } + mutex_unlock(&ice->gpio_mutex); + return 0; +} + +/* + * Analog bypass (In -> Out) + */ +#define wm_bypass_info snd_ctl_boolean_mono_info + +static int wm_bypass_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + + mutex_lock(&ice->gpio_mutex); + ucontrol->value.integer.value[0] = + (wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0; + mutex_unlock(&ice->gpio_mutex); + return 0; +} + +static int wm_bypass_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned short val, oval; + int change = 0; + + mutex_lock(&ice->gpio_mutex); + val = oval = wm_get(ice, WM_OUT_MUX); + if (ucontrol->value.integer.value[0]) + val |= 0x04; + else + val &= ~0x04; + if (val != oval) { + wm_put(ice, WM_OUT_MUX, val); + change = 1; + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + +/* + * Left/Right swap + */ +#define wm_chswap_info snd_ctl_boolean_mono_info + +static int wm_chswap_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + + mutex_lock(&ice->gpio_mutex); + ucontrol->value.integer.value[0] = + (wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90; + mutex_unlock(&ice->gpio_mutex); + return 0; +} + +static int wm_chswap_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned short val, oval; + int change = 0; + + mutex_lock(&ice->gpio_mutex); + oval = wm_get(ice, WM_DAC_CTRL1); + val = oval & 0x0f; + if (ucontrol->value.integer.value[0]) + val |= 0x60; + else + val |= 0x90; + if (val != oval) { + wm_put(ice, WM_DAC_CTRL1, val); + wm_put_nocache(ice, WM_DAC_CTRL1, val); + change = 1; + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + + +/* + * mixers + */ + +static struct snd_kcontrol_new prodigy_hifi_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Master Playback Volume", + .info = wm_master_vol_info, + .get = wm_master_vol_get, + .put = wm_master_vol_put, + .tlv = { .p = db_scale_wm_dac } + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Front Playback Volume", + .info = wm_dac_vol_info, + .get = wm_dac_vol_get, + .put = wm_dac_vol_put, + .tlv = { .p = db_scale_wm_dac }, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Rear Playback Volume", + .info = wm8766_vol_info, + .get = wm8766_vol_get, + .put = wm8766_vol_put, + .private_value = (2 << 8) | 0, + .tlv = { .p = db_scale_wm_dac }, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Center Playback Volume", + .info = wm8766_vol_info, + .get = wm8766_vol_get, + .put = wm8766_vol_put, + .private_value = (1 << 8) | 4, + .tlv = { .p = db_scale_wm_dac } + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "LFE Playback Volume", + .info = wm8766_vol_info, + .get = wm8766_vol_get, + .put = wm8766_vol_put, + .private_value = (1 << 8) | 5, + .tlv = { .p = db_scale_wm_dac } + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Side Playback Volume", + .info = wm8766_vol_info, + .get = wm8766_vol_get, + .put = wm8766_vol_put, + .private_value = (2 << 8) | 6, + .tlv = { .p = db_scale_wm_dac }, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Capture Volume", + .info = wm_adc_vol_info, + .get = wm_adc_vol_get, + .put = wm_adc_vol_put, + .tlv = { .p = db_scale_wm_dac }, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "CD Capture Switch", + .info = wm_adc_mux_info, + .get = wm_adc_mux_get, + .put = wm_adc_mux_put, + .private_value = 0, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line Capture Switch", + .info = wm_adc_mux_info, + .get = wm_adc_mux_get, + .put = wm_adc_mux_put, + .private_value = 1, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Bypass Switch", + .info = wm_bypass_info, + .get = wm_bypass_get, + .put = wm_bypass_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Swap Output Channels", + .info = wm_chswap_info, + .get = wm_chswap_get, + .put = wm_chswap_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Capture Source", + .info = wm_adc_mux_enum_info, + .get = wm_adc_mux_enum_get, + .put = wm_adc_mux_enum_put, + }, +}; + +/* + * WM codec registers + */ +static void wm_proc_regs_write(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_ice1712 *ice = entry->private_data; + char line[64]; + unsigned int reg, val; + mutex_lock(&ice->gpio_mutex); + while (!snd_info_get_line(buffer, line, sizeof(line))) { + if (sscanf(line, "%x %x", ®, &val) != 2) + continue; + if (reg <= 0x17 && val <= 0xffff) + wm_put(ice, reg, val); + } + mutex_unlock(&ice->gpio_mutex); +} + +static void wm_proc_regs_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_ice1712 *ice = entry->private_data; + int reg, val; + + mutex_lock(&ice->gpio_mutex); + for (reg = 0; reg <= 0x17; reg++) { + val = wm_get(ice, reg); + snd_iprintf(buffer, "%02x = %04x\n", reg, val); + } + mutex_unlock(&ice->gpio_mutex); +} + +static void wm_proc_init(struct snd_ice1712 *ice) +{ + struct snd_info_entry *entry; + if (!snd_card_proc_new(ice->card, "wm_codec", &entry)) { + snd_info_set_text_ops(entry, ice, wm_proc_regs_read); + entry->mode |= S_IWUSR; + entry->c.text.write = wm_proc_regs_write; + } +} + +static int __devinit prodigy_hifi_add_controls(struct snd_ice1712 *ice) +{ + unsigned int i; + int err; + + for (i = 0; i < ARRAY_SIZE(prodigy_hifi_controls); i++) { + err = snd_ctl_add(ice->card, + snd_ctl_new1(&prodigy_hifi_controls[i], ice)); + if (err < 0) + return err; + } + + wm_proc_init(ice); + + return 0; +} + +static int __devinit prodigy_hd2_add_controls(struct snd_ice1712 *ice) +{ + unsigned int i; + int err; + + for (i = 0; i < ARRAY_SIZE(prodigy_hd2_controls); i++) { + err = snd_ctl_add(ice->card, + snd_ctl_new1(&prodigy_hd2_controls[i], ice)); + if (err < 0) + return err; + } + + wm_proc_init(ice); + + return 0; +} + + +/* + * initialize the chip + */ +static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice) +{ + static unsigned short wm_inits[] = { + /* These come first to reduce init pop noise */ + WM_ADC_MUX, 0x0003, /* ADC mute */ + /* 0x00c0 replaced by 0x0003 */ + + WM_DAC_MUTE, 0x0001, /* DAC softmute */ + WM_DAC_CTRL1, 0x0000, /* DAC mute */ + + WM_POWERDOWN, 0x0008, /* All power-up except HP */ + WM_RESET, 0x0000, /* reset */ + }; + static unsigned short wm_inits2[] = { + WM_MASTER_CTRL, 0x0022, /* 256fs, slave mode */ + WM_DAC_INT, 0x0022, /* I2S, normal polarity, 24bit */ + WM_ADC_INT, 0x0022, /* I2S, normal polarity, 24bit */ + WM_DAC_CTRL1, 0x0090, /* DAC L/R */ + WM_OUT_MUX, 0x0001, /* OUT DAC */ + WM_HP_ATTEN_L, 0x0179, /* HP 0dB */ + WM_HP_ATTEN_R, 0x0179, /* HP 0dB */ + WM_DAC_ATTEN_L, 0x0000, /* DAC 0dB */ + WM_DAC_ATTEN_L, 0x0100, /* DAC 0dB */ + WM_DAC_ATTEN_R, 0x0000, /* DAC 0dB */ + WM_DAC_ATTEN_R, 0x0100, /* DAC 0dB */ + WM_PHASE_SWAP, 0x0000, /* phase normal */ +#if 0 + WM_DAC_MASTER, 0x0100, /* DAC master muted */ +#endif + WM_DAC_CTRL2, 0x0000, /* no deemphasis, no ZFLG */ + WM_ADC_ATTEN_L, 0x0000, /* ADC muted */ + WM_ADC_ATTEN_R, 0x0000, /* ADC muted */ +#if 1 + WM_ALC_CTRL1, 0x007b, /* */ + WM_ALC_CTRL2, 0x0000, /* */ + WM_ALC_CTRL3, 0x0000, /* */ + WM_NOISE_GATE, 0x0000, /* */ +#endif + WM_DAC_MUTE, 0x0000, /* DAC unmute */ + WM_ADC_MUX, 0x0003, /* ADC unmute, both CD/Line On */ + }; + static unsigned short wm8766_inits[] = { + WM8766_RESET, 0x0000, + WM8766_DAC_CTRL, 0x0120, + WM8766_INT_CTRL, 0x0022, /* I2S Normal Mode, 24 bit */ + WM8766_DAC_CTRL2, 0x0001, + WM8766_DAC_CTRL3, 0x0080, + WM8766_LDA1, 0x0100, + WM8766_LDA2, 0x0100, + WM8766_LDA3, 0x0100, + WM8766_RDA1, 0x0100, + WM8766_RDA2, 0x0100, + WM8766_RDA3, 0x0100, + WM8766_MUTE1, 0x0000, + WM8766_MUTE2, 0x0000, + }; + + struct prodigy_hifi_spec *spec; + unsigned int i; + + ice->vt1720 = 0; + ice->vt1724 = 1; + + ice->num_total_dacs = 8; + ice->num_total_adcs = 1; + + /* HACK - use this as the SPDIF source. + * don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten + */ + ice->gpio.saved[0] = 0; + /* to remeber the register values */ + + ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); + if (! ice->akm) + return -ENOMEM; + ice->akm_codecs = 1; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + + /* initialize WM8776 codec */ + for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2) + wm_put(ice, wm_inits[i], wm_inits[i+1]); + schedule_timeout_uninterruptible(1); + for (i = 0; i < ARRAY_SIZE(wm_inits2); i += 2) + wm_put(ice, wm_inits2[i], wm_inits2[i+1]); + + /* initialize WM8766 codec */ + for (i = 0; i < ARRAY_SIZE(wm8766_inits); i += 2) + wm8766_spi_write(ice, wm8766_inits[i], wm8766_inits[i+1]); + + + return 0; +} + + +/* + * initialize the chip + */ +static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice) +{ + static unsigned short ak4396_inits[] = { + AK4396_CTRL1, 0x87, /* I2S Normal Mode, 24 bit */ + AK4396_CTRL2, 0x02, + AK4396_CTRL3, 0x00, + AK4396_LCH_ATT, 0x00, + AK4396_RCH_ATT, 0x00, + }; + + struct prodigy_hifi_spec *spec; + unsigned int i; + + ice->vt1720 = 0; + ice->vt1724 = 1; + + ice->num_total_dacs = 1; + ice->num_total_adcs = 1; + + /* HACK - use this as the SPDIF source. + * don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten + */ + ice->gpio.saved[0] = 0; + /* to remeber the register values */ + + ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); + if (! ice->akm) + return -ENOMEM; + ice->akm_codecs = 1; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + + /* initialize ak4396 codec */ + /* reset codec */ + ak4396_write(ice, AK4396_CTRL1, 0x86); + msleep(100); + ak4396_write(ice, AK4396_CTRL1, 0x87); + + for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2) + ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]); + + return 0; +} + + +static unsigned char prodigy71hifi_eeprom[] __devinitdata = { + 0x4b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */ + 0x80, /* ACLINK: I2S */ + 0xfc, /* I2S: vol, 96k, 24bit, 192k */ + 0xc3, /* SPDIF: out-en, out-int, spdif-in */ + 0xff, /* GPIO_DIR */ + 0xff, /* GPIO_DIR1 */ + 0x5f, /* GPIO_DIR2 */ + 0x00, /* GPIO_MASK */ + 0x00, /* GPIO_MASK1 */ + 0x00, /* GPIO_MASK2 */ + 0x00, /* GPIO_STATE */ + 0x00, /* GPIO_STATE1 */ + 0x00, /* GPIO_STATE2 */ +}; + +static unsigned char prodigyhd2_eeprom[] __devinitdata = { + 0x4b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */ + 0x80, /* ACLINK: I2S */ + 0xfc, /* I2S: vol, 96k, 24bit, 192k */ + 0xc3, /* SPDIF: out-en, out-int, spdif-in */ + 0xff, /* GPIO_DIR */ + 0xff, /* GPIO_DIR1 */ + 0x5f, /* GPIO_DIR2 */ + 0x00, /* GPIO_MASK */ + 0x00, /* GPIO_MASK1 */ + 0x00, /* GPIO_MASK2 */ + 0x00, /* GPIO_STATE */ + 0x00, /* GPIO_STATE1 */ + 0x00, /* GPIO_STATE2 */ +}; + +static unsigned char fortissimo4_eeprom[] __devinitdata = { + 0x43, /* SYSCONF: clock 512, ADC, 4DACs */ + 0x80, /* ACLINK: I2S */ + 0xfc, /* I2S: vol, 96k, 24bit, 192k */ + 0xc1, /* SPDIF: out-en, out-int */ + 0xff, /* GPIO_DIR */ + 0xff, /* GPIO_DIR1 */ + 0x5f, /* GPIO_DIR2 */ + 0x00, /* GPIO_MASK */ + 0x00, /* GPIO_MASK1 */ + 0x00, /* GPIO_MASK2 */ + 0x00, /* GPIO_STATE */ + 0x00, /* GPIO_STATE1 */ + 0x00, /* GPIO_STATE2 */ +}; + +/* entry point */ +struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[] __devinitdata = { + { + .subvendor = VT1724_SUBDEVICE_PRODIGY_HIFI, + .name = "Audiotrak Prodigy 7.1 HiFi", + .model = "prodigy71hifi", + .chip_init = prodigy_hifi_init, + .build_controls = prodigy_hifi_add_controls, + .eeprom_size = sizeof(prodigy71hifi_eeprom), + .eeprom_data = prodigy71hifi_eeprom, + .driver = "Prodigy71HIFI", + }, + { + .subvendor = VT1724_SUBDEVICE_PRODIGY_HD2, + .name = "Audiotrak Prodigy HD2", + .model = "prodigyhd2", + .chip_init = prodigy_hd2_init, + .build_controls = prodigy_hd2_add_controls, + .eeprom_size = sizeof(prodigyhd2_eeprom), + .eeprom_data = prodigyhd2_eeprom, + .driver = "Prodigy71HD2", + }, + { + .subvendor = VT1724_SUBDEVICE_FORTISSIMO4, + .name = "Hercules Fortissimo IV", + .model = "fortissimo4", + .chip_init = prodigy_hifi_init, + .build_controls = prodigy_hifi_add_controls, + .eeprom_size = sizeof(fortissimo4_eeprom), + .eeprom_data = fortissimo4_eeprom, + .driver = "Fortissimo4", + }, + { } /* terminator */ +}; + diff --git a/sound/pci/ice1712/prodigy_hifi.h b/sound/pci/ice1712/prodigy_hifi.h new file mode 100644 index 0000000..a4415d4 --- /dev/null +++ b/sound/pci/ice1712/prodigy_hifi.h @@ -0,0 +1,38 @@ +#ifndef __SOUND_PRODIGY_HIFI_H +#define __SOUND_PRODIGY_HIFI_H + +/* + * ALSA driver for VIA VT1724 (Envy24HT) + * + * Lowlevel functions for Audiotrak Prodigy Hifi + * + * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define PRODIGY_HIFI_DEVICE_DESC "{Audiotrak,Prodigy 7.1 HIFI},"\ + "{Audiotrak Prodigy HD2},"\ + "{Hercules Fortissimo IV}," + +#define VT1724_SUBDEVICE_PRODIGY_HIFI 0x38315441 /* PRODIGY 7.1 HIFI */ +#define VT1724_SUBDEVICE_PRODIGY_HD2 0x37315441 /* PRODIGY HD2 */ +#define VT1724_SUBDEVICE_FORTISSIMO4 0x81160100 /* Fortissimo IV */ + + +extern struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[]; + +#endif /* __SOUND_PRODIGY_HIFI_H */ diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index d18a31e..ddd5fc8 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -33,6 +32,12 @@ #include "envy24ht.h" #include "revo.h" +/* a non-standard I2C device for revo51 */ +struct revo51_spec { + struct snd_i2c_device *dev; + struct snd_pt2258 *pt2258; +} revo51; + static void revo_i2s_mclk_changed(struct snd_ice1712 *ice) { /* assert PRST# to converters; MT05 bit 7 */ @@ -153,8 +158,14 @@ static struct snd_i2c_bit_ops revo51_bit_ops = { static int revo51_i2c_init(struct snd_ice1712 *ice, struct snd_pt2258 *pt) { + struct revo51_spec *spec; int err; + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + /* create the I2C bus */ err = snd_i2c_bus_create(ice->card, "ICE1724 GPIO6", NULL, &ice->i2c); if (err < 0) @@ -164,15 +175,14 @@ static int revo51_i2c_init(struct snd_ice1712 *ice, ice->i2c->hw_ops.bit = &revo51_bit_ops; /* create the I2C device */ - err = snd_i2c_device_create(ice->i2c, "PT2258", 0x40, - &ice->spec.revo51.dev); + err = snd_i2c_device_create(ice->i2c, "PT2258", 0x40, &spec->dev); if (err < 0) return err; pt->card = ice->card; pt->i2c_bus = ice->i2c; - pt->i2c_dev = ice->spec.revo51.dev; - ice->spec.revo51.pt2258 = pt; + pt->i2c_dev = spec->dev; + spec->pt2258 = pt; snd_pt2258_reset(pt); @@ -556,6 +566,7 @@ static int __devinit revo_init(struct snd_ice1712 *ice) static int __devinit revo_add_controls(struct snd_ice1712 *ice) { + struct revo51_spec *spec; int err; switch (ice->eeprom.subvendor) { @@ -568,7 +579,8 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice) err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) return err; - err = snd_pt2258_build_controls(ice->spec.revo51.pt2258); + spec = ice->spec; + err = snd_pt2258_build_controls(spec->pt2258); if (err < 0) return err; break; diff --git a/sound/pci/ice1712/se.c b/sound/pci/ice1712/se.c new file mode 100644 index 0000000..69673b9 --- /dev/null +++ b/sound/pci/ice1712/se.c @@ -0,0 +1,774 @@ +/* + * ALSA driver for ICEnsemble VT1724 (Envy24HT) + * + * Lowlevel functions for ONKYO WAVIO SE-90PCI and SE-200PCI + * + * Copyright (c) 2007 Shin-ya Okada sh_okada(at)d4.dion.ne.jp + * (at) -> @ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/tlv.h> + +#include "ice1712.h" +#include "envy24ht.h" +#include "se.h" + +struct se_spec { + struct { + unsigned char ch1, ch2; + } vol[8]; +}; + +/****************************************************************************/ +/* ONKYO WAVIO SE-200PCI */ +/****************************************************************************/ +/* + * system configuration ICE_EEP2_SYSCONF=0x4b + * XIN1 49.152MHz + * not have UART + * one stereo ADC and a S/PDIF receiver connected + * four stereo DACs connected + * + * AC-Link configuration ICE_EEP2_ACLINK=0x80 + * use I2C, not use AC97 + * + * I2S converters feature ICE_EEP2_I2S=0x78 + * I2S codec has no volume/mute control feature + * I2S codec supports 96KHz and 192KHz + * I2S codec 24bits + * + * S/PDIF configuration ICE_EEP2_SPDIF=0xc3 + * Enable integrated S/PDIF transmitter + * internal S/PDIF out implemented + * S/PDIF is stereo + * External S/PDIF out implemented + * + * + * ** connected chips ** + * + * WM8740 + * A 2ch-DAC of main outputs. + * It setuped as I2S mode by wire, so no way to setup from software. + * The sample-rate are automatically changed. + * ML/I2S (28pin) --------+ + * MC/DM1 (27pin) -- 5V | + * MD/DM0 (26pin) -- GND | + * MUTEB (25pin) -- NC | + * MODE (24pin) -- GND | + * CSBIW (23pin) --------+ + * | + * RSTB (22pin) --R(1K)-+ + * Probably it reduce the noise from the control line. + * + * WM8766 + * A 6ch-DAC for surrounds. + * It's control wire was connected to GPIOxx (3-wire serial interface) + * ML/I2S (11pin) -- GPIO18 + * MC/IWL (12pin) -- GPIO17 + * MD/DM (13pin) -- GPIO16 + * MUTE (14pin) -- GPIO01 + * + * WM8776 + * A 2ch-ADC(with 10ch-selector) plus 2ch-DAC. + * It's control wire was connected to SDA/SCLK (2-wire serial interface) + * MODE (16pin) -- R(1K) -- GND + * CE (17pin) -- R(1K) -- GND 2-wire mode (address=0x34) + * DI (18pin) -- SDA + * CL (19pin) -- SCLK + * + * + * ** output pins and device names ** + * + * 7.1ch name -- output connector color -- device (-D option) + * + * FRONT 2ch -- green -- plughw:0,0 + * CENTER(Lch) SUBWOOFER(Rch) -- black -- plughw:0,2,0 + * SURROUND 2ch -- orange -- plughw:0,2,1 + * SURROUND BACK 2ch -- white -- plughw:0,2,2 + * + */ + + +/****************************************************************************/ +/* WM8740 interface */ +/****************************************************************************/ + +static void __devinit se200pci_WM8740_init(struct snd_ice1712 *ice) +{ + /* nothing to do */ +} + + +static void se200pci_WM8740_set_pro_rate(struct snd_ice1712 *ice, + unsigned int rate) +{ + /* nothing to do */ +} + + +/****************************************************************************/ +/* WM8766 interface */ +/****************************************************************************/ + +static void se200pci_WM8766_write(struct snd_ice1712 *ice, + unsigned int addr, unsigned int data) +{ + unsigned int st; + unsigned int bits; + int i; + const unsigned int DATA = 0x010000; + const unsigned int CLOCK = 0x020000; + const unsigned int LOAD = 0x040000; + const unsigned int ALL_MASK = (DATA | CLOCK | LOAD); + + snd_ice1712_save_gpio_status(ice); + + st = ((addr & 0x7f) << 9) | (data & 0x1ff); + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction | ALL_MASK); + snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask & ~ALL_MASK); + bits = snd_ice1712_gpio_read(ice) & ~ALL_MASK; + + snd_ice1712_gpio_write(ice, bits); + for (i = 0; i < 16; i++) { + udelay(1); + bits &= ~CLOCK; + st = (st << 1); + if (st & 0x10000) + bits |= DATA; + else + bits &= ~DATA; + + snd_ice1712_gpio_write(ice, bits); + + udelay(1); + bits |= CLOCK; + snd_ice1712_gpio_write(ice, bits); + } + + udelay(1); + bits |= LOAD; + snd_ice1712_gpio_write(ice, bits); + + udelay(1); + bits |= (DATA | CLOCK); + snd_ice1712_gpio_write(ice, bits); + + snd_ice1712_restore_gpio_status(ice); +} + +static void se200pci_WM8766_set_volume(struct snd_ice1712 *ice, int ch, + unsigned int vol1, unsigned int vol2) +{ + switch (ch) { + case 0: + se200pci_WM8766_write(ice, 0x000, vol1); + se200pci_WM8766_write(ice, 0x001, vol2 | 0x100); + break; + case 1: + se200pci_WM8766_write(ice, 0x004, vol1); + se200pci_WM8766_write(ice, 0x005, vol2 | 0x100); + break; + case 2: + se200pci_WM8766_write(ice, 0x006, vol1); + se200pci_WM8766_write(ice, 0x007, vol2 | 0x100); + break; + } +} + +static void __devinit se200pci_WM8766_init(struct snd_ice1712 *ice) +{ + se200pci_WM8766_write(ice, 0x1f, 0x000); /* RESET ALL */ + udelay(10); + + se200pci_WM8766_set_volume(ice, 0, 0, 0); /* volume L=0 R=0 */ + se200pci_WM8766_set_volume(ice, 1, 0, 0); /* volume L=0 R=0 */ + se200pci_WM8766_set_volume(ice, 2, 0, 0); /* volume L=0 R=0 */ + + se200pci_WM8766_write(ice, 0x03, 0x022); /* serial mode I2S-24bits */ + se200pci_WM8766_write(ice, 0x0a, 0x080); /* MCLK=256fs */ + se200pci_WM8766_write(ice, 0x12, 0x000); /* MDP=0 */ + se200pci_WM8766_write(ice, 0x15, 0x000); /* MDP=0 */ + se200pci_WM8766_write(ice, 0x09, 0x000); /* demp=off mute=off */ + + se200pci_WM8766_write(ice, 0x02, 0x124); /* ch-assign L=L R=R RESET */ + se200pci_WM8766_write(ice, 0x02, 0x120); /* ch-assign L=L R=R */ +} + +static void se200pci_WM8766_set_pro_rate(struct snd_ice1712 *ice, + unsigned int rate) +{ + if (rate > 96000) + se200pci_WM8766_write(ice, 0x0a, 0x000); /* MCLK=128fs */ + else + se200pci_WM8766_write(ice, 0x0a, 0x080); /* MCLK=256fs */ +} + + +/****************************************************************************/ +/* WM8776 interface */ +/****************************************************************************/ + +static void se200pci_WM8776_write(struct snd_ice1712 *ice, + unsigned int addr, unsigned int data) +{ + unsigned int val; + + val = (addr << 9) | data; + snd_vt1724_write_i2c(ice, 0x34, val >> 8, val & 0xff); +} + + +static void se200pci_WM8776_set_output_volume(struct snd_ice1712 *ice, + unsigned int vol1, unsigned int vol2) +{ + se200pci_WM8776_write(ice, 0x03, vol1); + se200pci_WM8776_write(ice, 0x04, vol2 | 0x100); +} + +static void se200pci_WM8776_set_input_volume(struct snd_ice1712 *ice, + unsigned int vol1, unsigned int vol2) +{ + se200pci_WM8776_write(ice, 0x0e, vol1); + se200pci_WM8776_write(ice, 0x0f, vol2 | 0x100); +} + +static const char *se200pci_sel[] = { + "LINE-IN", "CD-IN", "MIC-IN", "ALL-MIX", NULL +}; + +static void se200pci_WM8776_set_input_selector(struct snd_ice1712 *ice, + unsigned int sel) +{ + static unsigned char vals[] = { + /* LINE, CD, MIC, ALL, GND */ + 0x10, 0x04, 0x08, 0x1c, 0x03 + }; + if (sel > 4) + sel = 4; + se200pci_WM8776_write(ice, 0x15, vals[sel]); +} + +static void se200pci_WM8776_set_afl(struct snd_ice1712 *ice, unsigned int afl) +{ + /* AFL -- After Fader Listening */ + if (afl) + se200pci_WM8776_write(ice, 0x16, 0x005); + else + se200pci_WM8776_write(ice, 0x16, 0x001); +} + +static const char *se200pci_agc[] = { + "Off", "LimiterMode", "ALCMode", NULL +}; + +static void se200pci_WM8776_set_agc(struct snd_ice1712 *ice, unsigned int agc) +{ + /* AGC -- Auto Gain Control of the input */ + switch (agc) { + case 0: + se200pci_WM8776_write(ice, 0x11, 0x000); /* Off */ + break; + case 1: + se200pci_WM8776_write(ice, 0x10, 0x07b); + se200pci_WM8776_write(ice, 0x11, 0x100); /* LimiterMode */ + break; + case 2: + se200pci_WM8776_write(ice, 0x10, 0x1fb); + se200pci_WM8776_write(ice, 0x11, 0x100); /* ALCMode */ + break; + } +} + +static void __devinit se200pci_WM8776_init(struct snd_ice1712 *ice) +{ + int i; + static unsigned short __devinitdata default_values[] = { + 0x100, 0x100, 0x100, + 0x100, 0x100, 0x100, + 0x000, 0x090, 0x000, 0x000, + 0x022, 0x022, 0x022, + 0x008, 0x0cf, 0x0cf, 0x07b, 0x000, + 0x032, 0x000, 0x0a6, 0x001, 0x001 + }; + + se200pci_WM8776_write(ice, 0x17, 0x000); /* reset all */ + /* ADC and DAC interface is I2S 24bits mode */ + /* The sample-rate are automatically changed */ + udelay(10); + /* BUT my board can not do reset all, so I load all by manually. */ + for (i = 0; i < ARRAY_SIZE(default_values); i++) + se200pci_WM8776_write(ice, i, default_values[i]); + + se200pci_WM8776_set_input_selector(ice, 0); + se200pci_WM8776_set_afl(ice, 0); + se200pci_WM8776_set_agc(ice, 0); + se200pci_WM8776_set_input_volume(ice, 0, 0); + se200pci_WM8776_set_output_volume(ice, 0, 0); + + /* head phone mute and power down */ + se200pci_WM8776_write(ice, 0x00, 0); + se200pci_WM8776_write(ice, 0x01, 0); + se200pci_WM8776_write(ice, 0x02, 0x100); + se200pci_WM8776_write(ice, 0x0d, 0x080); +} + +static void se200pci_WM8776_set_pro_rate(struct snd_ice1712 *ice, + unsigned int rate) +{ + /* nothing to do */ +} + + +/****************************************************************************/ +/* runtime interface */ +/****************************************************************************/ + +static void se200pci_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate) +{ + se200pci_WM8740_set_pro_rate(ice, rate); + se200pci_WM8766_set_pro_rate(ice, rate); + se200pci_WM8776_set_pro_rate(ice, rate); +} + +struct se200pci_control { + char *name; + enum { + WM8766, + WM8776in, + WM8776out, + WM8776sel, + WM8776agc, + WM8776afl + } target; + enum { VOLUME1, VOLUME2, BOOLEAN, ENUM } type; + int ch; + const char **member; + const char *comment; +}; + +static const struct se200pci_control se200pci_cont[] = { + { + .name = "Front Playback Volume", + .target = WM8776out, + .type = VOLUME1, + .comment = "Front(green)" + }, + { + .name = "Side Playback Volume", + .target = WM8766, + .type = VOLUME1, + .ch = 1, + .comment = "Surround(orange)" + }, + { + .name = "Surround Playback Volume", + .target = WM8766, + .type = VOLUME1, + .ch = 2, + .comment = "SurroundBack(white)" + }, + { + .name = "CLFE Playback Volume", + .target = WM8766, + .type = VOLUME1, + .ch = 0, + .comment = "Center(Lch)&SubWoofer(Rch)(black)" + }, + { + .name = "Capture Volume", + .target = WM8776in, + .type = VOLUME2 + }, + { + .name = "Capture Select", + .target = WM8776sel, + .type = ENUM, + .member = se200pci_sel + }, + { + .name = "AGC Capture Mode", + .target = WM8776agc, + .type = ENUM, + .member = se200pci_agc + }, + { + .name = "AFL Bypass Playback Switch", + .target = WM8776afl, + .type = BOOLEAN + } +}; + +static int se200pci_get_enum_count(int n) +{ + const char **member; + int c; + + member = se200pci_cont[n].member; + if (!member) + return 0; + for (c = 0; member[c]; c++) + ; + return c; +} + +static int se200pci_cont_volume_info(struct snd_kcontrol *kc, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; /* mute */ + uinfo->value.integer.max = 0xff; /* 0dB */ + return 0; +} + +#define se200pci_cont_boolean_info snd_ctl_boolean_mono_info + +static int se200pci_cont_enum_info(struct snd_kcontrol *kc, + struct snd_ctl_elem_info *uinfo) +{ + int n, c; + + n = kc->private_value; + c = se200pci_get_enum_count(n); + if (!c) + return -EINVAL; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = c; + if (uinfo->value.enumerated.item >= c) + uinfo->value.enumerated.item = c - 1; + strcpy(uinfo->value.enumerated.name, + se200pci_cont[n].member[uinfo->value.enumerated.item]); + return 0; +} + +static int se200pci_cont_volume_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *uc) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kc); + struct se_spec *spec = ice->spec; + int n = kc->private_value; + uc->value.integer.value[0] = spec->vol[n].ch1; + uc->value.integer.value[1] = spec->vol[n].ch2; + return 0; +} + +static int se200pci_cont_boolean_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *uc) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kc); + struct se_spec *spec = ice->spec; + int n = kc->private_value; + uc->value.integer.value[0] = spec->vol[n].ch1; + return 0; +} + +static int se200pci_cont_enum_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *uc) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kc); + struct se_spec *spec = ice->spec; + int n = kc->private_value; + uc->value.enumerated.item[0] = spec->vol[n].ch1; + return 0; +} + +static void se200pci_cont_update(struct snd_ice1712 *ice, int n) +{ + struct se_spec *spec = ice->spec; + switch (se200pci_cont[n].target) { + case WM8766: + se200pci_WM8766_set_volume(ice, + se200pci_cont[n].ch, + spec->vol[n].ch1, + spec->vol[n].ch2); + break; + + case WM8776in: + se200pci_WM8776_set_input_volume(ice, + spec->vol[n].ch1, + spec->vol[n].ch2); + break; + + case WM8776out: + se200pci_WM8776_set_output_volume(ice, + spec->vol[n].ch1, + spec->vol[n].ch2); + break; + + case WM8776sel: + se200pci_WM8776_set_input_selector(ice, + spec->vol[n].ch1); + break; + + case WM8776agc: + se200pci_WM8776_set_agc(ice, spec->vol[n].ch1); + break; + + case WM8776afl: + se200pci_WM8776_set_afl(ice, spec->vol[n].ch1); + break; + + default: + break; + } +} + +static int se200pci_cont_volume_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *uc) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kc); + struct se_spec *spec = ice->spec; + int n = kc->private_value; + unsigned int vol1, vol2; + int changed; + + changed = 0; + vol1 = uc->value.integer.value[0] & 0xff; + vol2 = uc->value.integer.value[1] & 0xff; + if (spec->vol[n].ch1 != vol1) { + spec->vol[n].ch1 = vol1; + changed = 1; + } + if (spec->vol[n].ch2 != vol2) { + spec->vol[n].ch2 = vol2; + changed = 1; + } + if (changed) + se200pci_cont_update(ice, n); + + return changed; +} + +static int se200pci_cont_boolean_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *uc) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kc); + struct se_spec *spec = ice->spec; + int n = kc->private_value; + unsigned int vol1; + + vol1 = !!uc->value.integer.value[0]; + if (spec->vol[n].ch1 != vol1) { + spec->vol[n].ch1 = vol1; + se200pci_cont_update(ice, n); + return 1; + } + return 0; +} + +static int se200pci_cont_enum_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *uc) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kc); + struct se_spec *spec = ice->spec; + int n = kc->private_value; + unsigned int vol1; + + vol1 = uc->value.enumerated.item[0]; + if (vol1 >= se200pci_get_enum_count(n)) + return -EINVAL; + if (spec->vol[n].ch1 != vol1) { + spec->vol[n].ch1 = vol1; + se200pci_cont_update(ice, n); + return 1; + } + return 0; +} + +static const DECLARE_TLV_DB_SCALE(db_scale_gain1, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(db_scale_gain2, -10350, 50, 1); + +static int __devinit se200pci_add_controls(struct snd_ice1712 *ice) +{ + int i; + struct snd_kcontrol_new cont; + int err; + + memset(&cont, 0, sizeof(cont)); + cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + for (i = 0; i < ARRAY_SIZE(se200pci_cont); i++) { + cont.private_value = i; + cont.name = se200pci_cont[i].name; + cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + cont.tlv.p = NULL; + switch (se200pci_cont[i].type) { + case VOLUME1: + case VOLUME2: + cont.info = se200pci_cont_volume_info; + cont.get = se200pci_cont_volume_get; + cont.put = se200pci_cont_volume_put; + cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + if (se200pci_cont[i].type == VOLUME1) + cont.tlv.p = db_scale_gain1; + else + cont.tlv.p = db_scale_gain2; + break; + case BOOLEAN: + cont.info = se200pci_cont_boolean_info; + cont.get = se200pci_cont_boolean_get; + cont.put = se200pci_cont_boolean_put; + break; + case ENUM: + cont.info = se200pci_cont_enum_info; + cont.get = se200pci_cont_enum_get; + cont.put = se200pci_cont_enum_put; + break; + default: + snd_BUG(); + return -EINVAL; + } + err = snd_ctl_add(ice->card, snd_ctl_new1(&cont, ice)); + if (err < 0) + return err; + } + + return 0; +} + + +/****************************************************************************/ +/* ONKYO WAVIO SE-90PCI */ +/****************************************************************************/ +/* + * system configuration ICE_EEP2_SYSCONF=0x4b + * AC-Link configuration ICE_EEP2_ACLINK=0x80 + * I2S converters feature ICE_EEP2_I2S=0x78 + * S/PDIF configuration ICE_EEP2_SPDIF=0xc3 + * + * ** connected chip ** + * + * WM8716 + * A 2ch-DAC of main outputs. + * It setuped as I2S mode by wire, so no way to setup from software. + * ML/I2S (28pin) -- +5V + * MC/DM1 (27pin) -- GND + * MC/DM0 (26pin) -- GND + * MUTEB (25pin) -- open (internal pull-up) + * MODE (24pin) -- GND + * CSBIWO (23pin) -- +5V + * + */ + + /* Nothing to do for this chip. */ + + +/****************************************************************************/ +/* probe/initialize/setup */ +/****************************************************************************/ + +static int __devinit se_init(struct snd_ice1712 *ice) +{ + struct se_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + + if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE90PCI) { + ice->num_total_dacs = 2; + ice->num_total_adcs = 0; + ice->vt1720 = 1; + return 0; + + } else if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE200PCI) { + ice->num_total_dacs = 8; + ice->num_total_adcs = 2; + se200pci_WM8740_init(ice); + se200pci_WM8766_init(ice); + se200pci_WM8776_init(ice); + ice->gpio.set_pro_rate = se200pci_set_pro_rate; + return 0; + } + + return -ENOENT; +} + +static int __devinit se_add_controls(struct snd_ice1712 *ice) +{ + int err; + + err = 0; + /* nothing to do for VT1724_SUBDEVICE_SE90PCI */ + if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE200PCI) + err = se200pci_add_controls(ice); + + return err; +} + + +/****************************************************************************/ +/* entry point */ +/****************************************************************************/ + +static unsigned char se200pci_eeprom[] __devinitdata = { + [ICE_EEP2_SYSCONF] = 0x4b, /* 49.152Hz, spdif-in/ADC, 4DACs */ + [ICE_EEP2_ACLINK] = 0x80, /* I2S */ + [ICE_EEP2_I2S] = 0x78, /* 96k-ok, 24bit, 192k-ok */ + [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ + + [ICE_EEP2_GPIO_DIR] = 0x02, /* WM8766 mute 1=output */ + [ICE_EEP2_GPIO_DIR1] = 0x00, /* not used */ + [ICE_EEP2_GPIO_DIR2] = 0x07, /* WM8766 ML/MC/MD 1=output */ + + [ICE_EEP2_GPIO_MASK] = 0x00, /* 0=writable */ + [ICE_EEP2_GPIO_MASK1] = 0x00, /* 0=writable */ + [ICE_EEP2_GPIO_MASK2] = 0x00, /* 0=writable */ + + [ICE_EEP2_GPIO_STATE] = 0x00, /* WM8766 mute=0 */ + [ICE_EEP2_GPIO_STATE1] = 0x00, /* not used */ + [ICE_EEP2_GPIO_STATE2] = 0x07, /* WM8766 ML/MC/MD */ +}; + +static unsigned char se90pci_eeprom[] __devinitdata = { + [ICE_EEP2_SYSCONF] = 0x4b, /* 49.152Hz, spdif-in/ADC, 4DACs */ + [ICE_EEP2_ACLINK] = 0x80, /* I2S */ + [ICE_EEP2_I2S] = 0x78, /* 96k-ok, 24bit, 192k-ok */ + [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ + + /* ALL GPIO bits are in input mode */ +}; + +struct snd_ice1712_card_info snd_vt1724_se_cards[] __devinitdata = { + { + .subvendor = VT1724_SUBDEVICE_SE200PCI, + .name = "ONKYO SE200PCI", + .model = "se200pci", + .chip_init = se_init, + .build_controls = se_add_controls, + .eeprom_size = sizeof(se200pci_eeprom), + .eeprom_data = se200pci_eeprom, + }, + { + .subvendor = VT1724_SUBDEVICE_SE90PCI, + .name = "ONKYO SE90PCI", + .model = "se90pci", + .chip_init = se_init, + .build_controls = se_add_controls, + .eeprom_size = sizeof(se90pci_eeprom), + .eeprom_data = se90pci_eeprom, + }, + {} /*terminator*/ +}; diff --git a/sound/pci/ice1712/se.h b/sound/pci/ice1712/se.h new file mode 100644 index 0000000..0b0a9da --- /dev/null +++ b/sound/pci/ice1712/se.h @@ -0,0 +1,15 @@ +#ifndef __SOUND_SE_H +#define __SOUND_SE_H + +/* ID */ +#define SE_DEVICE_DESC \ + "{ONKYO INC,SE-90PCI},"\ + "{ONKYO INC,SE-200PCI}," + +#define VT1724_SUBDEVICE_SE90PCI 0xb161000 +#define VT1724_SUBDEVICE_SE200PCI 0xb160100 + +/* entry struct */ +extern struct snd_ice1712_card_info snd_vt1724_se_cards[]; + +#endif /* __SOUND_SE_H */ diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c index 2395241..7f9674b 100644 --- a/sound/pci/ice1712/vt1720_mobo.c +++ b/sound/pci/ice1712/vt1720_mobo.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c index 7fcce0a..a08d17c 100644 --- a/sound/pci/ice1712/wtm.c +++ b/sound/pci/ice1712/wtm.c @@ -25,7 +25,6 @@ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -178,7 +177,7 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, if (kcontrol->private_value) { idx = STAC946X_MASTER_VOLUME; - nvol = ucontrol->value.integer.value[0]; + nvol = ucontrol->value.integer.value[0] & 0x7f; tmp = stac9460_get(ice, idx); ovol = 0x7f - (tmp & 0x7f); change = (ovol != nvol); @@ -189,7 +188,7 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, } else { id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); idx = id + STAC946X_LF_VOLUME; - nvol = ucontrol->value.integer.value[0]; + nvol = ucontrol->value.integer.value[0] & 0x7f; if (id < 6) tmp = stac9460_get(ice, idx); else @@ -317,7 +316,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, if (id == 0) { for (i = 0; i < 2; ++i) { reg = STAC946X_MIC_L_VOLUME + i; - nvol = ucontrol->value.integer.value[i]; + nvol = ucontrol->value.integer.value[i] & 0x0f; ovol = 0x0f - stac9460_get(ice, reg); change = ((ovol & 0x0f) != nvol); if (change) @@ -327,7 +326,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, } else { for (i = 0; i < 2; ++i) { reg = STAC946X_MIC_L_VOLUME + i; - nvol = ucontrol->value.integer.value[i]; + nvol = ucontrol->value.integer.value[i] & 0x0f; ovol = 0x0f - stac9460_2_get(ice, reg); change = ((ovol & 0x0f) != nvol); if (change) |