diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-02-01 10:16:28 +1100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-02-01 10:16:28 +1100 |
commit | e1a9c9872dd004617555dff079b357a6ffd945e9 (patch) | |
tree | c34779e59712ff345f8e4ee97e74086a85b34974 /sound/pci | |
parent | fcc3ff4f9d695a80dc6e6058e0d631a3026ed4c3 (diff) | |
parent | 2ecba4ffbbc6c85fce8c3878514be415edace413 (diff) |
Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/perex/alsa
* 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/perex/alsa: (299 commits)
[ALSA] version 1.0.16rc2
[ALSA] hda: fix Mic in as output
[ALSA] emu10k1 - Another EMU0404 Board ID
[ALSA] emu10k1 - Fix kthread handling at resume
[ALSA] emu10k1: General cleanup, add new locks, fix alsa bug#3501, kernel bug#9304.
[ALSA] emu10k1 - Use enum for emu_model types
[ALSA] emu10k1 - Don't create emu1010 controls for non-emu boards
[ALSA] emu10k1 - 1616(M) cardbus improvements
[ALSA] snd:emu10k1: E-Mu updates. Fixes to firmware loading and support for 0404.
[ALSA] emu10k1: Add comments regarding E-Mu ins and outs.
[ALSA] oxygen: revert SPI clock frequency change for AK4396/WM8785
[ALSA] es1938 - improve capture hw pointer reads
[ALSA] HDA-Intel - Add support for Intel SCH
[ALSA] hda: Add GPIO mute support to STAC9205
[ALSA] hda-codec - Add Dell T3400 support
[ALSA] hda-codec - Add model for HP DV9553EG laptop
[ALSA] hda-codec - Control SPDIF as slave
[ALSA] hda_intel: ALSA HD Audio patch for Intel ICH10 DeviceID's
[ALSA] Fix Oops with PCM OSS sync
[ALSA] hda-codec - Add speaker automute to ALC262 HP models
...
Diffstat (limited to 'sound/pci')
155 files changed, 14207 insertions, 2690 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 356bf21a150..812085d521f 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -183,6 +183,30 @@ config SND_CMIPCI To compile this driver as a module, choose M here: the module will be called snd-cmipci. +config SND_OXYGEN_LIB + tristate + depends on SND + select SND_PCM + select SND_MPU401_UART + +config SND_OXYGEN + tristate "C-Media 8788 (Oxygen)" + depends on SND + select SND_OXYGEN_LIB + help + Say Y here to include support for sound cards based on the + C-Media CMI8788 (Oxygen HD Audio) chip: + * Asound A-8788 + * AuzenTech X-Meridian + * Bgears b-Enspirer + * Club3D Theatron DTS + * HT-Omega Claro + * Razer Barracuda AC-1 + * Sondigo Inferno + + To compile this driver as a module, choose M here: the module + will be called snd-oxygen. + config SND_CS4281 tristate "Cirrus Logic (Sound Fusion) CS4281" depends on SND @@ -623,6 +647,17 @@ config SND_HDSPM To compile this driver as a module, choose M here: the module will be called snd-hdspm. +config SND_HIFIER + tristate "TempoTec HiFier Fantasia" + depends on SND + select SND_OXYGEN_LIB + help + Say Y here to include support for the MediaTek/TempoTec HiFier + Fantasia sound card. + + To compile this driver as a module, choose M here: the module + will be called snd-hifier. + config SND_ICE1712 tristate "ICEnsemble ICE1712 (Envy24)" depends on SND @@ -802,6 +837,16 @@ config SND_RME9652 To compile this driver as a module, choose M here: the module will be called snd-rme9652. +config SND_SIS7019 + tristate "SiS 7019 Audio Accelerator" + depends on SND && X86 && !X86_64 + select SND_AC97_CODEC + help + Say Y here to include support for the SiS 7019 Audio Accelerator. + + To compile this driver as a module, choose M here: the module + will be called snd-sis7019. + config SND_SONICVIBES tristate "S3 SonicVibes" depends on SND @@ -850,6 +895,17 @@ config SND_VIA82XX_MODEM To compile this driver as a module, choose M here: the module will be called snd-via82xx-modem. +config SND_VIRTUOSO + tristate "Asus Virtuoso 200 (Xonar)" + depends on SND + select SND_OXYGEN_LIB + help + Say Y here to include support for sound cards based on the + Asus AV200 chip, i.e., Xonar D2 and Xonar D2X. + + To compile this driver as a module, choose M here: the module + will be called snd-virtuoso. + config SND_VX222 tristate "Digigram VX222" depends on SND diff --git a/sound/pci/Makefile b/sound/pci/Makefile index 09ddc82eeca..2d42fd28f4e 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -23,6 +23,7 @@ snd-intel8x0m-objs := intel8x0m.o snd-maestro3-objs := maestro3.o snd-rme32-objs := rme32.o snd-rme96-objs := rme96.o +snd-sis7019-objs := sis7019.o snd-sonicvibes-objs := sonicvibes.o snd-via82xx-objs := via82xx.o snd-via82xx-modem-objs := via82xx_modem.o @@ -48,6 +49,7 @@ obj-$(CONFIG_SND_INTEL8X0M) += snd-intel8x0m.o obj-$(CONFIG_SND_MAESTRO3) += snd-maestro3.o obj-$(CONFIG_SND_RME32) += snd-rme32.o obj-$(CONFIG_SND_RME96) += snd-rme96.o +obj-$(CONFIG_SND_SIS7019) += snd-sis7019.o obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o obj-$(CONFIG_SND_VIA82XX_MODEM) += snd-via82xx-modem.o @@ -66,6 +68,7 @@ obj-$(CONFIG_SND) += \ korg1212/ \ mixart/ \ nm256/ \ + oxygen/ \ pcxhr/ \ riptide/ \ rme9652/ \ diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 6a9966df0cc..45fd29017dd 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -22,7 +22,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 98c8b727b62..50c637e55ff 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -133,6 +133,14 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); unsigned char mode = ucontrol->value.enumerated.item[0]; + if (kcontrol->private_value) { + if (mode >= 2) + return -EINVAL; + } else { + if (mode >= 3) + return -EINVAL; + } + if (mode != ac97->channel_mode) { ac97->channel_mode = mode; if (ac97->build_ops->update_jacks) @@ -2142,8 +2150,7 @@ static int snd_ac97_ad1985_vrefout_put(struct snd_kcontrol *kcontrol, struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; - if (ucontrol->value.enumerated.item[0] > 3 - || ucontrol->value.enumerated.item[0] < 0) + if (ucontrol->value.enumerated.item[0] > 3) return -EINVAL; val = ctrl2reg[ucontrol->value.enumerated.item[0]] << AC97_AD198X_VREF_SHIFT; diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h index 9cccc27ea1b..47bf8dfe827 100644 --- a/sound/pci/ac97/ac97_patch.h +++ b/sound/pci/ac97/ac97_patch.h @@ -83,8 +83,10 @@ static int snd_ac97_swap_ctl(struct snd_ac97 *ac97, const char *s1, const char *s2, const char *suffix); static void snd_ac97_rename_vol_ctl(struct snd_ac97 *ac97, const char *src, const char *dst); +#ifdef CONFIG_PM static void snd_ac97_restore_status(struct snd_ac97 *ac97); static void snd_ac97_restore_iec958(struct snd_ac97 *ac97); +#endif static int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); static int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c index 8cbc03332b0..3674f35c4a7 100644 --- a/sound/pci/ac97/ac97_pcm.c +++ b/sound/pci/ac97/ac97_pcm.c @@ -23,7 +23,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c index fed4a2c3d8a..060ea59d5f0 100644 --- a/sound/pci/ac97/ac97_proc.c +++ b/sound/pci/ac97/ac97_proc.c @@ -22,7 +22,6 @@ * */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/mutex.h> diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c index 722de451d15..c0c1633999e 100644 --- a/sound/pci/ac97/ak4531_codec.c +++ b/sound/pci/ac97/ak4531_codec.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index 98970d401be..a66d5150bb7 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -40,7 +40,6 @@ #include <linux/compiler.h> #include <linux/delay.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/initval.h> @@ -1055,7 +1054,7 @@ static struct pci_device_id snd_ad1889_ids[] = { }; MODULE_DEVICE_TABLE(pci, snd_ad1889_ids); -static struct pci_driver ad1889_pci = { +static struct pci_driver ad1889_pci_driver = { .name = "AD1889 Audio", .id_table = snd_ad1889_ids, .probe = snd_ad1889_probe, @@ -1065,13 +1064,13 @@ static struct pci_driver ad1889_pci = { static int __init alsa_ad1889_init(void) { - return pci_register_driver(&ad1889_pci); + return pci_register_driver(&ad1889_pci_driver); } static void __exit alsa_ad1889_fini(void) { - pci_unregister_driver(&ad1889_pci); + pci_unregister_driver(&ad1889_pci_driver); } module_init(alsa_ad1889_init); diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 4c2bd7adf67..6a905ed9cbd 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -25,7 +25,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 48cc39b771d..0e990a73582 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -30,7 +30,6 @@ * to keep track of what period we are in. */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/moduleparam.h> diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 1190ef366a4..27ce6136ab0 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -63,7 +63,6 @@ * - power management? (card can do voice wakeup according to datasheet!!) */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/init.h> #include <linux/pci.h> diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 89184a42414..4594186b83e 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -560,7 +559,7 @@ static int snd_atiixp_aclink_down(struct atiixp *chip) ATI_REG_ISR_CODEC2_NOT_READY) #define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME) -static int ac97_probing_bugs(struct pci_dev *pci) +static int __devinit ac97_probing_bugs(struct pci_dev *pci) { const struct snd_pci_quirk *q; @@ -574,7 +573,7 @@ static int ac97_probing_bugs(struct pci_dev *pci) return -1; } -static int snd_atiixp_codec_detect(struct atiixp *chip) +static int __devinit snd_atiixp_codec_detect(struct atiixp *chip) { int timeout; diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index ce752f84457..a67a869180d 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h index 5ccf0b1ec67..4aad35bba11 100644 --- a/sound/pci/au88x0/au88x0.h +++ b/sound/pci/au88x0/au88x0.h @@ -18,7 +18,6 @@ #define __SOUND_AU88X0_H #ifdef __KERNEL__ -#include <sound/driver.h> #include <linux/pci.h> #include <asm/io.h> #include <sound/core.h> diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index 4a336eaae9d..333c62de862 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -2395,7 +2395,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id) if (!(hwread(vortex->mmio, VORTEX_STAT) & 0x1)) return IRQ_NONE; - // This is the Interrrupt Enable flag we set before (consistency check). + // This is the Interrupt Enable flag we set before (consistency check). if (!(hwread(vortex->mmio, VORTEX_CTRL) & CTRL_IRQ_ENABLE)) return IRQ_NONE; diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c index a07d1deba32..bc212f41a38 100644 --- a/sound/pci/au88x0/au88x0_game.c +++ b/sound/pci/au88x0/au88x0_game.c @@ -30,7 +30,6 @@ * driver. (email: mjander@embedded.cl). */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/delay.h> #include <linux/init.h> diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c index c96da1dab86..c92f493d341 100644 --- a/sound/pci/au88x0/au88x0_mixer.c +++ b/sound/pci/au88x0/au88x0_mixer.c @@ -5,7 +5,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/init.h> #include <sound/core.h> diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c index 8db3d3e6f7b..0dc8d259d1e 100644 --- a/sound/pci/au88x0/au88x0_mpu401.c +++ b/sound/pci/au88x0/au88x0_mpu401.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/init.h> #include <sound/core.h> diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 7b5baa17385..526c6c5ecf7 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -21,7 +21,6 @@ * It remains stuck,and DMA transfers do not happen. */ #include <sound/asoundef.h> -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 36d3666a5b7..4e71a55120a 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -115,7 +115,6 @@ * code (but I'm not too optimistic that doing this is possible at all) */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/init.h> #include <linux/pci.h> diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 2dba752faf4..c9a2421cf6f 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h index 75da1746e75..74175fc80c7 100644 --- a/sound/pci/ca0106/ca0106.h +++ b/sound/pci/ca0106/ca0106.h @@ -272,7 +272,6 @@ #define SPCS_WORD_LENGTH_20A 0x0000000a /* Word Length 20 bit */ #define SPCS_WORD_LENGTH_20 0x00000009 /* Word Length 20 bit (both 0xa and 0x9 are 20 bit) */ #define SPCS_WORD_LENGTH_21 0x00000007 /* Word Length 21 bit */ -#define SPCS_WORD_LENGTH_21 0x00000007 /* Word Length 21 bit */ #define SPCS_WORD_LENGTH_22 0x00000005 /* Word Length 22 bit */ #define SPCS_WORD_LENGTH_23 0x00000003 /* Word Length 23 bit */ #define SPCS_WORD_LENGTH_24 0x0000000b /* Word Length 24 bit */ diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 31d8db9f7a4..176e0f0e805 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -135,7 +135,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 3f9b5c56003..af736869d9b 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -60,7 +60,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c index 61f2718ae35..c62b7d10ec6 100644 --- a/sound/pci/ca0106/ca0106_proc.c +++ b/sound/pci/ca0106/ca0106_proc.c @@ -60,7 +60,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c index ad32eff2713..893ee4f1ea7 100644 --- a/sound/pci/ca0106/ca_midi.c +++ b/sound/pci/ca0106/ca_midi.c @@ -27,7 +27,6 @@ */ #include <linux/spinlock.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/rawmidi.h> diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 1fa5f004e85..135f3086075 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -20,7 +20,6 @@ /* Does not work. Warning may block system in capture mode */ /* #define USE_VAR48KRATE */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -150,6 +149,8 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address."); #define CM_CH0_SRATE_176K 0x00000200 #define CM_CH0_SRATE_96K 0x00000200 /* model 055? */ #define CM_CH0_SRATE_88K 0x00000100 +#define CM_CH0_SRATE_128K 0x00000300 +#define CM_CH0_SRATE_MASK 0x00000300 #define CM_SPDIF_INVERSE2 0x00000080 /* model 055? */ #define CM_DBLSPDS 0x00000040 /* double SPDIF sample rate 88.2/96 */ @@ -473,6 +474,7 @@ struct cmipci { unsigned int can_ac3_sw: 1; unsigned int can_ac3_hw: 1; unsigned int can_multi_ch: 1; + unsigned int can_96k: 1; /* samplerate above 48k */ unsigned int do_soft_ac3: 1; unsigned int spdif_playback_avail: 1; /* spdif ready? */ @@ -603,8 +605,6 @@ static unsigned int snd_cmipci_rate_freq(unsigned int rate) { unsigned int i; - if (rate > 48000) - rate /= 2; for (i = 0; i < ARRAY_SIZE(rates); i++) { if (rates[i] == rate) return i; @@ -782,7 +782,7 @@ static int set_dac_channels(struct cmipci *cm, struct cmipci_pcm *rec, int chann static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec, struct snd_pcm_substream *substream) { - unsigned int reg, freq, val; + unsigned int reg, freq, freq_ext, val; unsigned int period_size; struct snd_pcm_runtime *runtime = substream->runtime; @@ -830,7 +830,17 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec, //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl); /* set sample rate */ - freq = snd_cmipci_rate_freq(runtime->rate); + freq = 0; + freq_ext = 0; + if (runtime->rate > 48000) + switch (runtime->rate) { + case 88200: freq_ext = CM_CH0_SRATE_88K; break; + case 96000: freq_ext = CM_CH0_SRATE_96K; break; + case 128000: freq_ext = CM_CH0_SRATE_128K; break; + default: snd_BUG(); break; + } + else + freq = snd_cmipci_rate_freq(runtime->rate); val = snd_cmipci_read(cm, CM_REG_FUNCTRL1); if (rec->ch) { val &= ~CM_DSFC_MASK; @@ -851,19 +861,20 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec, val &= ~CM_CH0FMT_MASK; val |= rec->fmt << CM_CH0FMT_SHIFT; } - if (cm->chip_version == 68) { - if (runtime->rate == 88200) - val |= CM_CH0_SRATE_88K << (rec->ch * 2); - else - val &= ~(CM_CH0_SRATE_88K << (rec->ch * 2)); - if (runtime->rate == 96000) - val |= CM_CH0_SRATE_96K << (rec->ch * 2); - else - val &= ~(CM_CH0_SRATE_96K << (rec->ch * 2)); + if (cm->can_96k) { + val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2)); + val |= freq_ext << (rec->ch * 2); } snd_cmipci_write(cm, CM_REG_CHFORMAT, val); //snd_printd("cmipci: chformat = %08x\n", val); + if (!rec->is_dac && cm->chip_version) { + if (runtime->rate > 44100) + snd_cmipci_set_bit(cm, CM_REG_EXT_MISC, CM_ADC48K44K); + else + snd_cmipci_clear_bit(cm, CM_REG_EXT_MISC, CM_ADC48K44K); + } + rec->running = 0; spin_unlock_irq(&cm->reg_lock); @@ -1280,7 +1291,7 @@ static int snd_cmipci_playback_prepare(struct snd_pcm_substream *substream) int rate = substream->runtime->rate; int err, do_spdif, do_ac3 = 0; - do_spdif = (rate >= 44100 && + do_spdif = (rate >= 44100 && rate <= 96000 && substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE && substream->runtime->channels == 2); if (do_spdif && cm->can_ac3_hw) @@ -1336,10 +1347,8 @@ static void snd_cmipci_silence_hack(struct cmipci *cm, struct cmipci_pcm *rec) val = snd_cmipci_read(cm, CM_REG_CHFORMAT); val &= ~(CM_CH0FMT_MASK << (rec->ch * 2)); val |= (3 << CM_CH0FMT_SHIFT) << (rec->ch * 2); - if (cm->chip_version == 68) { - val &= ~(CM_CH0_SRATE_88K << (rec->ch * 2)); - val &= ~(CM_CH0_SRATE_96K << (rec->ch * 2)); - } + if (cm->can_96k) + val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2)); snd_cmipci_write(cm, CM_REG_CHFORMAT, val); /* start stream (we don't need interrupts) */ @@ -1391,6 +1400,17 @@ static int snd_cmipci_capture_spdif_prepare(struct snd_pcm_substream *substream) spin_lock_irq(&cm->reg_lock); snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); + if (cm->can_96k) { + if (substream->runtime->rate > 48000) + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS); + else + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS); + } + if (snd_pcm_format_width(substream->runtime->format) > 16) + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); + else + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); + spin_unlock_irq(&cm->reg_lock); return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream); @@ -1402,6 +1422,7 @@ static int snd_cmipci_capture_spdif_hw_free(struct snd_pcm_substream *subs) spin_lock_irq(&cm->reg_lock); snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); spin_unlock_irq(&cm->reg_lock); return snd_cmipci_hw_free(subs); @@ -1553,7 +1574,8 @@ static struct snd_pcm_hardware snd_cmipci_capture_spdif = .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .rate_min = 44100, .rate_max = 48000, @@ -1567,6 +1589,14 @@ static struct snd_pcm_hardware snd_cmipci_capture_spdif = .fifo_size = 0, }; +static unsigned int rate_constraints[] = { 5512, 8000, 11025, 16000, 22050, + 32000, 44100, 48000, 88200, 96000, 128000 }; +static struct snd_pcm_hw_constraint_list hw_constraints_rates = { + .count = ARRAY_SIZE(rate_constraints), + .list = rate_constraints, + .mask = 0, +}; + /* * check device open/close */ @@ -1636,6 +1666,13 @@ static int snd_cmipci_playback_open(struct snd_pcm_substream *substream) runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; + } else if (cm->chip_version == 55) { + err = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + if (err < 0) + return err; + runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; + runtime->hw.rate_max = 128000; } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); cm->dig_pcm_status = cm->dig_status; @@ -1654,6 +1691,13 @@ static int snd_cmipci_capture_open(struct snd_pcm_substream *substream) if (cm->chip_version == 68) { // 8768 only supports 44k/48k recording runtime->hw.rate_min = 41000; runtime->hw.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000; + } else if (cm->chip_version == 55) { + err = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + if (err < 0) + return err; + runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; + runtime->hw.rate_max = 128000; } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); return 0; @@ -1685,6 +1729,13 @@ static int snd_cmipci_playback2_open(struct snd_pcm_substream *substream) runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; + } else if (cm->chip_version == 55) { + err = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + if (err < 0) + return err; + runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; + runtime->hw.rate_max = 128000; } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); return 0; @@ -1704,7 +1755,7 @@ static int snd_cmipci_playback_spdif_open(struct snd_pcm_substream *substream) runtime->hw.formats |= SNDRV_PCM_FMTBIT_S32_LE; snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); } - if (cm->chip_version == 68) { + if (cm->can_96k) { runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; @@ -1726,6 +1777,11 @@ static int snd_cmipci_capture_spdif_open(struct snd_pcm_substream *substream) if ((err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream)) < 0) /* use channel B */ return err; runtime->hw = snd_cmipci_capture_spdif; + if (cm->can_96k && !(cm->chip_version == 68)) { + runtime->hw.rates |= SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000; + runtime->hw.rate_max = 96000; + } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000); return 0; } @@ -2594,10 +2650,8 @@ static struct snd_kcontrol_new snd_cmipci_extra_mixer_switches[] __devinitdata = }; /* card control switches */ -static struct snd_kcontrol_new snd_cmipci_control_switches[] __devinitdata = { - // DEFINE_CARD_SWITCH("Joystick", joystick), /* now module option */ - DEFINE_CARD_SWITCH("Modem", modem), -}; +static struct snd_kcontrol_new snd_cmipci_modem_switch __devinitdata = +DEFINE_CARD_SWITCH("Modem", modem); static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device) @@ -2678,9 +2732,13 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic } /* card switches */ - sw = snd_cmipci_control_switches; - for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_control_switches); idx++, sw++) { - err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + /* + * newer chips don't have the register bits to force modem link + * detection; the bit that was FLINKON now mutes CH1 + */ + if (cm->chip_version < 39) { + err = snd_ctl_add(cm->card, + snd_ctl_new1(&snd_cmipci_modem_switch, cm)); if (err < 0) return err; } @@ -2785,9 +2843,11 @@ static void __devinit query_chip(struct cmipci *cm) } else if (detect & CM_CHIP_8768) { cm->chip_version = 68; cm->max_channels = 8; + cm->can_96k = 1; } else { cm->chip_version = 55; cm->max_channels = 6; + cm->can_96k = 1; } cm->can_ac3_hw = 1; cm->can_multi_ch = 1; diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 9a55f4a9739..7556fd90d0e 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 2699cb6c2cd..e876b3263e4 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -25,7 +25,6 @@ reloading the module may solve this. */ -#include <sound/driver.h> #include <linux/pci.h> #include <linux/time.h> #include <linux/init.h> diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 2c7bfc9fef6..87ddffcd9d8 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -8,7 +8,7 @@ * - Sometimes the SPDIF input DSP tasks get's unsynchronized * and the SPDIF get somewhat "distorcionated", or/and left right channel * are swapped. To get around this problem when it happens, mute and unmute - * the SPDIF input mixer controll. + * the SPDIF input mixer control. * - On the Hercules Game Theater XP the amplifier are sometimes turned * off on inadecuate moments which causes distorcions on sound. * @@ -45,7 +45,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/pci.h> #include <linux/pm.h> @@ -2084,71 +2083,6 @@ static int snd_cs46xx_spdif_stream_put(struct snd_kcontrol *kcontrol, #endif /* CONFIG_SND_CS46XX_NEW_DSP */ -#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO -static int snd_cs46xx_egpio_select_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 8; - return 0; -} - -static int snd_cs46xx_egpio_select_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = chip->current_gpio; - - return 0; -} - -static int snd_cs46xx_egpio_select_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol); - int change = (chip->current_gpio != ucontrol->value.integer.value[0]); - chip->current_gpio = ucontrol->value.integer.value[0]; - - return change; -} - - -static int snd_cs46xx_egpio_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol); - int reg = kcontrol->private_value; - - snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio); - ucontrol->value.integer.value[0] = - (snd_cs46xx_peekBA0(chip, reg) & (1 << chip->current_gpio)) ? 1 : 0; - - return 0; -} - -static int snd_cs46xx_egpio_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol); - int reg = kcontrol->private_value; - int val = snd_cs46xx_peekBA0(chip, reg); - int oldval = val; - snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio); - - if (ucontrol->value.integer.value[0]) - val |= (1 << chip->current_gpio); - else - val &= ~(1 << chip->current_gpio); - - snd_cs46xx_pokeBA0(chip, reg,val); - snd_printdd ("put: val %08x oldval %08x\n",val,oldval); - - return (oldval != val); -} -#endif /* CONFIG_SND_CS46XX_DEBUG_GPIO */ - static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -2241,40 +2175,6 @@ static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = { }, #endif -#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "EGPIO select", - .info = snd_cs46xx_egpio_select_info, - .get = snd_cs46xx_egpio_select_get, - .put = snd_cs46xx_egpio_select_put, - .private_value = 0, -}, -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "EGPIO Input/Output", - .info = snd_mixer_boolean_info, - .get = snd_cs46xx_egpio_get, - .put = snd_cs46xx_egpio_put, - .private_value = BA0_EGPIODR, -}, -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "EGPIO CMOS/Open drain", - .info = snd_mixer_boolean_info, - .get = snd_cs46xx_egpio_get, - .put = snd_cs46xx_egpio_put, - .private_value = BA0_EGPIOPTR, -}, -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "EGPIO On/Off", - .info = snd_mixer_boolean_info, - .get = snd_cs46xx_egpio_get, - .put = snd_cs46xx_egpio_put, - .private_value = BA0_EGPIOSR, -}, -#endif }; #ifdef CONFIG_SND_CS46XX_NEW_DSP diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index 590b35d91df..ccc8bedb5b1 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -20,7 +20,6 @@ */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/pm.h> diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c index eded4dfeba1..2873cfe48c3 100644 --- a/sound/pci/cs46xx/dsp_spos_scb_lib.c +++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c @@ -21,7 +21,6 @@ */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/pm.h> diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c index 240a0a46220..7ff8b68e997 100644 --- a/sound/pci/cs5530.c +++ b/sound/pci/cs5530.c @@ -36,7 +36,6 @@ * same manner. */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/moduleparam.h> #include <linux/pci.h> diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 2b35889787b..1d8b1605253 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/init.h> @@ -145,7 +144,7 @@ static unsigned short snd_cs5535audio_ac97_codec_read(struct snd_ac97 *ac97, return snd_cs5535audio_codec_read(cs5535au, reg); } -static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au) +static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au) { struct snd_card *card = cs5535au->card; struct snd_ac97_bus *pbus; diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c index 21df0634af3..cdcda87116c 100644 --- a/sound/pci/cs5535audio/cs5535audio_pcm.c +++ b/sound/pci/cs5535audio/cs5535audio_pcm.c @@ -25,7 +25,6 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/pci.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/control.h> #include <sound/initval.h> @@ -98,6 +97,8 @@ static int snd_cs5535audio_playback_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; runtime->hw = snd_cs5535audio_playback; + runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_FRONT_DAC]; + snd_pcm_limit_hw_rates(runtime); cs5535au->playback_substream = substream; runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK]); if ((err = snd_pcm_hw_constraint_integer(runtime, @@ -343,6 +344,8 @@ static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; runtime->hw = snd_cs5535audio_capture; + runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_ADC]; + snd_pcm_limit_hw_rates(runtime); cs5535au->capture_substream = substream; runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]); if ((err = snd_pcm_hw_constraint_integer(runtime, diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c index 838708f6d45..564c33b6095 100644 --- a/sound/pci/cs5535audio/cs5535audio_pm.c +++ b/sound/pci/cs5535audio/cs5535audio_pm.c @@ -22,7 +22,6 @@ #include <linux/slab.h> #include <linux/pci.h> #include <linux/delay.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/control.h> #include <sound/initval.h> diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c index 87078d3a685..8c6db3aa3c1 100644 --- a/sound/pci/echoaudio/darla20.c +++ b/sound/pci/echoaudio/darla20.c @@ -36,7 +36,6 @@ #define BX_NUM 10 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c index 42b48f9d212..04cbf3eaf05 100644 --- a/sound/pci/echoaudio/darla24.c +++ b/sound/pci/echoaudio/darla24.c @@ -40,7 +40,6 @@ #define BX_NUM 10 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c index 8dbb7ac865c..4022e43a005 100644 --- a/sound/pci/echoaudio/echo3g.c +++ b/sound/pci/echoaudio/echo3g.c @@ -47,7 +47,6 @@ #define BX_NUM chip->bx_num -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 499ee1a5319..90ec090792b 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -378,7 +378,7 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream) DE_ACT(("pcm_digital_in_open\n")); max_channels = num_digital_busses_in(chip) - substream->number; - down(&chip->mode_mutex); + mutex_lock(&chip->mode_mutex); if (chip->digital_mode == DIGITAL_MODE_ADAT) err = pcm_open(substream, max_channels); else /* If the card has ADAT, subtract the 6 channels @@ -405,7 +405,7 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream) chip->can_set_rate=0; din_exit: - up(&chip->mode_mutex); + mutex_unlock(&chip->mode_mutex); return err; } @@ -420,7 +420,7 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream) DE_ACT(("pcm_digital_out_open\n")); max_channels = num_digital_busses_out(chip) - substream->number; - down(&chip->mode_mutex); + mutex_lock(&chip->mode_mutex); if (chip->digital_mode == DIGITAL_MODE_ADAT) err = pcm_open(substream, max_channels); else /* If the card has ADAT, subtract the 6 channels @@ -447,7 +447,7 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream) if (atomic_read(&chip->opencount) > 1 && chip->rate_set) chip->can_set_rate=0; dout_exit: - up(&chip->mode_mutex); + mutex_unlock(&chip->mode_mutex); return err; } @@ -1420,7 +1420,7 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol, if (dmode != chip->digital_mode) { /* mode_mutex is required to make this operation atomic wrt pcm_digital_*_open() and set_input_clock() functions. */ - down(&chip->mode_mutex); + mutex_lock(&chip->mode_mutex); /* Do not allow the user to change the digital mode when a pcm device is open because it also changes the number of channels @@ -1439,7 +1439,7 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol, if (changed >= 0) changed = 1; /* No errors */ } - up(&chip->mode_mutex); + mutex_unlock(&chip->mode_mutex); } return changed; } @@ -1566,12 +1566,12 @@ static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol, return -EINVAL; dclock = chip->clock_source_list[eclock]; if (chip->input_clock != dclock) { - down(&chip->mode_mutex); + mutex_lock(&chip->mode_mutex); spin_lock_irq(&chip->lock); if ((changed = set_input_clock(chip, dclock)) == 0) changed = 1; /* no errors */ spin_unlock_irq(&chip->lock); - up(&chip->mode_mutex); + mutex_unlock(&chip->mode_mutex); } if (changed < 0) @@ -1972,7 +1972,7 @@ static __devinit int snd_echo_create(struct snd_card *card, return err; } atomic_set(&chip->opencount, 0); - init_MUTEX(&chip->mode_mutex); + mutex_init(&chip->mode_mutex); chip->can_set_rate = 1; *rchip = chip; /* Init done ! */ diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h index 7e88c968e22..1c88e051abf 100644 --- a/sound/pci/echoaudio/echoaudio.h +++ b/sound/pci/echoaudio/echoaudio.h @@ -361,7 +361,7 @@ struct echoaudio { spinlock_t lock; struct snd_pcm_substream *substream[DSP_MAXPIPES]; int last_period[DSP_MAXPIPES]; - struct semaphore mode_mutex; + struct mutex mode_mutex; u16 num_digital_modes, digital_mode_list[6]; u16 num_clock_sources, clock_source_list[10]; atomic_t opencount; diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c index fee2d483173..c0e64b8f52a 100644 --- a/sound/pci/echoaudio/gina20.c +++ b/sound/pci/echoaudio/gina20.c @@ -40,7 +40,6 @@ #define BX_NUM 14 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c index d5eae470fe9..c36a78dd0b5 100644 --- a/sound/pci/echoaudio/gina24.c +++ b/sound/pci/echoaudio/gina24.c @@ -46,7 +46,6 @@ #define BX_NUM 26 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c index 40f601cd016..0a58a7c1fd7 100644 --- a/sound/pci/echoaudio/indigo.c +++ b/sound/pci/echoaudio/indigo.c @@ -38,7 +38,6 @@ #define BX_NUM 2 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c index 771c5383210..2db24d29332 100644 --- a/sound/pci/echoaudio/indigodj.c +++ b/sound/pci/echoaudio/indigodj.c @@ -38,7 +38,6 @@ #define BX_NUM 4 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c index 49c550defcf..a60c0a0a89b 100644 --- a/sound/pci/echoaudio/indigoio.c +++ b/sound/pci/echoaudio/indigoio.c @@ -39,7 +39,6 @@ #define BX_NUM 4 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c index 8f5483a405a..50619468899 100644 --- a/sound/pci/echoaudio/layla20.c +++ b/sound/pci/echoaudio/layla20.c @@ -45,7 +45,6 @@ #define BX_NUM 22 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c index 0524667c02f..e09e3ea7781 100644 --- a/sound/pci/echoaudio/layla24.c +++ b/sound/pci/echoaudio/layla24.c @@ -47,7 +47,6 @@ #define BX_NUM 32 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c index 893c7c20dd7..f3b9b45c9c1 100644 --- a/sound/pci/echoaudio/mia.c +++ b/sound/pci/echoaudio/mia.c @@ -45,7 +45,6 @@ #define BX_NUM 8 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c index 3a5d5b0020d..b05bad94490 100644 --- a/sound/pci/echoaudio/mona.c +++ b/sound/pci/echoaudio/mona.c @@ -44,7 +44,6 @@ #define BX_NUM 26 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 9680caff90c..8354c1a8331 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -23,7 +23,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/time.h> diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c index 01965bd9996..45088ebcce5 100644 --- a/sound/pci/emu10k1/emu10k1_callback.c +++ b/sound/pci/emu10k1/emu10k1_callback.c @@ -35,9 +35,9 @@ struct best_voice { /* * prototypes */ -static void lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw, +static void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw, struct best_voice *best, int active_only); -static struct snd_emux_voice *get_voice(struct snd_emux *emu, +static struct snd_emux_voice *get_voice(struct snd_emux *emux, struct snd_emux_port *port); static int start_voice(struct snd_emux_voice *vp); static void trigger_voice(struct snd_emux_voice *vp); @@ -45,7 +45,6 @@ static void release_voice(struct snd_emux_voice *vp); static void update_voice(struct snd_emux_voice *vp, int update); static void terminate_voice(struct snd_emux_voice *vp); static void free_voice(struct snd_emux_voice *vp); - static void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp); static void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp); static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp); @@ -75,9 +74,9 @@ static struct snd_emux_operators emu10k1_ops = { }; void -snd_emu10k1_ops_setup(struct snd_emux *emu) +snd_emu10k1_ops_setup(struct snd_emux *emux) { - emu->ops = emu10k1_ops; + emux->ops = emu10k1_ops; } @@ -166,7 +165,11 @@ free_voice(struct snd_emux_voice *vp) struct snd_emu10k1 *hw; hw = vp->hw; - if (vp->ch >= 0) { + /* FIXME: emu10k1_synth is broken. */ + /* This can get called with hw == 0 */ + /* Problem apparent on plug, unplug then plug */ + /* on the Audigy 2 ZS Notebook. */ + if (hw && (vp->ch >= 0)) { snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00); snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK); // snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 97c41d72a25..9a9b977d3cf 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -33,7 +33,6 @@ #include <linux/sched.h> #include <linux/kthread.h> -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -55,12 +54,14 @@ #define DOCK_FILENAME "emu/audio_dock.fw" #define EMU1010B_FILENAME "emu/emu1010b.fw" #define MICRO_DOCK_FILENAME "emu/micro_dock.fw" +#define EMU0404_FILENAME "emu/emu0404.fw" #define EMU1010_NOTEBOOK_FILENAME "emu/emu1010_notebook.fw" MODULE_FIRMWARE(HANA_FILENAME); MODULE_FIRMWARE(DOCK_FILENAME); MODULE_FIRMWARE(EMU1010B_FILENAME); MODULE_FIRMWARE(MICRO_DOCK_FILENAME); +MODULE_FIRMWARE(EMU0404_FILENAME); MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME); @@ -258,7 +259,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) * GPIO7: Unknown */ outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */ - } if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */ int size, n; @@ -274,7 +274,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) emu->i2c_capture_volume[n][0]= 0xcf; emu->i2c_capture_volume[n][1]= 0xcf; } - } @@ -288,7 +287,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page); } - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { outl(HCFG_AUTOMUTE_ASYNC | HCFG_EMU32_SLAVE | HCFG_AUDIOENABLE, emu->port + HCFG); @@ -318,7 +317,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); if (enable_ir) { /* enable IR for SB Live */ - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { ; /* Disable all access to A_IOCFG for the emu1010 */ } else if (emu->card_capabilities->i2c_adc) { ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ @@ -339,7 +338,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) } } - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { ; /* Disable all access to A_IOCFG for the emu1010 */ } else if (emu->card_capabilities->i2c_adc) { ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ @@ -359,7 +358,7 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu) outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG); /* Enable analog/digital outs on audigy */ - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { ; /* Disable all access to A_IOCFG for the emu1010 */ } else if (emu->card_capabilities->i2c_adc) { ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ @@ -652,6 +651,8 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu) value = inl(special_port); snd_emu10k1_ptr20_write(emu, TINA2_VOLUME, 0, 0xfefefefe); /* Defaults to 0x30303030 */ + /* Delay to give time for ADC chip to switch on. It needs 113ms */ + msleep(200); return 0; } @@ -661,6 +662,8 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file int n, i; int reg; int value; + unsigned int write_post; + unsigned long flags; const struct firmware *fw_entry; if ((err = request_firmware(&fw_entry, filename, &emu->pci->dev)) != 0) { @@ -668,12 +671,6 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file return err; } snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size); -#if 0 - if (fw_entry->size != 0x133a4) { - snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename); - return -EINVAL; - } -#endif /* The FPGA is a Xilinx Spartan IIE XC2S50E */ /* GPIO7 -> FPGA PGMN @@ -681,9 +678,12 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file * GPIO5 -> FPGA DIN * FPGA CONFIG OFF -> FPGA PGMN */ + spin_lock_irqsave(&emu->emu_lock, flags); outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */ - udelay(1); + write_post = inl(emu->port + A_IOCFG); + udelay(100); outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */ + write_post = inl(emu->port + A_IOCFG); udelay(100); /* Allow FPGA memory to clean */ for(n = 0; n < fw_entry->size; n++) { value=fw_entry->data[n]; @@ -693,18 +693,22 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file reg = reg | 0x20; value = value >> 1; outl(reg, emu->port + A_IOCFG); + write_post = inl(emu->port + A_IOCFG); outl(reg | 0x40, emu->port + A_IOCFG); + write_post = inl(emu->port + A_IOCFG); } } /* After programming, set GPIO bit 4 high again. */ outl(0x10, emu->port + A_IOCFG); - + write_post = inl(emu->port + A_IOCFG); + spin_unlock_irqrestore(&emu->emu_lock, flags); release_firmware(fw_entry); return 0; } -int emu1010_firmware_thread(void *data) { +static int emu1010_firmware_thread(void *data) +{ struct snd_emu10k1 * emu = data; int tmp,tmp2; int reg; @@ -712,7 +716,7 @@ int emu1010_firmware_thread(void *data) { for (;;) { /* Delay to allow Audio Dock to settle */ - msleep(1000); + msleep_interruptible(1000); if (kthread_should_stop()) break; snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */ @@ -722,17 +726,20 @@ int emu1010_firmware_thread(void *data) { /* Return to Audio Dock programming mode */ snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n"); snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK ); - if (emu->card_capabilities->emu1010 == 1) { + if (emu->card_capabilities->emu_model == + EMU_MODEL_EMU1010) { if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) { - return err; + continue; } - } else if (emu->card_capabilities->emu1010 == 2) { + } else if (emu->card_capabilities->emu_model == + EMU_MODEL_EMU1010B) { if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) { - return err; + continue; } - } else if (emu->card_capabilities->emu1010 == 3) { + } else if (emu->card_capabilities->emu_model == + EMU_MODEL_EMU1616) { if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) { - return err; + continue; } } @@ -745,8 +752,7 @@ int emu1010_firmware_thread(void *data) { if ((reg & 0x1f) != 0x15) { /* FPGA failed to be programmed */ snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg); - return 0; - return -ENODEV; + continue; } snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n"); snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp ); @@ -757,9 +763,9 @@ int emu1010_firmware_thread(void *data) { msleep(10); /* Unmute all. Default is muted after a firmware load */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE ); - break; } } + snd_printk(KERN_INFO "emu1010: firmware thread stopping\n"); return 0; } @@ -800,6 +806,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) int tmp,tmp2; int reg; int err; + const char *filename = NULL; snd_printk(KERN_INFO "emu1010: Special config.\n"); /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave, @@ -841,21 +848,31 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) return -ENODEV; } snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg); - if (emu->card_capabilities->emu1010 == 1) { - if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) { - snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME); - return err; - } - } else if (emu->card_capabilities->emu1010 == 2) { - if ((err = snd_emu1010_load_firmware(emu, EMU1010B_FILENAME)) != 0) { - snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010B_FILENAME); - return err; - } - } else if (emu->card_capabilities->emu1010 == 3) { - if ((err = snd_emu1010_load_firmware(emu, EMU1010_NOTEBOOK_FILENAME)) != 0) { - snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010_NOTEBOOK_FILENAME); - return err; - } + switch (emu->card_capabilities->emu_model) { + case EMU_MODEL_EMU1010: + filename = HANA_FILENAME; + break; + case EMU_MODEL_EMU1010B: + filename = EMU1010B_FILENAME; + break; + case EMU_MODEL_EMU1616: + filename = EMU1010_NOTEBOOK_FILENAME; + break; + case EMU_MODEL_EMU0404: + filename = EMU0404_FILENAME; + break; + default: + filename = NULL; + return -ENODEV; + break; + } + snd_printk(KERN_INFO "emu1010: filename %s testing\n", filename); + err = snd_emu1010_load_firmware(emu, filename); + if (err != 0) { + snd_printk( + KERN_INFO "emu1010: Loading Firmware file %s failed\n", + filename); + return err; } /* ID, should read & 0x7f = 0x55 when FPGA programmed. */ @@ -1074,10 +1091,12 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* SPDIF Format spdif (or 0x11 for aes/ebu) */ /* Start Micro/Audio Dock firmware loader thread */ - emu->emu1010.firmware_thread = kthread_create(&emu1010_firmware_thread, - emu, - "emu1010_firmware"); - wake_up_process(emu->emu1010.firmware_thread); + if (!emu->emu1010.firmware_thread) { + emu->emu1010.firmware_thread = + kthread_create(emu1010_firmware_thread, emu, + "emu1010_firmware"); + wake_up_process(emu->emu1010.firmware_thread); + } #if 0 snd_emu1010_fpga_link_dst_src_write(emu, @@ -1090,79 +1109,114 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); /* ALICE2 bus 0xb3 */ #endif /* Default outputs */ - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ - emu->emu1010.output_source[0] = 21; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); - emu->emu1010.output_source[1] = 22; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2); - emu->emu1010.output_source[2] = 23; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); - emu->emu1010.output_source[3] = 24; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4); - emu->emu1010.output_source[4] = 25; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5); - emu->emu1010.output_source[5] = 26; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6); - emu->emu1010.output_source[6] = 27; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7); - emu->emu1010.output_source[7] = 28; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ - emu->emu1010.output_source[8] = 21; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); - emu->emu1010.output_source[9] = 22; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ - emu->emu1010.output_source[10] = 21; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); - emu->emu1010.output_source[11] = 22; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ - emu->emu1010.output_source[12] = 21; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); - emu->emu1010.output_source[13] = 22; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ - emu->emu1010.output_source[14] = 21; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); - emu->emu1010.output_source[15] = 22; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ - emu->emu1010.output_source[16] = 21; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1); - emu->emu1010.output_source[17] = 22; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2); - emu->emu1010.output_source[18] = 23; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3); - emu->emu1010.output_source[19] = 24; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4); - emu->emu1010.output_source[20] = 25; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5); - emu->emu1010.output_source[21] = 26; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6); - emu->emu1010.output_source[22] = 27; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7); - emu->emu1010.output_source[23] = 28; - + if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) { + /* 1616(M) cardbus default outputs */ + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[0] = 17; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[1] = 18; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2); + emu->emu1010.output_source[2] = 19; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); + emu->emu1010.output_source[3] = 20; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4); + emu->emu1010.output_source[4] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5); + emu->emu1010.output_source[5] = 22; + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_MANA_DAC_LEFT, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[16] = 17; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_MANA_DAC_RIGHT, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[17] = 18; + } else { + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[0] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[1] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2); + emu->emu1010.output_source[2] = 23; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); + emu->emu1010.output_source[3] = 24; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4); + emu->emu1010.output_source[4] = 25; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5); + emu->emu1010.output_source[5] = 26; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6); + emu->emu1010.output_source[6] = 27; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7); + emu->emu1010.output_source[7] = 28; + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[8] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[9] = 22; + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[10] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[11] = 22; + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[12] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[13] = 22; + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[14] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[15] = 22; + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[16] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[17] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2); + emu->emu1010.output_source[18] = 23; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3); + emu->emu1010.output_source[19] = 24; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4); + emu->emu1010.output_source[20] = 25; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5); + emu->emu1010.output_source[21] = 26; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6); + emu->emu1010.output_source[22] = 27; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7); + emu->emu1010.output_source[23] = 28; + } /* TEMP: Select SPDIF in/out */ //snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */ @@ -1202,11 +1256,12 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu) } snd_emu10k1_free_efx(emu); } - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) { /* Disable 48Volt power to Audio Dock */ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0 ); - kthread_stop(emu->emu1010.firmware_thread); } + if (emu->emu1010.firmware_thread) + kthread_stop(emu->emu1010.firmware_thread); if (emu->memhdr) snd_util_memhdr_free(emu->memhdr); if (emu->silent_page.area) @@ -1338,6 +1393,7 @@ static struct snd_emu_chip_details emu_chip_details[] = { .spi_dac = 1, .i2c_adc = 1, .spk71 = 1} , + /* Tested by James@superbug.co.uk 4th Nov 2007. */ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x42011102, .driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]", .id = "EMU1010", @@ -1345,28 +1401,46 @@ static struct snd_emu_chip_details emu_chip_details[] = { .ca0108_chip = 1, .ca_cardbus_chip = 1, .spk71 = 1 , - .emu1010 = 3} , + .emu_model = EMU_MODEL_EMU1616}, + /* Tested by James@superbug.co.uk 4th Nov 2007. */ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102, .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM????]", .id = "EMU1010", .emu10k2_chip = 1, .ca0108_chip = 1, - .spk71 = 1 , - .emu1010 = 2} , + .spk71 = 1, + .emu_model = EMU_MODEL_EMU1010B}, + /* Tested by James@superbug.co.uk 8th July 2005. */ + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102, + .driver = "Audigy2", .name = "E-mu 1010 [4001]", + .id = "EMU1010", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .spk71 = 1, + .emu_model = EMU_MODEL_EMU1010}, /* Emu 1010 */ + /* EMU0404b */ + {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40021102, + .driver = "Audigy2", .name = "E-mu 0404b [4002]", + .id = "EMU0404", + .emu10k2_chip = 1, + .ca0108_chip = 1, + .spk71 = 1, + .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */ + /* Tested by James@superbug.co.uk 20-3-2007. */ + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40021102, + .driver = "Audigy2", .name = "E-mu 0404 [4002]", + .id = "EMU0404", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .spk71 = 1, + .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */ + /* Audigy4 (Not PRO) SB0610 */ {.vendor = 0x1102, .device = 0x0008, .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", .id = "Audigy2", .emu10k2_chip = 1, .ca0108_chip = 1, .ac97_chip = 1} , - /* Tested by James@superbug.co.uk 8th July 2005. No sound available yet. */ - {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102, - .driver = "Audigy2", .name = "E-mu 1010 [4001]", - .id = "EMU1010", - .emu10k2_chip = 1, - .ca0102_chip = 1, - .spk71 = 1, - .emu1010 = 1} , /* Tested by James@superbug.co.uk 3rd July 2005 */ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102, .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]", @@ -1654,6 +1728,8 @@ int __devinit snd_emu10k1_create(struct snd_card *card, emu->card = card; spin_lock_init(&emu->reg_lock); spin_lock_init(&emu->emu_lock); + spin_lock_init(&emu->spi_lock); + spin_lock_init(&emu->i2c_lock); spin_lock_init(&emu->voice_lock); spin_lock_init(&emu->synth_lock); spin_lock_init(&emu->memblk_lock); @@ -1794,7 +1870,7 @@ int __devinit snd_emu10k1_create(struct snd_card *card, if (emu->card_capabilities->ecard) { if ((err = snd_emu10k1_ecard_init(emu)) < 0) goto error; - } else if (emu->card_capabilities->emu1010) { + } else if (emu->card_capabilities->emu_model) { if ((err = snd_emu10k1_emu1010_init(emu)) < 0) { snd_emu10k1_free(emu); return err; @@ -1943,7 +2019,7 @@ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu) snd_emu10k1_cardbus_init(emu); if (emu->card_capabilities->ecard) snd_emu10k1_ecard_init(emu); - else if (emu->card_capabilities->emu1010) + else if (emu->card_capabilities->emu_model) snd_emu10k1_emu1010_init(emu); else snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE); diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c index 204995a1dfb..ad7b71491fc 100644 --- a/sound/pci/emu10k1/emu10k1_synth.c +++ b/sound/pci/emu10k1/emu10k1_synth.c @@ -30,7 +30,7 @@ MODULE_LICENSE("GPL"); */ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev) { - struct snd_emux *emu; + struct snd_emux *emux; struct snd_emu10k1 *hw; struct snd_emu10k1_synth_arg *arg; unsigned long flags; @@ -46,53 +46,56 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev) else if (arg->max_voices > 64) arg->max_voices = 64; - if (snd_emux_new(&emu) < 0) + if (snd_emux_new(&emux) < 0) return -ENOMEM; - snd_emu10k1_ops_setup(emu); - emu->hw = hw = arg->hwptr; - emu->max_voices = arg->max_voices; - emu->num_ports = arg->seq_ports; - emu->pitch_shift = -501; - emu->memhdr = hw->memhdr; - emu->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2; /* maximum two ports */ - emu->midi_devidx = hw->audigy ? 2 : 1; /* audigy has two external midis */ - emu->linear_panning = 0; - emu->hwdep_idx = 2; /* FIXED */ - - if (snd_emux_register(emu, dev->card, arg->index, "Emu10k1") < 0) { - snd_emux_free(emu); + snd_emu10k1_ops_setup(emux); + hw = arg->hwptr; + emux->hw = hw; + emux->max_voices = arg->max_voices; + emux->num_ports = arg->seq_ports; + emux->pitch_shift = -501; + emux->memhdr = hw->memhdr; + /* maximum two ports */ + emux->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2; + /* audigy has two external midis */ + emux->midi_devidx = hw->audigy ? 2 : 1; + emux->linear_panning = 0; + emux->hwdep_idx = 2; /* FIXED */ + + if (snd_emux_register(emux, dev->card, arg->index, "Emu10k1") < 0) { + snd_emux_free(emux); return -ENOMEM; } spin_lock_irqsave(&hw->voice_lock, flags); - hw->synth = emu; + hw->synth = emux; hw->get_synth_voice = snd_emu10k1_synth_get_voice; spin_unlock_irqrestore(&hw->voice_lock, flags); - dev->driver_data = emu; + dev->driver_data = emux; return 0; } static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev) { - struct snd_emux *emu; + struct snd_emux *emux; struct snd_emu10k1 *hw; unsigned long flags; if (dev->driver_data == NULL) return 0; /* not registered actually */ - emu = dev->driver_data; + emux = dev->driver_data; - hw = emu->hw; + hw = emux->hw; spin_lock_irqsave(&hw->voice_lock, flags); hw->synth = NULL; hw->get_synth_voice = NULL; spin_unlock_irqrestore(&hw->voice_lock, flags); - snd_emux_free(emu); + snd_emux_free(emux); return 0; } diff --git a/sound/pci/emu10k1/emu10k1_synth_local.h b/sound/pci/emu10k1/emu10k1_synth_local.h index 308ddc84bb4..25f328ff639 100644 --- a/sound/pci/emu10k1/emu10k1_synth_local.h +++ b/sound/pci/emu10k1/emu10k1_synth_local.h @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/emu10k1_synth.h> diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 1ec7ebaff9e..5512abd98bd 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -29,7 +29,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> @@ -1583,6 +1582,8 @@ static int __devinit snd_emu10k1x_probe(struct pci_dev *pci, sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->port, chip->irq); + snd_card_set_dev(card, &pci->dev); + if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 9bf1cd59219..71dc4c8865b 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -28,7 +28,6 @@ * */ -#include <sound/driver.h> #include <linux/pci.h> #include <linux/capability.h> #include <linux/delay.h> @@ -666,7 +665,7 @@ static unsigned int *copy_tlv(const unsigned int __user *_tlv) return NULL; if (data[1] >= MAX_TLV_SIZE) return NULL; - tlv = kmalloc(data[1] * 4 + sizeof(data), GFP_KERNEL); + tlv = kmalloc(data[1] + sizeof(data), GFP_KERNEL); if (!tlv) return NULL; memcpy(tlv, data, sizeof(data)); @@ -1262,7 +1261,7 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) /* emu1212 DSP 0 and DSP 1 Capture */ - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { if (emu->card_capabilities->ca0108_chip) { /* Note:JCD:No longer bit shift lower 16bits to upper 16bits of 32bit value. */ A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x0), A_C_00000001); @@ -1516,7 +1515,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) /* digital outputs */ /* A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); */ - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { /* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */ snd_printk("EMU outputs on\n"); for (z = 0; z < 8; z++) { @@ -1564,7 +1563,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1); #endif - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { if (emu->card_capabilities->ca0108_chip) { snd_printk("EMU2 inputs on\n"); for (z = 0; z < 0x10; z++) { diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index ccacd7b890e..fd221209abc 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -30,7 +30,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/init.h> #include <sound/core.h> @@ -140,6 +139,61 @@ static char *emu1010_src_texts[] = { "DSP 31", }; +/* 1616(m) cardbus */ + +static char *emu1616_src_texts[] = { + "Silence", + "Dock Mic A", + "Dock Mic B", + "Dock ADC1 Left", + "Dock ADC1 Right", + "Dock ADC2 Left", + "Dock ADC2 Right", + "Dock SPDIF Left", + "Dock SPDIF Right", + "ADAT 0", + "ADAT 1", + "ADAT 2", + "ADAT 3", + "ADAT 4", + "ADAT 5", + "ADAT 6", + "ADAT 7", + "DSP 0", + "DSP 1", + "DSP 2", + "DSP 3", + "DSP 4", + "DSP 5", + "DSP 6", + "DSP 7", + "DSP 8", + "DSP 9", + "DSP 10", + "DSP 11", + "DSP 12", + "DSP 13", + "DSP 14", + "DSP 15", + "DSP 16", + "DSP 17", + "DSP 18", + "DSP 19", + "DSP 20", + "DSP 21", + "DSP 22", + "DSP 23", + "DSP 24", + "DSP 25", + "DSP 26", + "DSP 27", + "DSP 28", + "DSP 29", + "DSP 30", + "DSP 31", +}; + + /* * List of data sources available for each destination */ @@ -199,6 +253,59 @@ static unsigned int emu1010_src_regs[] = { EMU_SRC_ALICE_EMU32B+0xf, /* 52 */ }; +/* 1616(m) cardbus */ +static unsigned int emu1616_src_regs[] = { + EMU_SRC_SILENCE, + EMU_SRC_DOCK_MIC_A1, + EMU_SRC_DOCK_MIC_B1, + EMU_SRC_DOCK_ADC1_LEFT1, + EMU_SRC_DOCK_ADC1_RIGHT1, + EMU_SRC_DOCK_ADC2_LEFT1, + EMU_SRC_DOCK_ADC2_RIGHT1, + EMU_SRC_MDOCK_SPDIF_LEFT1, + EMU_SRC_MDOCK_SPDIF_RIGHT1, + EMU_SRC_MDOCK_ADAT, + EMU_SRC_MDOCK_ADAT+1, + EMU_SRC_MDOCK_ADAT+2, + EMU_SRC_MDOCK_ADAT+3, + EMU_SRC_MDOCK_ADAT+4, + EMU_SRC_MDOCK_ADAT+5, + EMU_SRC_MDOCK_ADAT+6, + EMU_SRC_MDOCK_ADAT+7, + EMU_SRC_ALICE_EMU32A, + EMU_SRC_ALICE_EMU32A+1, + EMU_SRC_ALICE_EMU32A+2, + EMU_SRC_ALICE_EMU32A+3, + EMU_SRC_ALICE_EMU32A+4, + EMU_SRC_ALICE_EMU32A+5, + EMU_SRC_ALICE_EMU32A+6, + EMU_SRC_ALICE_EMU32A+7, + EMU_SRC_ALICE_EMU32A+8, + EMU_SRC_ALICE_EMU32A+9, + EMU_SRC_ALICE_EMU32A+0xa, + EMU_SRC_ALICE_EMU32A+0xb, + EMU_SRC_ALICE_EMU32A+0xc, + EMU_SRC_ALICE_EMU32A+0xd, + EMU_SRC_ALICE_EMU32A+0xe, + EMU_SRC_ALICE_EMU32A+0xf, + EMU_SRC_ALICE_EMU32B, + EMU_SRC_ALICE_EMU32B+1, + EMU_SRC_ALICE_EMU32B+2, + EMU_SRC_ALICE_EMU32B+3, + EMU_SRC_ALICE_EMU32B+4, + EMU_SRC_ALICE_EMU32B+5, + EMU_SRC_ALICE_EMU32B+6, + EMU_SRC_ALICE_EMU32B+7, + EMU_SRC_ALICE_EMU32B+8, + EMU_SRC_ALICE_EMU32B+9, + EMU_SRC_ALICE_EMU32B+0xa, + EMU_SRC_ALICE_EMU32B+0xb, + EMU_SRC_ALICE_EMU32B+0xc, + EMU_SRC_ALICE_EMU32B+0xd, + EMU_SRC_ALICE_EMU32B+0xe, + EMU_SRC_ALICE_EMU32B+0xf, +}; + /* * Data destinations - physical EMU outputs. * Each destination has an enum mixer control to choose a data source @@ -230,6 +337,28 @@ static unsigned int emu1010_output_dst[] = { EMU_DST_HANA_ADAT+7, /* 23 */ }; +/* 1616(m) cardbus */ +static unsigned int emu1616_output_dst[] = { + EMU_DST_DOCK_DAC1_LEFT1, + EMU_DST_DOCK_DAC1_RIGHT1, + EMU_DST_DOCK_DAC2_LEFT1, + EMU_DST_DOCK_DAC2_RIGHT1, + EMU_DST_DOCK_DAC3_LEFT1, + EMU_DST_DOCK_DAC3_RIGHT1, + EMU_DST_MDOCK_SPDIF_LEFT1, + EMU_DST_MDOCK_SPDIF_RIGHT1, + EMU_DST_MDOCK_ADAT, + EMU_DST_MDOCK_ADAT+1, + EMU_DST_MDOCK_ADAT+2, + EMU_DST_MDOCK_ADAT+3, + EMU_DST_MDOCK_ADAT+4, + EMU_DST_MDOCK_ADAT+5, + EMU_DST_MDOCK_ADAT+6, + EMU_DST_MDOCK_ADAT+7, + EMU_DST_MANA_DAC_LEFT, + EMU_DST_MANA_DAC_RIGHT, +}; + /* * Data destinations - HANA outputs going to Alice2 (audigy) for * capture (EMU32 + I2S links) @@ -260,14 +389,26 @@ static unsigned int emu1010_input_dst[] = { EMU_DST_ALICE_I2S2_RIGHT, }; -static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + char **items; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 53; + if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) { + uinfo->value.enumerated.items = 49; + items = emu1616_src_texts; + } else { + uinfo->value.enumerated.items = 53; + items = emu1010_src_texts; + } if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, emu1010_src_texts[uinfo->value.enumerated.item]); + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + items[uinfo->value.enumerated.item]); return 0; } @@ -279,7 +420,9 @@ static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol, channel = (kcontrol->private_value) & 0xff; /* Limit: emu1010_output_dst, emu->emu1010.output_source */ - if (channel >= 24) + if (channel >= 24 || + (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 && + channel >= 18)) return -EINVAL; ucontrol->value.enumerated.item[0] = emu->emu1010.output_source[channel]; return 0; @@ -289,24 +432,30 @@ static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); - int change = 0; unsigned int val; unsigned int channel; val = ucontrol->value.enumerated.item[0]; - if (val >= 53) + if (val >= 53 || + (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 && + val >= 49)) return -EINVAL; channel = (kcontrol->private_value) & 0xff; /* Limit: emu1010_output_dst, emu->emu1010.output_source */ - if (channel >= 24) + if (channel >= 24 || + (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 && + channel >= 18)) return -EINVAL; - if (emu->emu1010.output_source[channel] != val) { - emu->emu1010.output_source[channel] = val; - change = 1; + if (emu->emu1010.output_source[channel] == val) + return 0; + emu->emu1010.output_source[channel] = val; + if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) + snd_emu1010_fpga_link_dst_src_write(emu, + emu1616_output_dst[channel], emu1616_src_regs[val]); + else snd_emu1010_fpga_link_dst_src_write(emu, emu1010_output_dst[channel], emu1010_src_regs[val]); - } - return change; + return 1; } static int snd_emu1010_input_source_get(struct snd_kcontrol *kcontrol, @@ -327,24 +476,28 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); - int change = 0; unsigned int val; unsigned int channel; val = ucontrol->value.enumerated.item[0]; - if (val >= 53) + if (val >= 53 || + (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 && + val >= 49)) return -EINVAL; channel = (kcontrol->private_value) & 0xff; /* Limit: emu1010_input_dst, emu->emu1010.input_source */ if (channel >= 22) return -EINVAL; - if (emu->emu1010.input_source[channel] != val) { - emu->emu1010.input_source[channel] = val; - change = 1; + if (emu->emu1010.input_source[channel] == val) + return 0; + emu->emu1010.input_source[channel] = val; + if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) + snd_emu1010_fpga_link_dst_src_write(emu, + emu1010_input_dst[channel], emu1616_src_regs[val]); + else snd_emu1010_fpga_link_dst_src_write(emu, emu1010_input_dst[channel], emu1010_src_regs[val]); - } - return change; + return 1; } #define EMU1010_SOURCE_OUTPUT(xname,chid) \ @@ -384,6 +537,30 @@ static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = { EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Enum", 0x17), }; + +/* 1616(m) cardbus */ +static struct snd_kcontrol_new snd_emu1616_output_enum_ctls[] __devinitdata = { + EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0), + EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1), + EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2), + EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Enum", 3), + EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Enum", 4), + EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Enum", 5), + EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Enum", 6), + EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Enum", 7), + EMU1010_SOURCE_OUTPUT("Dock ADAT 0 Playback Enum", 8), + EMU1010_SOURCE_OUTPUT("Dock ADAT 1 Playback Enum", 9), + EMU1010_SOURCE_OUTPUT("Dock ADAT 2 Playback Enum", 0xa), + EMU1010_SOURCE_OUTPUT("Dock ADAT 3 Playback Enum", 0xb), + EMU1010_SOURCE_OUTPUT("Dock ADAT 4 Playback Enum", 0xc), + EMU1010_SOURCE_OUTPUT("Dock ADAT 5 Playback Enum", 0xd), + EMU1010_SOURCE_OUTPUT("Dock ADAT 6 Playback Enum", 0xe), + EMU1010_SOURCE_OUTPUT("Dock ADAT 7 Playback Enum", 0xf), + EMU1010_SOURCE_OUTPUT("Mana DAC Left Playback Enum", 0x10), + EMU1010_SOURCE_OUTPUT("Mana DAC Right Playback Enum", 0x11), +}; + + #define EMU1010_SOURCE_INPUT(xname,chid) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -1793,7 +1970,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, return err; } - if ( emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { ; /* Disable the snd_audigy_spdif_shared_spdif */ } else if (emu->audigy) { if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL) @@ -1818,30 +1995,73 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, return err; } - if ( emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) { + /* 1616(m) cardbus */ + int i; + + for (i = 0; i < ARRAY_SIZE(snd_emu1616_output_enum_ctls); i++) { + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1616_output_enum_ctls[i], + emu)); + if (err < 0) + return err; + } + for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) { + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_input_enum_ctls[i], + emu)); + if (err < 0) + return err; + } + for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads) - 2; i++) { + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_adc_pads[i], emu)); + if (err < 0) + return err; + } + for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads) - 2; i++) { + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_dac_pads[i], emu)); + if (err < 0) + return err; + } + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_internal_clock, emu)); + if (err < 0) + return err; + + } else if (emu->card_capabilities->emu_model) { + /* all other e-mu cards for now */ int i; for (i = 0; i < ARRAY_SIZE(snd_emu1010_output_enum_ctls); i++) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_output_enum_ctls[i], emu)); + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_output_enum_ctls[i], + emu)); if (err < 0) return err; } for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_input_enum_ctls[i], emu)); + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_input_enum_ctls[i], + emu)); if (err < 0) return err; } for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads); i++) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_adc_pads[i], emu)); + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_adc_pads[i], emu)); if (err < 0) return err; } for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads); i++) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_dac_pads[i], emu)); + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_dac_pads[i], emu)); if (err < 0) return err; } - err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_internal_clock, emu)); + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_internal_clock, emu)); if (err < 0) return err; } diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c index 04c7cf70353..c4d76d16661 100644 --- a/sound/pci/emu10k1/emumpu401.c +++ b/sound/pci/emu10k1/emumpu401.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/init.h> #include <sound/core.h> diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 5ce5befc701..cf9276ddad4 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -26,7 +26,6 @@ * */ -#include <sound/driver.h> #include <linux/pci.h> #include <linux/delay.h> #include <linux/slab.h> @@ -358,7 +357,7 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu, snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]); snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24)); snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24)); - if (emu->card_capabilities->emu1010) + if (emu->card_capabilities->emu_model) pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */ else pitch_target = emu10k1_calc_pitch_target(runtime->rate); @@ -701,7 +700,7 @@ static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, struct s voice = evoice->number; pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8; - if (emu->card_capabilities->emu1010) + if (emu->card_capabilities->emu_model) pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */ else pitch_target = emu10k1_calc_pitch_target(runtime->rate); @@ -1232,7 +1231,7 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream) runtime->hw.rates = SNDRV_PCM_RATE_48000; runtime->hw.rate_min = runtime->hw.rate_max = 48000; spin_lock_irq(&emu->reg_lock); - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { /* Nb. of channels has been increased to 16 */ /* TODO * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE @@ -1791,7 +1790,7 @@ int __devinit snd_emu10k1_pcm_efx(struct snd_emu10k1 * emu, int device, struct s /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */ if (emu->audigy) { emu->efx_voices_mask[0] = 0; - if (emu->card_capabilities->emu1010) + if (emu->card_capabilities->emu_model) /* Pavel Hofman - 32 voices will be used for * capture (write mode) - * each bit = corresponding voice diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index c3fb10e81c9..f3caa3f890c 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -28,7 +28,6 @@ * */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/init.h> #include <sound/core.h> @@ -245,7 +244,7 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry, unsigned long flags; u32 rate; - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { spin_lock_irqsave(&emu->emu_lock, flags); snd_emu1010_fpga_read(emu, 0x38, &value); spin_unlock_irqrestore(&emu->emu_lock, flags); @@ -585,7 +584,7 @@ int __devinit snd_emu10k1_proc_init(struct snd_emu10k1 * emu) { struct snd_info_entry *entry; #ifdef CONFIG_SND_DEBUG - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { if (! snd_card_proc_new(emu->card, "emu1010_regs", &entry)) snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read); } diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index 6702c15fefa..b5a802bdeb7 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -25,7 +25,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/emu10k1.h> @@ -71,6 +70,11 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i unsigned long flags; unsigned int mask; + if (!emu) { + snd_printk(KERN_ERR "ptr_write: emu is null!\n"); + dump_stack(); + return; + } mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK; regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK); @@ -135,15 +139,23 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, unsigned int reset, set; unsigned int reg, tmp; int n, result; + int err = 0; + + /* This function is not re-entrant, so protect against it. */ + spin_lock(&emu->spi_lock); if (emu->card_capabilities->ca0108_chip) reg = 0x3c; /* PTR20, reg 0x3c */ else { /* For other chip types the SPI register * is currently unknown. */ - return 1; + err = 1; + goto spi_write_exit; + } + if (data > 0xffff) { + /* Only 16bit values allowed */ + err = 1; + goto spi_write_exit; } - if (data > 0xffff) /* Only 16bit values allowed */ - return 1; tmp = snd_emu10k1_ptr20_read(emu, reg, 0); reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */ @@ -161,11 +173,17 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, break; } } - if (result) /* Timed out */ - return 1; + if (result) { + /* Timed out */ + err = 1; + goto spi_write_exit; + } snd_emu10k1_ptr20_write(emu, reg, 0, reset | data); tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */ - return 0; + err = 0; +spi_write_exit: + spin_unlock(&emu->spi_lock); + return err; } /* The ADC does not support i2c read, so only write is implemented */ @@ -177,15 +195,17 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, int timeout = 0; int status; int retry; + int err = 0; + if ((reg > 0x7f) || (value > 0x1ff)) { snd_printk(KERN_ERR "i2c_write: invalid values.\n"); return -EINVAL; } + /* This function is not re-entrant, so protect against it. */ + spin_lock(&emu->i2c_lock); + tmp = reg << 25 | value << 16; - // snd_printk("I2C-write:reg=0x%x, value=0x%x\n", reg, value); - /* Not sure what this I2C channel controls. */ - /* snd_emu10k1_ptr_write(emu, P17V_I2C_0, 0, tmp); */ /* This controls the I2C connected to the WM8775 ADC Codec */ snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp); @@ -193,17 +213,14 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, for (retry = 0; retry < 10; retry++) { /* Send the data to i2c */ - //tmp = snd_emu10k1_ptr_read(emu, P17V_I2C_ADDR, 0); - //tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK); tmp = 0; tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD); snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp); /* Wait till the transaction ends */ while (1) { - udelay(10); + mdelay(1); status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0); - // snd_printk("I2C:status=0x%x\n", status); timeout++; if ((status & I2C_A_ADC_START) == 0) break; @@ -220,19 +237,26 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, if (retry == 10) { snd_printk(KERN_ERR "Writing to ADC failed!\n"); - return -EINVAL; + snd_printk(KERN_ERR "status=0x%x, reg=%d, value=%d\n", + status, reg, value); + /* dump_stack(); */ + err = -EINVAL; } - return 0; + spin_unlock(&emu->i2c_lock); + return err; } int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value) { + unsigned long flags; + if (reg > 0x3f) return 1; reg += 0x40; /* 0x40 upwards are registers. */ if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */ return 1; + spin_lock_irqsave(&emu->emu_lock, flags); outl(reg, emu->port + A_IOCFG); udelay(10); outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ @@ -240,20 +264,24 @@ int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value) outl(value, emu->port + A_IOCFG); udelay(10); outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ + spin_unlock_irqrestore(&emu->emu_lock, flags); return 0; } int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value) { + unsigned long flags; if (reg > 0x3f) return 1; reg += 0x40; /* 0x40 upwards are registers. */ + spin_lock_irqsave(&emu->emu_lock, flags); outl(reg, emu->port + A_IOCFG); udelay(10); outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ udelay(10); *value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f); + spin_unlock_irqrestore(&emu->emu_lock, flags); return 0; } diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c index 3c114b45e0b..30bfed6f833 100644 --- a/sound/pci/emu10k1/irq.c +++ b/sound/pci/emu10k1/irq.c @@ -25,7 +25,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/emu10k1.h> @@ -35,9 +34,10 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id) struct snd_emu10k1 *emu = dev_id; unsigned int status, status2, orig_status, orig_status2; int handled = 0; + int timeout = 0; - while ((status = inl(emu->port + IPR)) != 0) { - //snd_printk(KERN_INFO "emu10k1 irq - status = 0x%x\n", status); + while (((status = inl(emu->port + IPR)) != 0) && (timeout < 1000)) { + timeout++; orig_status = status; handled = 1; if ((status & 0xffffffff) == 0xffffffff) { @@ -201,5 +201,8 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id) } outl(orig_status, emu->port + IPR); /* ack all */ } + if (timeout == 1000) + snd_printk(KERN_INFO "emu10k1 irq routine failure\n"); + return IRQ_RETVAL(handled); } diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index 48097c6bb15..916c1dbcd53 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/pci.h> #include <linux/time.h> #include <linux/mutex.h> diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index 9fd3135f311..749a21b6bd0 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -87,7 +87,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c index 6295b2dca78..72321e946cc 100644 --- a/sound/pci/emu10k1/timer.c +++ b/sound/pci/emu10k1/timer.c @@ -25,7 +25,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/emu10k1.h> diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c index 04fa8492abb..958cb2a65a4 100644 --- a/sound/pci/emu10k1/voice.c +++ b/sound/pci/emu10k1/voice.c @@ -28,7 +28,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/emu10k1.h> diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index b958f869cb1..72d85a5ae6a 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -26,7 +26,6 @@ * by Kurt J. Bosch */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index fb25abe68a0..1a314fa99c4 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -47,7 +47,6 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> @@ -227,6 +226,7 @@ struct es1938 { unsigned int dma2_start; unsigned int dma1_shift; unsigned int dma2_shift; + unsigned int last_capture_dmaaddr; unsigned int active; spinlock_t reg_lock; @@ -529,6 +529,7 @@ static void snd_es1938_capture_setdma(struct es1938 *chip) outb(1, SLDM_REG(chip, DMAMASK)); outb(0x14, SLDM_REG(chip, DMAMODE)); outl(chip->dma1_start, SLDM_REG(chip, DMAADDR)); + chip->last_capture_dmaaddr = chip->dma1_start; outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT)); /* 3. Unmask DMA */ outb(0, SLDM_REG(chip, DMAMASK)); @@ -770,19 +771,40 @@ static int snd_es1938_playback_prepare(struct snd_pcm_substream *substream) return -EINVAL; } +/* during the incrementing of dma counters the DMA register reads sometimes + returns garbage. To ensure a valid hw pointer, the following checks which + should be very unlikely to fail are used: + - is the current DMA address in the valid DMA range ? + - is the sum of DMA address and DMA counter pointing to the last DMA byte ? + One can argue this could differ by one byte depending on which register is + updated first, so the implementation below allows for that. +*/ static snd_pcm_uframes_t snd_es1938_capture_pointer(struct snd_pcm_substream *substream) { struct es1938 *chip = snd_pcm_substream_chip(substream); size_t ptr; +#if 0 size_t old, new; -#if 1 /* This stuff is *needed*, don't ask why - AB */ old = inw(SLDM_REG(chip, DMACOUNT)); while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old) old = new; ptr = chip->dma1_size - 1 - new; #else - ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start; + size_t count; + unsigned int diff; + + ptr = inl(SLDM_REG(chip, DMAADDR)); + count = inw(SLDM_REG(chip, DMACOUNT)); + diff = chip->dma1_start + chip->dma1_size - ptr - count; + + if (diff > 3 || ptr < chip->dma1_start + || ptr >= chip->dma1_start+chip->dma1_size) + ptr = chip->last_capture_dmaaddr; /* bad, use last saved */ + else + chip->last_capture_dmaaddr = ptr; /* good, remember it */ + + ptr -= chip->dma1_start; #endif return ptr >> chip->dma1_shift; } diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index d69b11d1f99..25ccfce4575 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -94,7 +94,6 @@ * places. */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 9939109f05a..4c300e6149f 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -979,6 +978,27 @@ static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea) return val; } +static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea, + unsigned int mute) +{ + struct fm801 *chip = tea->private_data; + unsigned short reg; + + spin_lock_irq(&chip->reg_lock); + + reg = inw(FM801_REG(chip, GPIO_CTRL)); + if (mute) + /* 0xf800 (mute) */ + reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE); + else + /* 0xf802 (unmute) */ + reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE); + outw(reg, FM801_REG(chip, GPIO_CTRL)); + udelay(1); + + spin_unlock_irq(&chip->reg_lock); +} + static struct snd_tea575x_ops snd_fm801_tea_ops[3] = { { /* 1 = MediaForte 256-PCS */ @@ -994,6 +1014,7 @@ static struct snd_tea575x_ops snd_fm801_tea_ops[3] = { /* 3 = MediaForte 64-PCR */ .write = snd_fm801_tea575x_64pcr_write, .read = snd_fm801_tea575x_64pcr_read, + .mute = snd_fm801_tea575x_64pcr_mute, } }; #endif diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index ab0c726d648..9e0d8a1268a 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -2,7 +2,7 @@ snd-hda-intel-y := hda_intel.o # since snd-hda-intel is the only driver using hda-codec, # merge it into a single module although it was originally # designed to be individual modules -snd-hda-intel-y += hda_codec.o +snd-hda-intel-y += hda_codec.o vmaster.o snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 8cbe3bf1e31..26812dc2b7f 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -55,6 +54,7 @@ static struct hda_vendor_id hda_vendor_ids[] = { { 0x10ec, "Realtek" }, { 0x1057, "Motorola" }, { 0x1106, "VIA" }, + { 0x111d, "IDT" }, { 0x11d4, "Analog Devices" }, { 0x13f6, "C-Media" }, { 0x14f1, "Conexant" }, @@ -429,6 +429,10 @@ find_codec_preset(struct hda_codec *codec) for (tbl = hda_preset_tables; *tbl; tbl++) { for (preset = *tbl; preset->id; preset++) { u32 mask = preset->mask; + if (preset->afg && preset->afg != codec->afg) + continue; + if (preset->mfg && preset->mfg != codec->mfg) + continue; if (!mask) mask = ~0; if (preset->id == (codec->vendor_id & mask) && @@ -765,7 +769,7 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key) /* * query AMP capabilities for the given widget and direction */ -static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) +u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) { struct hda_amp_info *info; @@ -933,7 +937,8 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; if (!caps) { printk(KERN_WARNING "hda_codec: " - "num_steps = 0 for NID=0x%x\n", nid); + "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid, + kcontrol->id.name); return -EINVAL; } uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; @@ -1012,6 +1017,66 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, return 0; } +/* + * set (static) TLV for virtual master volume; recalculated as max 0dB + */ +void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int *tlv) +{ + u32 caps; + int nums, step; + + caps = query_amp_caps(codec, nid, dir); + nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; + step = (step + 1) * 25; + tlv[0] = SNDRV_CTL_TLVT_DB_SCALE; + tlv[1] = 2 * sizeof(unsigned int); + tlv[2] = -nums * step; + tlv[3] = step; +} + +/* find a mixer control element with the given name */ +struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, + const char *name) +{ + struct snd_ctl_elem_id id; + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, name); + return snd_ctl_find_id(codec->bus->card, &id); +} + +/* create a virtual master control and add slaves */ +int snd_hda_add_vmaster(struct hda_codec *codec, char *name, + unsigned int *tlv, const char **slaves) +{ + struct snd_kcontrol *kctl; + const char **s; + int err; + + kctl = snd_ctl_make_virtual_master(name, tlv); + if (!kctl) + return -ENOMEM; + err = snd_ctl_add(codec->bus->card, kctl); + if (err < 0) + return err; + + for (s = slaves; *s; s++) { + struct snd_kcontrol *sctl; + + sctl = snd_hda_find_mixer_ctl(codec, *s); + if (!sctl) { + snd_printdd("Cannot find slave %s, skipped\n", *s); + continue; + } + err = snd_ctl_add_slave(kctl, sctl); + if (err < 0) + return err; + } + return 0; +} + /* switch */ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -1434,7 +1499,8 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) return err; } codec->spdif_ctls = - snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0); + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT_1, 0); codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls); return 0; } @@ -1481,7 +1547,7 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol, unsigned short val; unsigned int sbits; - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0); + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0); sbits = convert_to_spdif_status(val); ucontrol->value.iec958.status[0] = sbits; ucontrol->value.iec958.status[1] = sbits >> 8; @@ -1532,7 +1598,8 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) return err; } codec->spdif_in_enable = - snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0) & + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT_1, 0) & AC_DIG1_ENABLE; return 0; } @@ -1622,6 +1689,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); + msleep(10); /* partial workaround for "azx_get_response timeout" */ nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) { @@ -2336,7 +2404,8 @@ int snd_hda_ch_mode_put(struct hda_codec *codec, unsigned int mode; mode = ucontrol->value.enumerated.item[0]; - snd_assert(mode < num_chmodes, return -EINVAL); + if (mode >= num_chmodes) + return -EINVAL; if (*max_channelsp == chmode[mode].channels) return 0; /* change the current channel setting */ @@ -2602,20 +2671,21 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg, hda_nid_t *ignore_nids) { - hda_nid_t nid, nid_start; - int nodes; + hda_nid_t nid, end_nid; short seq, assoc_line_out, assoc_speaker; short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)]; short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)]; + short sequences_hp[ARRAY_SIZE(cfg->hp_pins)]; memset(cfg, 0, sizeof(*cfg)); memset(sequences_line_out, 0, sizeof(sequences_line_out)); memset(sequences_speaker, 0, sizeof(sequences_speaker)); + memset(sequences_hp, 0, sizeof(sequences_hp)); assoc_line_out = assoc_speaker = 0; - nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start); - for (nid = nid_start; nid < nodes + nid_start; nid++) { + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { unsigned int wid_caps = get_wcaps(codec, nid); unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; @@ -2638,6 +2708,10 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, case AC_JACK_LINE_OUT: seq = get_defcfg_sequence(def_conf); assoc = get_defcfg_association(def_conf); + + if (!(wid_caps & AC_WCAP_STEREO)) + if (!cfg->mono_out_pin) + cfg->mono_out_pin = nid; if (!assoc) continue; if (!assoc_line_out) @@ -2666,9 +2740,12 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, cfg->speaker_outs++; break; case AC_JACK_HP_OUT: + seq = get_defcfg_sequence(def_conf); + assoc = get_defcfg_association(def_conf); if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins)) continue; cfg->hp_pins[cfg->hp_outs] = nid; + sequences_hp[cfg->hp_outs] = (assoc << 4) | seq; cfg->hp_outs++; break; case AC_JACK_MIC_IN: { @@ -2712,7 +2789,24 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, cfg->line_outs); sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker, cfg->speaker_outs); + sort_pins_by_sequence(cfg->hp_pins, sequences_hp, + cfg->hp_outs); + /* if we have only one mic, make it AUTO_PIN_MIC */ + if (!cfg->input_pins[AUTO_PIN_MIC] && + cfg->input_pins[AUTO_PIN_FRONT_MIC]) { + cfg->input_pins[AUTO_PIN_MIC] = + cfg->input_pins[AUTO_PIN_FRONT_MIC]; + cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0; + } + /* ditto for line-in */ + if (!cfg->input_pins[AUTO_PIN_LINE] && + cfg->input_pins[AUTO_PIN_FRONT_LINE]) { + cfg->input_pins[AUTO_PIN_LINE] = + cfg->input_pins[AUTO_PIN_FRONT_LINE]; + cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0; + } + /* * FIX-UP: if no line-outs are detected, try to use speaker or HP pin * as a primary output @@ -2766,6 +2860,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, cfg->hp_outs, cfg->hp_pins[0], cfg->hp_pins[1], cfg->hp_pins[2], cfg->hp_pins[3], cfg->hp_pins[4]); + snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin); snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x," " cd=0x%x, aux=0x%x\n", cfg->input_pins[AUTO_PIN_MIC], diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 2bce925d84e..f14871151be 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -77,12 +77,16 @@ enum { #define AC_VERB_GET_PIN_SENSE 0x0f09 #define AC_VERB_GET_BEEP_CONTROL 0x0f0a #define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c -#define AC_VERB_GET_DIGI_CONVERT 0x0f0d +#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d +#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e #define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f /* f10-f1a: GPIO */ #define AC_VERB_GET_GPIO_DATA 0x0f15 #define AC_VERB_GET_GPIO_MASK 0x0f16 #define AC_VERB_GET_GPIO_DIRECTION 0x0f17 +#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18 +#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19 +#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a #define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c /* f20: AFG/MFG */ #define AC_VERB_GET_SUBSYSTEM_ID 0x0f20 @@ -110,6 +114,9 @@ enum { #define AC_VERB_SET_GPIO_DATA 0x715 #define AC_VERB_SET_GPIO_MASK 0x716 #define AC_VERB_SET_GPIO_DIRECTION 0x717 +#define AC_VERB_SET_GPIO_WAKE_MASK 0x718 +#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719 +#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e @@ -135,6 +142,7 @@ enum { #define AC_PAR_PROC_CAP 0x10 #define AC_PAR_GPIO_CAP 0x11 #define AC_PAR_AMP_OUT_CAP 0x12 +#define AC_PAR_VOL_KNB_CAP 0x13 /* * AC_VERB_PARAMETERS results (32bit) @@ -181,6 +189,27 @@ enum { #define AC_SUPFMT_FLOAT32 (1<<1) #define AC_SUPFMT_AC3 (1<<2) +/* GP I/O count */ +#define AC_GPIO_IO_COUNT (0xff<<0) +#define AC_GPIO_O_COUNT (0xff<<8) +#define AC_GPIO_O_COUNT_SHIFT 8 +#define AC_GPIO_I_COUNT (0xff<<16) +#define AC_GPIO_I_COUNT_SHIFT 16 +#define AC_GPIO_UNSOLICITED (1<<30) +#define AC_GPIO_WAKE (1<<31) + +/* Converter stream, channel */ +#define AC_CONV_CHANNEL (0xf<<0) +#define AC_CONV_STREAM (0xf<<4) +#define AC_CONV_STREAM_SHIFT 4 + +/* Input converter SDI select */ +#define AC_SDI_SELECT (0xf<<0) + +/* Unsolicited response */ +#define AC_UNSOL_TAG (0x3f<<0) +#define AC_UNSOL_ENABLED (1<<7) + /* Pin widget capabilies */ #define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */ #define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */ @@ -189,6 +218,10 @@ enum { #define AC_PINCAP_OUT (1<<4) /* output capable */ #define AC_PINCAP_IN (1<<5) /* input capable */ #define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */ +/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification, + * but is marked reserved in the Intel HDA specification. + */ +#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */ #define AC_PINCAP_VREF (0x37<<8) #define AC_PINCAP_VREF_SHIFT 8 #define AC_PINCAP_EAPD (1<<16) /* EAPD capable */ @@ -222,6 +255,9 @@ enum { #define AC_PWRST_D3SUP (1<<3) /* Power state values */ +#define AC_PWRST_SETTING (0xf<<0) +#define AC_PWRST_ACTUAL (0xf<<4) +#define AC_PWRST_ACTUAL_SHIFT 4 #define AC_PWRST_D0 0x00 #define AC_PWRST_D1 0x01 #define AC_PWRST_D2 0x02 @@ -230,10 +266,11 @@ enum { /* Processing capabilies */ #define AC_PCAP_BENIGN (1<<0) #define AC_PCAP_NUM_COEF (0xff<<8) +#define AC_PCAP_NUM_COEF_SHIFT 8 /* Volume knobs capabilities */ #define AC_KNBCAP_NUM_STEPS (0x7f<<0) -#define AC_KNBCAP_DELTA (1<<8) +#define AC_KNBCAP_DELTA (1<<7) /* * Control Parameters @@ -266,6 +303,9 @@ enum { #define AC_DIG1_PROFESSIONAL (1<<6) #define AC_DIG1_LEVEL (1<<7) +/* DIGITAL2 bits */ +#define AC_DIG2_CC (0x7f<<0) + /* Pin widget control - 8bit */ #define AC_PINCTL_VREFEN (0x7<<0) #define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */ @@ -280,12 +320,22 @@ enum { /* Unsolicited response - 8bit */ #define AC_USRSP_EN (1<<7) +/* Pin sense - 32bit */ +#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff) +#define AC_PINSENSE_PRESENCE (1<<31) + +/* EAPD/BTL enable - 32bit */ +#define AC_EAPDBTL_BALANCED (1<<0) +#define AC_EAPDBTL_EAPD (1<<1) +#define AC_EAPDBTL_LR_SWAP (1<<2) + /* configuration default - 32bit */ #define AC_DEFCFG_SEQUENCE (0xf<<0) #define AC_DEFCFG_DEF_ASSOC (0xf<<4) #define AC_DEFCFG_ASSOC_SHIFT 4 #define AC_DEFCFG_MISC (0xf<<8) #define AC_DEFCFG_MISC_SHIFT 8 +#define AC_DEFCFG_MISC_NO_PRESENCE (1<<0) #define AC_DEFCFG_COLOR (0xf<<12) #define AC_DEFCFG_COLOR_SHIFT 12 #define AC_DEFCFG_CONN_TYPE (0xf<<16) @@ -417,7 +467,7 @@ struct hda_bus_ops { /* free the private data */ void (*private_free)(struct hda_bus *); #ifdef CONFIG_SND_HDA_POWER_SAVE - /* notify power-up/down from codec to contoller */ + /* notify power-up/down from codec to controller */ void (*pm_notify)(struct hda_codec *codec); #endif }; @@ -456,6 +506,9 @@ struct hda_bus { struct hda_bus_unsolicited *unsol; struct snd_info_entry *proc; + + /* misc op flags */ + unsigned int needs_damn_long_delay :1; }; /* @@ -470,6 +523,7 @@ struct hda_codec_preset { unsigned int subs; unsigned int subs_mask; unsigned int rev; + hda_nid_t afg, mfg; const char *name; int (*patch)(struct hda_codec *codec); }; diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c957eb58de5..f9de7c467c2 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <sound/core.h> diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index bafb7b01f5a..2177d9af533 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/pci.h> diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 3fa0f970490..56f8a305075 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -34,7 +34,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -50,29 +49,32 @@ #include "hda_codec.h" -static int index = SNDRV_DEFAULT_IDX1; -static char *id = SNDRV_DEFAULT_STR1; -static char *model; -static int position_fix; -static int probe_mask = -1; +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static char *model[SNDRV_CARDS]; +static int position_fix[SNDRV_CARDS]; +static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int single_cmd; static int enable_msi; -module_param(index, int, 0444); +module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); -module_param(id, charp, 0444); +module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string for Intel HD audio interface."); -module_param(model, charp, 0444); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable Intel HD audio interface."); +module_param_array(model, charp, NULL, 0444); MODULE_PARM_DESC(model, "Use the given board model."); -module_param(position_fix, int, 0444); +module_param_array(position_fix, int, NULL, 0444); MODULE_PARM_DESC(position_fix, "Fix DMA pointer " "(0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)."); -module_param(probe_mask, int, 0444); +module_param_array(probe_mask, int, NULL, 0444); MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1)."); module_param(single_cmd, bool, 0444); MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " "(for debugging only)."); -module_param(enable_msi, int, 0); +module_param(enable_msi, int, 0444); MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -87,10 +89,6 @@ module_param(power_save_controller, bool, 0644); MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode."); #endif -/* just for backward compatibility */ -static int enable; -module_param(enable, bool, 0444); - MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," "{Intel, ICH6M}," @@ -98,12 +96,20 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," "{Intel, ESB2}," "{Intel, ICH8}," "{Intel, ICH9}," + "{Intel, ICH10}," + "{Intel, SCH}," "{ATI, SB450}," "{ATI, SB600}," "{ATI, RS600}," "{ATI, RS690}," "{ATI, RS780}," "{ATI, R600}," + "{ATI, RV630}," + "{ATI, RV610}," + "{ATI, RV670}," + "{ATI, RV635}," + "{ATI, RV620}," + "{ATI, RV770}," "{VIA, VT8251}," "{VIA, VT8237A}," "{SiS, SIS966}," @@ -370,6 +376,7 @@ struct azx { /* driver types */ enum { AZX_DRIVER_ICH, + AZX_DRIVER_SCH, AZX_DRIVER_ATI, AZX_DRIVER_ATIHDMI, AZX_DRIVER_VIA, @@ -380,6 +387,7 @@ enum { static char *driver_short_names[] __devinitdata = { [AZX_DRIVER_ICH] = "HDA Intel", + [AZX_DRIVER_SCH] = "HDA Intel MID", [AZX_DRIVER_ATI] = "HDA ATI SB", [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI", [AZX_DRIVER_VIA] = "HDA VIA VT82xx", @@ -547,7 +555,7 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec) again: timeout = jiffies + msecs_to_jiffies(1000); - do { + for (;;) { if (chip->polling_mode) { spin_lock_irq(&chip->reg_lock); azx_update_rirb(chip); @@ -555,8 +563,15 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec) } if (!chip->rirb.cmds) return chip->rirb.res; /* the last value */ - schedule_timeout_uninterruptible(1); - } while (time_after_eq(timeout, jiffies)); + if (time_after(jiffies, timeout)) + break; + if (codec->bus->needs_damn_long_delay) + msleep(2); /* temporary workaround */ + else { + udelay(10); + cond_resched(); + } + } if (chip->msi) { snd_printk(KERN_WARNING "hda_intel: No response from codec, " @@ -618,8 +633,9 @@ static int azx_single_send_cmd(struct hda_codec *codec, u32 val) } udelay(1); } - snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", - azx_readw(chip, IRS), val); + if (printk_ratelimit()) + snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", + azx_readw(chip, IRS), val); return -EIO; } @@ -635,8 +651,9 @@ static unsigned int azx_single_get_response(struct hda_codec *codec) return azx_readl(chip, IR); udelay(1); } - snd_printd(SFX "get_response timeout: IRS=0x%x\n", - azx_readw(chip, IRS)); + if (printk_ratelimit()) + snd_printd(SFX "get_response timeout: IRS=0x%x\n", + azx_readw(chip, IRS)); return (unsigned int)-1; } @@ -1031,7 +1048,8 @@ static unsigned int azx_max_codecs[] __devinitdata = { [AZX_DRIVER_NVIDIA] = 3, /* FIXME: correct? */ }; -static int __devinit azx_codec_create(struct azx *chip, const char *model) +static int __devinit azx_codec_create(struct azx *chip, const char *model, + unsigned int codec_probe_mask) { struct hda_bus_template bus_temp; int c, codecs, audio_codecs, err; @@ -1052,7 +1070,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model) codecs = audio_codecs = 0; for (c = 0; c < AZX_MAX_CODECS; c++) { - if ((chip->codec_mask & (1 << c)) & probe_mask) { + if ((chip->codec_mask & (1 << c)) & codec_probe_mask) { struct hda_codec *codec; err = snd_hda_codec_new(chip->bus, c, &codec); if (err < 0) @@ -1065,7 +1083,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model) if (!audio_codecs) { /* probe additional slots if no codec is found */ for (; c < azx_max_codecs[chip->driver_type]; c++) { - if ((chip->codec_mask & (1 << c)) & probe_mask) { + if ((chip->codec_mask & (1 << c)) & codec_probe_mask) { err = snd_hda_codec_new(chip->bus, c, NULL); if (err < 0) continue; @@ -1676,18 +1694,18 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = { {} }; -static void __devinit check_probe_mask(struct azx *chip) +static void __devinit check_probe_mask(struct azx *chip, int dev) { const struct snd_pci_quirk *q; - if (probe_mask == -1) { + if (probe_mask[dev] == -1) { q = snd_pci_quirk_lookup(chip->pci, probe_mask_list); if (q) { printk(KERN_INFO "hda_intel: probe_mask set to 0x%x " "for device %04x:%04x\n", q->value, q->subvendor, q->subdevice); - probe_mask = q->value; + probe_mask[dev] = q->value; } } } @@ -1697,17 +1715,18 @@ static void __devinit check_probe_mask(struct azx *chip) * constructor */ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, - int driver_type, + int dev, int driver_type, struct azx **rchip) { struct azx *chip; int err; + unsigned short gcap; static struct snd_device_ops ops = { .dev_free = azx_dev_free, }; *rchip = NULL; - + err = pci_enable_device(pci); if (err < 0) return err; @@ -1727,8 +1746,8 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, chip->driver_type = driver_type; chip->msi = enable_msi; - chip->position_fix = check_position_fix(chip, position_fix); - check_probe_mask(chip); + chip->position_fix = check_position_fix(chip, position_fix[dev]); + check_probe_mask(chip, dev); chip->single_cmd = single_cmd; @@ -1769,25 +1788,40 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, pci_set_master(pci); synchronize_irq(chip->irq); - switch (chip->driver_type) { - case AZX_DRIVER_ULI: - chip->playback_streams = ULI_NUM_PLAYBACK; - chip->capture_streams = ULI_NUM_CAPTURE; - chip->playback_index_offset = ULI_PLAYBACK_INDEX; - chip->capture_index_offset = ULI_CAPTURE_INDEX; - break; - case AZX_DRIVER_ATIHDMI: - chip->playback_streams = ATIHDMI_NUM_PLAYBACK; - chip->capture_streams = ATIHDMI_NUM_CAPTURE; - chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX; - chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX; - break; - default: - chip->playback_streams = ICH6_NUM_PLAYBACK; - chip->capture_streams = ICH6_NUM_CAPTURE; - chip->playback_index_offset = ICH6_PLAYBACK_INDEX; - chip->capture_index_offset = ICH6_CAPTURE_INDEX; - break; + gcap = azx_readw(chip, GCAP); + snd_printdd("chipset global capabilities = 0x%x\n", gcap); + + if (gcap) { + /* read number of streams from GCAP register instead of using + * hardcoded value + */ + chip->playback_streams = (gcap & (0xF << 12)) >> 12; + chip->capture_streams = (gcap & (0xF << 8)) >> 8; + chip->playback_index_offset = (gcap & (0xF << 12)) >> 12; + chip->capture_index_offset = 0; + } else { + /* gcap didn't give any info, switching to old method */ + + switch (chip->driver_type) { + case AZX_DRIVER_ULI: + chip->playback_streams = ULI_NUM_PLAYBACK; + chip->capture_streams = ULI_NUM_CAPTURE; + chip->playback_index_offset = ULI_PLAYBACK_INDEX; + chip->capture_index_offset = ULI_CAPTURE_INDEX; + break; + case AZX_DRIVER_ATIHDMI: + chip->playback_streams = ATIHDMI_NUM_PLAYBACK; + chip->capture_streams = ATIHDMI_NUM_CAPTURE; + chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX; + chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX; + break; + default: + chip->playback_streams = ICH6_NUM_PLAYBACK; + chip->capture_streams = ICH6_NUM_CAPTURE; + chip->playback_index_offset = ICH6_PLAYBACK_INDEX; + chip->capture_index_offset = ICH6_CAPTURE_INDEX; + break; + } } chip->num_streams = chip->playback_streams + chip->capture_streams; chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), @@ -1869,17 +1903,25 @@ static void power_down_all_codecs(struct azx *chip) static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { + static int dev; struct snd_card *card; struct azx *chip; int err; - card = snd_card_new(index, id, THIS_MODULE, 0); + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); if (!card) { snd_printk(KERN_ERR SFX "Error creating card!\n"); return -ENOMEM; } - err = azx_create(card, pci, pci_id->driver_data, &chip); + err = azx_create(card, pci, dev, pci_id->driver_data, &chip); if (err < 0) { snd_card_free(card); return err; @@ -1887,7 +1929,7 @@ static int __devinit azx_probe(struct pci_dev *pci, card->private_data = chip; /* create codec instances */ - err = azx_codec_create(chip, model); + err = azx_codec_create(chip, model[dev], probe_mask[dev]); if (err < 0) { snd_card_free(card); return err; @@ -1919,6 +1961,7 @@ static int __devinit azx_probe(struct pci_dev *pci, chip->running = 1; power_down_all_codecs(chip); + dev++; return err; } @@ -1936,12 +1979,21 @@ static struct pci_device_id azx_ids[] = { { 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */ { 0x8086, 0x293e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */ { 0x8086, 0x293f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */ + { 0x8086, 0x3a3e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */ + { 0x8086, 0x3a6e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */ + { 0x8086, 0x811b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SCH }, /* SCH*/ { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */ { 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */ { 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */ { 0x1002, 0x7919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS690 HDMI */ - { 0x1002, 0x960c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */ + { 0x1002, 0x960f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */ { 0x1002, 0xaa00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI R600 HDMI */ + { 0x1002, 0xaa08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV630 HDMI */ + { 0x1002, 0xaa10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV610 HDMI */ + { 0x1002, 0xaa18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV670 HDMI */ + { 0x1002, 0xaa20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV635 HDMI */ + { 0x1002, 0xaa28, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV620 HDMI */ + { 0x1002, 0xaa30, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV770 HDMI */ { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */ { 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */ { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 8c56c9cb0d0..ad0014ab71f 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -90,6 +90,13 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, void snd_hda_codec_resume_amp(struct hda_codec *codec); #endif +void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int *tlv); +struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, + const char *name); +int snd_hda_add_vmaster(struct hda_codec *codec, char *name, + unsigned int *tlv, const char **slaves); + /* amp value bits */ #define HDA_AMP_MUTE 0x80 #define HDA_AMP_UNMUTE 0x00 @@ -325,6 +332,7 @@ struct auto_pin_cfg { hda_nid_t input_pins[AUTO_PIN_LAST]; hda_nid_t dig_out_pin; hda_nid_t dig_in_pin; + hda_nid_t mono_out_pin; }; #define get_defcfg_connect(cfg) \ @@ -363,10 +371,11 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid) { if (nid < codec->start_nid || nid >= codec->start_nid + codec->num_nodes) - return snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); + return 0; return codec->wcaps[nid - codec->start_nid]; } +u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction); int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps); @@ -398,4 +407,11 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, hda_nid_t nid); #endif /* CONFIG_SND_HDA_POWER_SAVE */ +/* + * virtual master control + */ +struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, + const unsigned int *tlv); +int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave); + #endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index e94944f34ff..35a630d1770 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <sound/core.h> #include "hda_codec.h" @@ -203,7 +202,8 @@ static const char *get_jack_color(u32 cfg) } static void print_pin_caps(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) + struct hda_codec *codec, hda_nid_t nid, + int *supports_vref) { static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" }; static char *jack_types[16] = { @@ -213,7 +213,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer, "SPDIF In", "Digitial In", "Reserved", "Other" }; static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; - unsigned int caps; + unsigned int caps, val; caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); snd_iprintf(buffer, " Pincap 0x08%x:", caps); @@ -227,7 +227,45 @@ static void print_pin_caps(struct snd_info_buffer *buffer, snd_iprintf(buffer, " EAPD"); if (caps & AC_PINCAP_PRES_DETECT) snd_iprintf(buffer, " Detect"); + if (caps & AC_PINCAP_BALANCE) + snd_iprintf(buffer, " Balanced"); + if (caps & AC_PINCAP_LR_SWAP) + snd_iprintf(buffer, " R/L"); + if (caps & AC_PINCAP_TRIG_REQ) + snd_iprintf(buffer, " Trigger"); + if (caps & AC_PINCAP_IMP_SENSE) + snd_iprintf(buffer, " ImpSense"); snd_iprintf(buffer, "\n"); + if (caps & AC_PINCAP_VREF) { + unsigned int vref = + (caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; + snd_iprintf(buffer, " Vref caps:"); + if (vref & AC_PINCAP_VREF_HIZ) + snd_iprintf(buffer, " HIZ"); + if (vref & AC_PINCAP_VREF_50) + snd_iprintf(buffer, " 50"); + if (vref & AC_PINCAP_VREF_GRD) + snd_iprintf(buffer, " GRD"); + if (vref & AC_PINCAP_VREF_80) + snd_iprintf(buffer, " 80"); + if (vref & AC_PINCAP_VREF_100) + snd_iprintf(buffer, " 100"); + snd_iprintf(buffer, "\n"); + *supports_vref = 1; + } else + *supports_vref = 0; + if (caps & AC_PINCAP_EAPD) { + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_EAPD_BTLENABLE, 0); + snd_iprintf(buffer, " EAPD 0x%x:", val); + if (val & AC_EAPDBTL_BALANCED) + snd_iprintf(buffer, " BALANCED"); + if (val & AC_EAPDBTL_EAPD) + snd_iprintf(buffer, " EAPD"); + if (val & AC_EAPDBTL_LR_SWAP) + snd_iprintf(buffer, " R/L"); + snd_iprintf(buffer, "\n"); + } caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps, jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT], @@ -237,8 +275,233 @@ static void print_pin_caps(struct snd_info_buffer *buffer, snd_iprintf(buffer, " Conn = %s, Color = %s\n", get_jack_connection(caps), get_jack_color(caps)); + /* Default association and sequence values refer to default grouping + * of pin complexes and their sequence within the group. This is used + * for priority and resource allocation. + */ + snd_iprintf(buffer, " DefAssociation = 0x%x, Sequence = 0x%x\n", + (caps & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT, + caps & AC_DEFCFG_SEQUENCE); + if (((caps & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) & + AC_DEFCFG_MISC_NO_PRESENCE) { + /* Miscellaneous bit indicates external hardware does not + * support presence detection even if the pin complex + * indicates it is supported. + */ + snd_iprintf(buffer, " Misc = NO_PRESENCE\n"); + } +} + +static void print_pin_ctls(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid, + int supports_vref) +{ + unsigned int pinctls; + + pinctls = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls); + if (pinctls & AC_PINCTL_IN_EN) + snd_iprintf(buffer, " IN"); + if (pinctls & AC_PINCTL_OUT_EN) + snd_iprintf(buffer, " OUT"); + if (pinctls & AC_PINCTL_HP_EN) + snd_iprintf(buffer, " HP"); + if (supports_vref) { + int vref = pinctls & AC_PINCTL_VREFEN; + switch (vref) { + case AC_PINCTL_VREF_HIZ: + snd_iprintf(buffer, " VREF_HIZ"); + break; + case AC_PINCTL_VREF_50: + snd_iprintf(buffer, " VREF_50"); + break; + case AC_PINCTL_VREF_GRD: + snd_iprintf(buffer, " VREF_GRD"); + break; + case AC_PINCTL_VREF_80: + snd_iprintf(buffer, " VREF_80"); + break; + case AC_PINCTL_VREF_100: + snd_iprintf(buffer, " VREF_100"); + break; + } + } + snd_iprintf(buffer, "\n"); +} + +static void print_vol_knob(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int cap = snd_hda_param_read(codec, nid, + AC_PAR_VOL_KNB_CAP); + snd_iprintf(buffer, " Volume-Knob: delta=%d, steps=%d, ", + (cap >> 7) & 1, cap & 0x7f); + cap = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); + snd_iprintf(buffer, "direct=%d, val=%d\n", + (cap >> 7) & 1, cap & 0x7f); +} + +static void print_audio_io(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid, + unsigned int wid_type) +{ + int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); + snd_iprintf(buffer, + " Converter: stream=%d, channel=%d\n", + (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT, + conv & AC_CONV_CHANNEL); + + if (wid_type == AC_WID_AUD_IN && (conv & AC_CONV_CHANNEL) == 0) { + int sdi = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_SDI_SELECT, 0); + snd_iprintf(buffer, " SDI-Select: %d\n", + sdi & AC_SDI_SELECT); + } +} + +static void print_digital_conv(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int digi1 = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT_1, 0); + unsigned int digi2 = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT_2, 0); + snd_iprintf(buffer, " Digital:"); + if (digi1 & AC_DIG1_ENABLE) + snd_iprintf(buffer, " Enabled"); + if (digi1 & AC_DIG1_V) + snd_iprintf(buffer, " Validity"); + if (digi1 & AC_DIG1_VCFG) + snd_iprintf(buffer, " ValidityCfg"); + if (digi1 & AC_DIG1_EMPHASIS) + snd_iprintf(buffer, " Preemphasis"); + if (digi1 & AC_DIG1_COPYRIGHT) + snd_iprintf(buffer, " Copyright"); + if (digi1 & AC_DIG1_NONAUDIO) + snd_iprintf(buffer, " Non-Audio"); + if (digi1 & AC_DIG1_PROFESSIONAL) + snd_iprintf(buffer, " Pro"); + if (digi1 & AC_DIG1_LEVEL) + snd_iprintf(buffer, " GenLevel"); + snd_iprintf(buffer, "\n"); + snd_iprintf(buffer, " Digital category: 0x%x\n", digi2 & AC_DIG2_CC); +} + +static const char *get_pwr_state(u32 state) +{ + static const char *buf[4] = { + "D0", "D1", "D2", "D3" + }; + if (state < 4) + return buf[state]; + return "UNKNOWN"; +} + +static void print_power_state(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int pwr = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_POWER_STATE, 0); + snd_iprintf(buffer, " Power: setting=%s, actual=%s\n", + get_pwr_state(pwr & AC_PWRST_SETTING), + get_pwr_state((pwr & AC_PWRST_ACTUAL) >> + AC_PWRST_ACTUAL_SHIFT)); +} + +static void print_unsol_cap(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int unsol = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_UNSOLICITED_RESPONSE, 0); + snd_iprintf(buffer, + " Unsolicited: tag=%02x, enabled=%d\n", + unsol & AC_UNSOL_TAG, + (unsol & AC_UNSOL_ENABLED) ? 1 : 0); +} + +static void print_proc_caps(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int proc_caps = snd_hda_param_read(codec, nid, + AC_PAR_PROC_CAP); + snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n", + proc_caps & AC_PCAP_BENIGN, + (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT); } +static void print_conn_list(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid, + unsigned int wid_type, hda_nid_t *conn, + int conn_len) +{ + int c, curr = -1; + + if (conn_len > 1 && wid_type != AC_WID_AUD_MIX) + curr = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONNECT_SEL, 0); + snd_iprintf(buffer, " Connection: %d\n", conn_len); + if (conn_len > 0) { + snd_iprintf(buffer, " "); + for (c = 0; c < conn_len; c++) { + snd_iprintf(buffer, " 0x%02x", conn[c]); + if (c == curr) + snd_iprintf(buffer, "*"); + } + snd_iprintf(buffer, "\n"); + } +} + +static void print_realtek_coef(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int coeff = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PROC_COEF, 0); + snd_iprintf(buffer, " Processing Coefficient: 0x%02x\n", coeff); + coeff = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_COEF_INDEX, 0); + snd_iprintf(buffer, " Coefficient Index: 0x%02x\n", coeff); +} + +static void print_gpio(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int gpio = + snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); + unsigned int enable, direction, wake, unsol, sticky, data; + int i, max; + snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, " + "unsolicited=%d, wake=%d\n", + gpio & AC_GPIO_IO_COUNT, + (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT, + (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT, + (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0, + (gpio & AC_GPIO_WAKE) ? 1 : 0); + max = gpio & AC_GPIO_IO_COUNT; + enable = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_MASK, 0); + direction = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_DIRECTION, 0); + wake = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_WAKE_MASK, 0); + unsol = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0); + sticky = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_STICKY_MASK, 0); + data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_DATA, 0); + for (i = 0; i < max; ++i) + snd_iprintf(buffer, + " IO[%d]: enable=%d, dir=%d, wake=%d, " + "sticky=%d, data=%d\n", i, + (enable & (1<<i)) ? 1 : 0, + (direction & (1<<i)) ? 1 : 0, + (wake & (1<<i)) ? 1 : 0, + (sticky & (1<<i)) ? 1 : 0, + (data & (1<<i)) ? 1 : 0); + /* FIXME: add GPO and GPI pin information */ +} static void print_codec_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) @@ -276,14 +539,17 @@ static void print_codec_info(struct snd_info_entry *entry, snd_hda_power_down(codec); return; } + + print_gpio(buffer, codec, codec->afg); + for (i = 0; i < nodes; i++, nid++) { unsigned int wid_caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - int conn_len = 0; hda_nid_t conn[HDA_MAX_CONNECTIONS]; + int conn_len = 0; snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid, get_wid_type_name(wid_type), wid_caps); @@ -297,8 +563,18 @@ static void print_codec_info(struct snd_info_entry *entry, snd_iprintf(buffer, " Amp-In"); if (wid_caps & AC_WCAP_OUT_AMP) snd_iprintf(buffer, " Amp-Out"); + if (wid_caps & AC_WCAP_STRIPE) + snd_iprintf(buffer, " Stripe"); + if (wid_caps & AC_WCAP_LR_SWAP) + snd_iprintf(buffer, " R/L"); snd_iprintf(buffer, "\n"); + /* volume knob is a special widget that always have connection + * list + */ + if (wid_type == AC_WID_VOL_KNB) + wid_caps |= AC_WCAP_CONN_LIST; + if (wid_caps & AC_WCAP_CONN_LIST) conn_len = snd_hda_get_connections(codec, nid, conn, HDA_MAX_CONNECTIONS); @@ -318,48 +594,49 @@ static void print_codec_info(struct snd_info_entry *entry, wid_caps & AC_WCAP_STEREO, 1); } - if (wid_type == AC_WID_PIN) { - unsigned int pinctls; - print_pin_caps(buffer, codec, nid); - pinctls = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, - 0); - snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls); - if (pinctls & AC_PINCTL_IN_EN) - snd_iprintf(buffer, " IN"); - if (pinctls & AC_PINCTL_OUT_EN) - snd_iprintf(buffer, " OUT"); - if (pinctls & AC_PINCTL_HP_EN) - snd_iprintf(buffer, " HP"); - snd_iprintf(buffer, "\n"); + switch (wid_type) { + case AC_WID_PIN: { + int supports_vref; + print_pin_caps(buffer, codec, nid, &supports_vref); + print_pin_ctls(buffer, codec, nid, supports_vref); + break; } - - if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) && - (wid_caps & AC_WCAP_FORMAT_OVRD)) { - snd_iprintf(buffer, " PCM:\n"); - print_pcm_caps(buffer, codec, nid); + case AC_WID_VOL_KNB: + print_vol_knob(buffer, codec, nid); + break; + case AC_WID_AUD_OUT: + case AC_WID_AUD_IN: + print_audio_io(buffer, codec, nid, wid_type); + if (wid_caps & AC_WCAP_DIGITAL) + print_digital_conv(buffer, codec, nid); + if (wid_caps & AC_WCAP_FORMAT_OVRD) { + snd_iprintf(buffer, " PCM:\n"); + print_pcm_caps(buffer, codec, nid); + } + break; } + if (wid_caps & AC_WCAP_UNSOL_CAP) + print_unsol_cap(buffer, codec, nid); + if (wid_caps & AC_WCAP_POWER) - snd_iprintf(buffer, " Power: 0x%x\n", - snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, - 0)); - - if (wid_caps & AC_WCAP_CONN_LIST) { - int c, curr = -1; - if (conn_len > 1 && wid_type != AC_WID_AUD_MIX) - curr = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); - snd_iprintf(buffer, " Connection: %d\n", conn_len); - snd_iprintf(buffer, " "); - for (c = 0; c < conn_len; c++) { - snd_iprintf(buffer, " 0x%02x", conn[c]); - if (c == curr) - snd_iprintf(buffer, "*"); - } - snd_iprintf(buffer, "\n"); - } + print_power_state(buffer, codec, nid); + + if (wid_caps & AC_WCAP_DELAY) + snd_iprintf(buffer, " Delay: %d samples\n", + (wid_caps & AC_WCAP_DELAY) >> + AC_WCAP_DELAY_SHIFT); + + if (wid_caps & AC_WCAP_CONN_LIST) + print_conn_list(buffer, codec, nid, wid_type, + conn, conn_len); + + if (wid_caps & AC_WCAP_PROC_WID) + print_proc_caps(buffer, codec, nid); + + /* NID 0x20 == Realtek Define Registers */ + if (codec->vendor_id == 0x10ec && nid == 0x20) + print_realtek_coef(buffer, codec, nid); } snd_hda_power_down(codec); } diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 196ad3c9405..19f08846d6f 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -79,6 +78,11 @@ struct ad198x_spec { #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; #endif + /* for virtual master */ + hda_nid_t vmaster_nid; + u32 vmaster_tlv[4]; + const char **slave_vols; + const char **slave_sws; }; /* @@ -126,6 +130,32 @@ static int ad198x_init(struct hda_codec *codec) return 0; } +static const char *ad_slave_vols[] = { + "Front Playback Volume", + "Surround Playback Volume", + "Center Playback Volume", + "LFE Playback Volume", + "Side Playback Volume", + "Headphone Playback Volume", + "Mono Playback Volume", + "Speaker Playback Volume", + "IEC958 Playback Volume", + NULL +}; + +static const char *ad_slave_sws[] = { + "Front Playback Switch", + "Surround Playback Switch", + "Center Playback Switch", + "LFE Playback Switch", + "Side Playback Switch", + "Headphone Playback Switch", + "Mono Playback Switch", + "Speaker Playback Switch", + "IEC958 Playback Switch", + NULL +}; + static int ad198x_build_controls(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; @@ -147,6 +177,27 @@ static int ad198x_build_controls(struct hda_codec *codec) if (err < 0) return err; } + + /* if we have no master control, let's create it */ + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, + HDA_OUTPUT, spec->vmaster_tlv); + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + spec->vmaster_tlv, + (spec->slave_vols ? + spec->slave_vols : ad_slave_vols)); + if (err < 0) + return err; + } + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { + err = snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, + (spec->slave_sws ? + spec->slave_sws : ad_slave_sws)); + if (err < 0) + return err; + } + return 0; } @@ -370,7 +421,7 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol, int invert = (kcontrol->private_value >> 8) & 1; hda_nid_t nid = kcontrol->private_value & 0xff; unsigned int eapd; - eapd = ucontrol->value.integer.value[0]; + eapd = !!ucontrol->value.integer.value[0]; if (invert) eapd = !eapd; if (eapd == spec->cur_eapd) @@ -833,27 +884,29 @@ static const char *ad1986a_models[AD1986A_MODELS] = { static struct snd_pci_quirk ad1986a_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP), SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP), SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK), + SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK), + SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), + SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE), @@ -872,6 +925,13 @@ static struct hda_amp_list ad1986a_loopbacks[] = { }; #endif +static int is_jack_available(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int conf = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0); + return get_defcfg_connect(conf) != AC_JACK_PORT_NONE; +} + static int patch_ad1986a(struct hda_codec *codec) { struct ad198x_spec *spec; @@ -898,6 +958,7 @@ static int patch_ad1986a(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = ad1986a_loopbacks; #endif + spec->vmaster_nid = 0x1b; codec->patch_ops = ad198x_patch_ops; @@ -930,7 +991,8 @@ static int patch_ad1986a(struct hda_codec *codec) spec->multiout.max_channels = 2; spec->multiout.num_dacs = 1; spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - spec->multiout.dig_out_nid = 0; + if (!is_jack_available(codec, 0x25)) + spec->multiout.dig_out_nid = 0; spec->input_mux = &ad1986a_laptop_eapd_capture_source; break; case AD1986A_LAPTOP_AUTOMUTE: @@ -941,7 +1003,8 @@ static int patch_ad1986a(struct hda_codec *codec) spec->multiout.max_channels = 2; spec->multiout.num_dacs = 1; spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - spec->multiout.dig_out_nid = 0; + if (!is_jack_available(codec, 0x25)) + spec->multiout.dig_out_nid = 0; spec->input_mux = &ad1986a_laptop_eapd_capture_source; codec->patch_ops.unsol_event = ad1986a_hp_unsol_event; codec->patch_ops.init = ad1986a_hp_init; @@ -1020,6 +1083,8 @@ static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ad198x_spec *spec = codec->spec; + if (ucontrol->value.enumerated.item[0] > 1) + return -EINVAL; if (spec->spdif_route != ucontrol->value.enumerated.item[0]) { spec->spdif_route = ucontrol->value.enumerated.item[0]; snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0, @@ -1138,6 +1203,7 @@ static int patch_ad1983(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = ad1983_loopbacks; #endif + spec->vmaster_nid = 0x05; codec->patch_ops = ad198x_patch_ops; @@ -1496,14 +1562,14 @@ static const char *ad1981_models[AD1981_MODELS] = { }; static struct snd_pci_quirk ad1981_cfg_tbl[] = { + SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD), /* All HP models */ SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP), - /* HP nx6320 (reversed SSID, H/W bug) */ - SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP), + SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA), /* Lenovo Thinkpad T60/X60/Z6xx */ SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD), - SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD), - SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA), + /* HP nx6320 (reversed SSID, H/W bug) */ + SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP), {} }; @@ -1534,6 +1600,7 @@ static int patch_ad1981(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = ad1981_loopbacks; #endif + spec->vmaster_nid = 0x05; codec->patch_ops = ad198x_patch_ops; @@ -1908,7 +1975,6 @@ static struct snd_kcontrol_new ad1988_capture_mixers[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -1965,6 +2031,8 @@ static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol, int change; val = ucontrol->value.enumerated.item[0]; + if (val > 3) + return -EINVAL; if (!val) { sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE, @@ -2079,6 +2147,8 @@ static struct hda_verb ad1988_6stack_init_verbs[] = { {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x34, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* Analog CD Input */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, { } }; @@ -2720,8 +2790,8 @@ static const char *ad1988_models[AD1988_MODEL_LAST] = { }; static struct snd_pci_quirk ad1988_cfg_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG), SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG), + SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG), {} }; @@ -2843,6 +2913,7 @@ static int patch_ad1988(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = ad1988_loopbacks; #endif + spec->vmaster_nid = 0x04; return 0; } @@ -2919,7 +2990,6 @@ static struct snd_kcontrol_new ad1884_base_mixers[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -3009,6 +3079,20 @@ static struct hda_amp_list ad1884_loopbacks[] = { }; #endif +static const char *ad1884_slave_vols[] = { + "PCM Playback Volume", + "Mic Playback Volume", + "Mono Playback Volume", + "Front Mic Playback Volume", + "Mic Playback Volume", + "CD Playback Volume", + "Internal Mic Playback Volume", + "Docking Mic Playback Volume" + "Beep Playback Volume", + "IEC958 Playback Volume", + NULL +}; + static int patch_ad1884(struct hda_codec *codec) { struct ad198x_spec *spec; @@ -3036,6 +3120,9 @@ static int patch_ad1884(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = ad1884_loopbacks; #endif + spec->vmaster_nid = 0x04; + /* we need to cover all playback volumes */ + spec->slave_vols = ad1884_slave_vols; codec->patch_ops = ad198x_patch_ops; @@ -3054,6 +3141,20 @@ static struct hda_input_mux ad1984_thinkpad_capture_source = { }, }; + +/* + * Dell Precision T3400 + */ +static struct hda_input_mux ad1984_dell_desktop_capture_source = { + .num_items = 3, + .items = { + { "Front Mic", 0x0 }, + { "Line-In", 0x1 }, + { "Mix", 0x3 }, + }, +}; + + static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = { HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */ @@ -3078,7 +3179,6 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -3087,6 +3187,16 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = { .get = ad198x_mux_enum_get, .put = ad198x_mux_enum_put, }, + /* SPDIF controls */ + HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", + /* identical with ad1983 */ + .info = ad1983_spdif_route_info, + .get = ad1983_spdif_route_get, + .put = ad1983_spdif_route_put, + }, { } /* end */ }; @@ -3104,6 +3214,44 @@ static struct hda_verb ad1984_thinkpad_init_verbs[] = { { } /* end */ }; +/* + * Dell Precision T3400 + */ +static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = { + HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT), + /* + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT), + */ + HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + { } /* end */ +}; + /* Digial MIC ADC NID 0x05 + 0x06 */ static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, @@ -3157,17 +3305,20 @@ static int ad1984_build_pcms(struct hda_codec *codec) enum { AD1984_BASIC, AD1984_THINKPAD, + AD1984_DELL_DESKTOP, AD1984_MODELS }; static const char *ad1984_models[AD1984_MODELS] = { [AD1984_BASIC] = "basic", [AD1984_THINKPAD] = "thinkpad", + [AD1984_DELL_DESKTOP] = "dell_desktop", }; static struct snd_pci_quirk ad1984_cfg_tbl[] = { /* Lenovo Thinkpad T61/X61 */ SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD), + SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP), {} }; @@ -3189,11 +3340,16 @@ static int patch_ad1984(struct hda_codec *codec) codec->patch_ops.build_pcms = ad1984_build_pcms; break; case AD1984_THINKPAD: - spec->multiout.dig_out_nid = 0; + spec->multiout.dig_out_nid = AD1884_SPDIF_OUT; spec->input_mux = &ad1984_thinkpad_capture_source; spec->mixers[0] = ad1984_thinkpad_mixers; spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs; break; + case AD1984_DELL_DESKTOP: + spec->multiout.dig_out_nid = 0; + spec->input_mux = &ad1984_dell_desktop_capture_source; + spec->mixers[0] = ad1984_dell_desktop_mixers; + break; } return 0; } @@ -3267,7 +3423,6 @@ static struct snd_kcontrol_new ad1882_base_mixers[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -3468,6 +3623,7 @@ static int patch_ad1882(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = ad1882_loopbacks; #endif + spec->vmaster_nid = 0x04; codec->patch_ops = ad198x_patch_ops; diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c index fbb8969dc55..9a8bb4ce3f8 100644 --- a/sound/pci/hda/patch_atihdmi.c +++ b/sound/pci/hda/patch_atihdmi.c @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -158,6 +157,6 @@ struct hda_codec_preset snd_hda_preset_atihdmi[] = { { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, { .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, { .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi }, - { .id = 0x1002aa01, .name = "ATI R600 HDMI", .patch = patch_atihdmi }, + { .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 6c54793bf42..3d6097ba1d6 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -186,7 +185,6 @@ static struct snd_kcontrol_new cmi9880_basic_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 6aa07398674..f6dd51cda7b 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -65,6 +64,11 @@ struct conexant_spec { hda_nid_t *adc_nids; hda_nid_t dig_in_nid; /* digital-in NID; optional */ + unsigned int cur_adc_idx; + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; + /* capture source */ const struct hda_input_mux *input_mux; hda_nid_t *capsrc_nids; @@ -218,6 +222,41 @@ static struct hda_pcm_stream conexant_pcm_digital_capture = { /* NID is set in alc_build_pcms */ }; +static int cx5051_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct conexant_spec *spec = codec->spec; + spec->cur_adc = spec->adc_nids[spec->cur_adc_idx]; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); + return 0; +} + +static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct conexant_spec *spec = codec->spec; + snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0); + spec->cur_adc = 0; + return 0; +} + +static struct hda_pcm_stream cx5051_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0, /* fill later */ + .ops = { + .prepare = cx5051_capture_pcm_prepare, + .cleanup = cx5051_capture_pcm_cleanup + }, +}; + static int conexant_build_pcms(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -232,7 +271,12 @@ static int conexant_build_pcms(struct hda_codec *codec) spec->multiout.max_channels; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_analog_capture; + if (codec->vendor_id == 0x14f15051) + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + cx5051_pcm_analog_capture; + else + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + conexant_pcm_analog_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; @@ -373,7 +417,7 @@ static int cxt_eapd_put(struct snd_kcontrol *kcontrol, hda_nid_t nid = kcontrol->private_value & 0xff; unsigned int eapd; - eapd = ucontrol->value.integer.value[0]; + eapd = !!ucontrol->value.integer.value[0]; if (invert) eapd = !eapd; if (eapd == spec->cur_eapd) @@ -454,7 +498,16 @@ static struct hda_input_mux cxt5045_capture_source = { .num_items = 2, .items = { { "IntMic", 0x1 }, - { "LineIn", 0x2 }, + { "ExtMic", 0x2 }, + } +}; + +static struct hda_input_mux cxt5045_capture_source_benq = { + .num_items = 3, + .items = { + { "IntMic", 0x1 }, + { "ExtMic", 0x2 }, + { "LineIn", 0x3 }, } }; @@ -577,6 +630,15 @@ static struct snd_kcontrol_new cxt5045_mixers[] = { {} }; +static struct snd_kcontrol_new cxt5045_benq_mixers[] = { + HDA_CODEC_VOLUME("Line In Capture Volume", 0x1a, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Line In Capture Switch", 0x1a, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Playback Volume", 0x17, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("Line In Playback Switch", 0x17, 0x3, HDA_INPUT), + + {} +}; + static struct hda_verb cxt5045_init_verbs[] = { /* Line in, Mic */ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, @@ -602,6 +664,30 @@ static struct hda_verb cxt5045_init_verbs[] = { { } /* end */ }; +static struct hda_verb cxt5045_benq_init_verbs[] = { + /* Int Mic, Mic */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, + /* Line In,HP, Amp */ + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x10, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x11, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Record selector: Int mic */ + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17}, + /* SPDIF route: PCM */ + {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* EAPD */ + {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ + { } /* end */ +}; static struct hda_verb cxt5045_hp_sense_init_verbs[] = { /* pin sensing on HP jack */ @@ -740,8 +826,10 @@ static int cxt5045_init(struct hda_codec *codec) enum { - CXT5045_LAPTOP, /* Laptops w/ EAPD support */ - CXT5045_FUJITSU, /* Laptops w/ EAPD support */ + CXT5045_LAPTOP_HPSENSE, + CXT5045_LAPTOP_MICSENSE, + CXT5045_LAPTOP_HPMICSENSE, + CXT5045_BENQ, #ifdef CONFIG_SND_DEBUG CXT5045_TEST, #endif @@ -749,23 +837,35 @@ enum { }; static const char *cxt5045_models[CXT5045_MODELS] = { - [CXT5045_LAPTOP] = "laptop", - [CXT5045_FUJITSU] = "fujitsu", + [CXT5045_LAPTOP_HPSENSE] = "laptop-hpsense", + [CXT5045_LAPTOP_MICSENSE] = "laptop-micsense", + [CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense", + [CXT5045_BENQ] = "benq", #ifdef CONFIG_SND_DEBUG [CXT5045_TEST] = "test", #endif }; static struct snd_pci_quirk cxt5045_cfg_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU), - SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP), + SND_PCI_QUIRK(0x103c, 0x30a5, "HP", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ), + SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE), + SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1631, 0xc106, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1631, 0xc107, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE), {} }; @@ -803,7 +903,7 @@ static int patch_cxt5045(struct hda_codec *codec) cxt5045_models, cxt5045_cfg_tbl); switch (board_config) { - case CXT5045_LAPTOP: + case CXT5045_LAPTOP_HPSENSE: codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; spec->input_mux = &cxt5045_capture_source; spec->num_init_verbs = 2; @@ -811,20 +911,53 @@ static int patch_cxt5045(struct hda_codec *codec) spec->mixers[0] = cxt5045_mixers; codec->patch_ops.init = cxt5045_init; break; - case CXT5045_FUJITSU: + case CXT5045_LAPTOP_MICSENSE: spec->input_mux = &cxt5045_capture_source; spec->num_init_verbs = 2; spec->init_verbs[1] = cxt5045_mic_sense_init_verbs; spec->mixers[0] = cxt5045_mixers; codec->patch_ops.init = cxt5045_init; break; + default: + case CXT5045_LAPTOP_HPMICSENSE: + codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; + spec->input_mux = &cxt5045_capture_source; + spec->num_init_verbs = 3; + spec->init_verbs[1] = cxt5045_hp_sense_init_verbs; + spec->init_verbs[2] = cxt5045_mic_sense_init_verbs; + spec->mixers[0] = cxt5045_mixers; + codec->patch_ops.init = cxt5045_init; + break; + case CXT5045_BENQ: + codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; + spec->input_mux = &cxt5045_capture_source_benq; + spec->num_init_verbs = 1; + spec->init_verbs[0] = cxt5045_benq_init_verbs; + spec->mixers[0] = cxt5045_mixers; + spec->mixers[1] = cxt5045_benq_mixers; + spec->num_mixers = 2; + codec->patch_ops.init = cxt5045_init; + break; #ifdef CONFIG_SND_DEBUG case CXT5045_TEST: spec->input_mux = &cxt5045_test_capture_source; spec->mixers[0] = cxt5045_test_mixer; spec->init_verbs[0] = cxt5045_test_init_verbs; + break; + #endif } + + /* + * Fix max PCM level to 0 dB + * (originall it has 0x2b steps with 0dB offset 0x14) + */ + snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT, + (0x14 << AC_AMPCAP_OFFSET_SHIFT) | + (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); + return 0; } @@ -933,13 +1066,13 @@ static void cxt5047_hp2_automute(struct hda_codec *codec) static void cxt5047_hp_automic(struct hda_codec *codec) { static struct hda_verb mic_jack_on[] = { - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {} }; static struct hda_verb mic_jack_off[] = { - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {} }; unsigned int present; @@ -956,8 +1089,7 @@ static void cxt5047_hp_automic(struct hda_codec *codec) static void cxt5047_hp_unsol_event(struct hda_codec *codec, unsigned int res) { - res >>= 26; - switch (res) { + switch (res >> 26) { case CONEXANT_HP_EVENT: cxt5047_hp_automute(codec); break; @@ -1166,6 +1298,17 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = { .get = conexant_mux_enum_get, .put = conexant_mux_enum_put, }, + HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT), + HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT), + { } /* end */ }; @@ -1255,9 +1398,9 @@ static const char *cxt5047_models[CXT5047_MODELS] = { static struct snd_pci_quirk cxt5047_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30a0, "HP DV1000", CXT5047_LAPTOP), + SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP), SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV2000T/DV3000T", CXT5047_LAPTOP), SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2000Z", CXT5047_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP), SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD), {} }; @@ -1324,10 +1467,260 @@ static int patch_cxt5047(struct hda_codec *codec) return 0; } +/* Conexant 5051 specific */ +static hda_nid_t cxt5051_dac_nids[1] = { 0x10 }; +static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 }; +#define CXT5051_SPDIF_OUT 0x1C +#define CXT5051_PORTB_EVENT 0x38 +#define CXT5051_PORTC_EVENT 0x39 + +static struct hda_channel_mode cxt5051_modes[1] = { + { 2, NULL }, +}; + +static void cxt5051_update_speaker(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + unsigned int pinctl; + pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl); +} + +/* turn on/off EAPD (+ mute HP) as a master switch */ +static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + if (!cxt_eapd_put(kcontrol, ucontrol)) + return 0; + cxt5051_update_speaker(codec); + return 1; +} + +/* toggle input of built-in and mic jack appropriately */ +static void cxt5051_portb_automic(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x17, 0, + AC_VERB_GET_PIN_SENSE, 0) & + AC_PINSENSE_PRESENCE; + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_CONNECT_SEL, + present ? 0x01 : 0x00); +} + +/* switch the current ADC according to the jack state */ +static void cxt5051_portc_automic(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + unsigned int present; + hda_nid_t new_adc; + + present = snd_hda_codec_read(codec, 0x18, 0, + AC_VERB_GET_PIN_SENSE, 0) & + AC_PINSENSE_PRESENCE; + if (present) + spec->cur_adc_idx = 1; + else + spec->cur_adc_idx = 0; + new_adc = spec->adc_nids[spec->cur_adc_idx]; + if (spec->cur_adc && spec->cur_adc != new_adc) { + /* stream is running, let's swap the current ADC */ + snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0); + spec->cur_adc = new_adc; + snd_hda_codec_setup_stream(codec, new_adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + } +} + +/* mute internal speaker if HP is plugged */ +static void cxt5051_hp_automute(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + + spec->hp_present = snd_hda_codec_read(codec, 0x16, 0, + AC_VERB_GET_PIN_SENSE, 0) & + AC_PINSENSE_PRESENCE; + cxt5051_update_speaker(codec); +} + +/* unsolicited event for HP jack sensing */ +static void cxt5051_hp_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case CONEXANT_HP_EVENT: + cxt5051_hp_automute(codec); + break; + case CXT5051_PORTB_EVENT: + cxt5051_portb_automic(codec); + break; + case CXT5051_PORTC_EVENT: + cxt5051_portc_automic(codec); + break; + } +} + +static struct snd_kcontrol_new cxt5051_mixers[] = { + HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = cxt_eapd_info, + .get = cxt_eapd_get, + .put = cxt5051_hp_master_sw_put, + .private_value = 0x1a, + }, + + {} +}; + +static struct snd_kcontrol_new cxt5051_hp_mixers[] = { + HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = cxt_eapd_info, + .get = cxt_eapd_get, + .put = cxt5051_hp_master_sw_put, + .private_value = 0x1a, + }, + + {} +}; + +static struct hda_verb cxt5051_init_verbs[] = { + /* Line in, Mic */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, + /* SPK */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP, Amp */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* DAC1 */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Record selector: Int mic */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, + /* SPDIF route: PCM */ + {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* EAPD */ + {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ + {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, + {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT}, + { } /* end */ +}; + +/* initialize jack-sensing, too */ +static int cxt5051_init(struct hda_codec *codec) +{ + conexant_init(codec); + if (codec->patch_ops.unsol_event) { + cxt5051_hp_automute(codec); + cxt5051_portb_automic(codec); + cxt5051_portc_automic(codec); + } + return 0; +} + + +enum { + CXT5051_LAPTOP, /* Laptops w/ EAPD support */ + CXT5051_HP, /* no docking */ + CXT5051_MODELS +}; + +static const char *cxt5051_models[CXT5051_MODELS] = { + [CXT5051_LAPTOP] = "laptop", + [CXT5051_HP] = "hp", +}; + +static struct snd_pci_quirk cxt5051_cfg_tbl[] = { + SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", + CXT5051_LAPTOP), + SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), + {} +}; + +static int patch_cxt5051(struct hda_codec *codec) +{ + struct conexant_spec *spec; + int board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + mutex_init(&spec->amp_mutex); + codec->spec = spec; + + codec->patch_ops = conexant_patch_ops; + codec->patch_ops.init = cxt5051_init; + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(cxt5051_dac_nids); + spec->multiout.dac_nids = cxt5051_dac_nids; + spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT; + spec->num_adc_nids = 1; /* not 2; via auto-mic switch */ + spec->adc_nids = cxt5051_adc_nids; + spec->num_mixers = 1; + spec->mixers[0] = cxt5051_mixers; + spec->num_init_verbs = 1; + spec->init_verbs[0] = cxt5051_init_verbs; + spec->spdif_route = 0; + spec->num_channel_mode = ARRAY_SIZE(cxt5051_modes); + spec->channel_mode = cxt5051_modes; + spec->cur_adc = 0; + spec->cur_adc_idx = 0; + + board_config = snd_hda_check_board_config(codec, CXT5051_MODELS, + cxt5051_models, + cxt5051_cfg_tbl); + switch (board_config) { + case CXT5051_HP: + codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; + spec->mixers[0] = cxt5051_hp_mixers; + break; + default: + case CXT5051_LAPTOP: + codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; + break; + } + + return 0; +} + + +/* + */ + struct hda_codec_preset snd_hda_preset_conexant[] = { { .id = 0x14f15045, .name = "CX20549 (Venice)", .patch = patch_cxt5045 }, { .id = 0x14f15047, .name = "CX20551 (Waikiki)", .patch = patch_cxt5047 }, + { .id = 0x14f15051, .name = "CX20561 (Hermosa)", + .patch = patch_cxt5051 }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1c502789cc1..586d98f1b63 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -23,7 +23,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -92,9 +91,12 @@ enum { ALC262_HP_BPC, ALC262_HP_BPC_D7000_WL, ALC262_HP_BPC_D7000_WF, + ALC262_HP_TC_T5735, + ALC262_HP_RP5700, ALC262_BENQ_ED8, ALC262_SONY_ASSAMD, ALC262_BENQ_T31, + ALC262_ULTRA, ALC262_AUTO, ALC262_MODEL_LAST /* last tag */ }; @@ -104,10 +106,21 @@ enum { ALC268_3ST, ALC268_TOSHIBA, ALC268_ACER, + ALC268_DELL, +#ifdef CONFIG_SND_DEBUG + ALC268_TEST, +#endif ALC268_AUTO, ALC268_MODEL_LAST /* last tag */ }; +/* ALC269 models */ +enum { + ALC269_BASIC, + ALC269_AUTO, + ALC269_MODEL_LAST /* last tag */ +}; + /* ALC861 models */ enum { ALC861_3ST, @@ -144,6 +157,7 @@ enum { ALC662_5ST_DIG, ALC662_LENOVO_101E, ALC662_ASUS_EEEPC_P701, + ALC662_ASUS_EEEPC_EP20, ALC662_AUTO, ALC662_MODEL_LAST, }; @@ -183,6 +197,8 @@ enum { ALC883_HAIER_W66, ALC888_6ST_HP, ALC888_3ST_HP, + ALC888_6ST_DELL, + ALC883_MITAC, ALC883_AUTO, ALC883_MODEL_LAST, }; @@ -204,6 +220,8 @@ struct alc_spec { char *stream_name_analog; /* analog PCM stream */ struct hda_pcm_stream *stream_analog_playback; struct hda_pcm_stream *stream_analog_capture; + struct hda_pcm_stream *stream_analog_alt_playback; + struct hda_pcm_stream *stream_analog_alt_capture; char *stream_name_digital; /* digital PCM stream */ struct hda_pcm_stream *stream_digital_playback; @@ -214,6 +232,7 @@ struct alc_spec { * max_channels, dacs must be set * dig_out_nid and hp_nid are optional */ + hda_nid_t alt_dac_nid; /* capture */ unsigned int num_adc_nids; @@ -247,7 +266,11 @@ struct alc_spec { /* for pin sensing */ unsigned int sense_updated: 1; unsigned int jack_present: 1; + unsigned int master_sw: 1; + /* for virtual master */ + hda_nid_t vmaster_nid; + u32 vmaster_tlv[4]; #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; #endif @@ -562,7 +585,7 @@ static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol, unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long *valp = ucontrol->value.integer.value; unsigned int val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DIGI_CONVERT, 0x00); + AC_VERB_GET_DIGI_CONVERT_1, 0x00); *valp = (val & mask) != 0; return 0; @@ -576,7 +599,7 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long val = *ucontrol->value.integer.value; unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DIGI_CONVERT, + AC_VERB_GET_DIGI_CONVERT_1, 0x00); /* Set/unset the masked control bit(s) as needed */ @@ -598,6 +621,59 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, .private_value = nid | (mask<<16) } #endif /* CONFIG_SND_DEBUG */ +/* A switch control to allow the enabling EAPD digital outputs on the ALC26x. + * Again, this is only used in the ALC26x test models to help identify when + * the EAPD line must be asserted for features to work. + */ +#ifdef CONFIG_SND_DEBUG +#define alc_eapd_ctrl_info snd_ctl_boolean_mono_info + +static int alc_eapd_ctrl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char mask = (kcontrol->private_value >> 16) & 0xff; + long *valp = ucontrol->value.integer.value; + unsigned int val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_EAPD_BTLENABLE, 0x00); + + *valp = (val & mask) != 0; + return 0; +} + +static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int change; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char mask = (kcontrol->private_value >> 16) & 0xff; + long val = *ucontrol->value.integer.value; + unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_EAPD_BTLENABLE, + 0x00); + + /* Set/unset the masked control bit(s) as needed */ + change = (!val ? 0 : mask) != (ctrl_data & mask); + if (!val) + ctrl_data &= ~mask; + else + ctrl_data |= mask; + snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, + ctrl_data); + + return change; +} + +#define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .info = alc_eapd_ctrl_info, \ + .get = alc_eapd_ctrl_get, \ + .put = alc_eapd_ctrl_put, \ + .private_value = nid | (mask<<16) } +#endif /* CONFIG_SND_DEBUG */ + /* * set up from the preset table */ @@ -739,7 +815,7 @@ static void alc_subsystem_id(struct hda_codec *codec, /* check sum */ tmp = 0; for (i = 1; i < 16; i++) { - if ((ass >> i) && 1) + if ((ass >> i) & 1) tmp++; } if (((ass >> 16) & 0xf) != tmp) @@ -828,10 +904,10 @@ do_sku: break; } - /* is laptop and enable the function "Mute internal speaker + /* is laptop or Desktop and enable the function "Mute internal speaker * when the external headphone out jack is plugged" */ - if (!(ass & 0x4) || !(ass & 0x8000)) + if (!(ass & 0x8000)) return; /* * 10~8 : Jack location @@ -841,9 +917,9 @@ do_sku: * when the external headphone out jack is plugged" */ if (!spec->autocfg.speaker_pins[0]) { - if (spec->multiout.dac_nids[0]) + if (spec->autocfg.line_out_pins[0]) spec->autocfg.speaker_pins[0] = - spec->multiout.dac_nids[0]; + spec->autocfg.line_out_pins[0]; else return; } @@ -1009,7 +1085,6 @@ static struct snd_kcontrol_new alc880_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -1031,7 +1106,6 @@ static struct snd_kcontrol_new alc880_capture_alt_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -1226,7 +1300,6 @@ static struct snd_kcontrol_new alc880_z71v_mixer[] = { }; -/* FIXME! */ /* * ALC880 F1734 model * @@ -1242,8 +1315,8 @@ static hda_nid_t alc880_f1734_dac_nids[1] = { static struct snd_kcontrol_new alc880_f1734_mixer[] = { HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Internal Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), @@ -1252,7 +1325,6 @@ static struct snd_kcontrol_new alc880_f1734_mixer[] = { }; -/* FIXME! */ /* * ALC880 ASUS model * @@ -1289,7 +1361,6 @@ static struct snd_kcontrol_new alc880_asus_mixer[] = { { } /* end */ }; -/* FIXME! */ /* * ALC880 ASUS W1V model * @@ -1327,7 +1398,6 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -1341,10 +1411,10 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = { /* Uniwill */ static struct snd_kcontrol_new alc880_uniwill_mixer[] = { - HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), @@ -1384,16 +1454,49 @@ static struct snd_kcontrol_new alc880_fujitsu_mixer[] = { }; static struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = { - HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), { } /* end */ }; /* + * virtual master controls + */ + +/* + * slave controls for virtual master + */ +static const char *alc_slave_vols[] = { + "Front Playback Volume", + "Surround Playback Volume", + "Center Playback Volume", + "LFE Playback Volume", + "Side Playback Volume", + "Headphone Playback Volume", + "Speaker Playback Volume", + "Mono Playback Volume", + "Line-Out Playback Volume", + NULL, +}; + +static const char *alc_slave_sws[] = { + "Front Playback Switch", + "Surround Playback Switch", + "Center Playback Switch", + "LFE Playback Switch", + "Side Playback Switch", + "Headphone Playback Switch", + "Speaker Playback Switch", + "Mono Playback Switch", + "IEC958 Playback Switch", + NULL, +}; + +/* * build control elements */ static int alc_build_controls(struct hda_codec *codec) @@ -1419,6 +1522,23 @@ static int alc_build_controls(struct hda_codec *codec) if (err < 0) return err; } + + /* if we have no master control, let's create it */ + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, + HDA_OUTPUT, spec->vmaster_tlv); + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + spec->vmaster_tlv, alc_slave_vols); + if (err < 0) + return err; + } + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { + err = snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, alc_slave_sws); + if (err < 0) + return err; + } + return 0; } @@ -1790,7 +1910,6 @@ static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec, alc880_uniwill_p53_dcvol_automute(codec); } -/* FIXME! */ /* * F1734 pin configuration: * HP = 0x14, speaker-out = 0x15, mic = 0x18 @@ -1819,7 +1938,6 @@ static struct hda_verb alc880_pin_f1734_init_verbs[] = { { } }; -/* FIXME! */ /* * ASUS pin configuration: * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a @@ -1966,9 +2084,8 @@ static struct hda_channel_mode alc880_lg_ch_modes[3] = { }; static struct snd_kcontrol_new alc880_lg_mixer[] = { - /* FIXME: it's not really "master" but front channels */ - HDA_CODEC_VOLUME("Master Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Front Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0f, 2, HDA_INPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), @@ -2256,7 +2373,7 @@ static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, /* * Analog capture */ -static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo, +static int alc880_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, @@ -2264,18 +2381,18 @@ static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo, { struct alc_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], stream_tag, 0, format); return 0; } -static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, +static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct alc_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], 0, 0, 0); return 0; } @@ -2296,13 +2413,27 @@ static struct hda_pcm_stream alc880_pcm_analog_playback = { }; static struct hda_pcm_stream alc880_pcm_analog_capture = { - .substreams = 2, + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ +}; + +static struct hda_pcm_stream alc880_pcm_analog_alt_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ +}; + +static struct hda_pcm_stream alc880_pcm_analog_alt_capture = { + .substreams = 2, /* can be overridden */ .channels_min = 2, .channels_max = 2, /* NID is set in alc_build_pcms */ .ops = { - .prepare = alc880_capture_pcm_prepare, - .cleanup = alc880_capture_pcm_cleanup + .prepare = alc880_alt_capture_pcm_prepare, + .cleanup = alc880_alt_capture_pcm_cleanup }, }; @@ -2326,7 +2457,7 @@ static struct hda_pcm_stream alc880_pcm_digital_capture = { }; /* Used by alc_build_pcms to flag that a PCM has no playback stream */ -static struct hda_pcm_stream alc_pcm_null_playback = { +static struct hda_pcm_stream alc_pcm_null_stream = { .substreams = 0, .channels_min = 0, .channels_max = 0, @@ -2383,17 +2514,32 @@ static int alc_build_pcms(struct hda_codec *codec) * model, configure a second analog capture-only PCM. */ /* Additional Analaog capture for index #2 */ - if (spec->num_adc_nids > 1 && spec->stream_analog_capture && - spec->adc_nids) { + if ((spec->alt_dac_nid && spec->stream_analog_alt_playback) || + (spec->num_adc_nids > 1 && spec->stream_analog_alt_capture)) { codec->num_pcms = 3; info = spec->pcm_rec + 2; info->name = spec->stream_name_analog; - /* No playback stream for second PCM */ - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = alc_pcm_null_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; - if (spec->stream_analog_capture) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1]; + if (spec->alt_dac_nid) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + *spec->stream_analog_alt_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->alt_dac_nid; + } else { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + alc_pcm_null_stream; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; + } + if (spec->num_adc_nids > 1) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + *spec->stream_analog_alt_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = + spec->adc_nids[1]; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = + spec->num_adc_nids - 1; + } else { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + alc_pcm_null_stream; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0; } } @@ -2723,23 +2869,17 @@ static const char *alc880_models[ALC880_MODEL_LAST] = { }; static struct snd_pci_quirk alc880_cfg_tbl[] = { - /* Broken BIOS configuration */ - SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), - SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG), - + SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810), SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG), SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST), - SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810), SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG), SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG), SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG), SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG), SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG), SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST), - SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG), SND_PCI_QUIRK(0x103c, 0x2a09, "HP", ALC880_5ST), - SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V), SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG), SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG), @@ -2754,54 +2894,50 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG), SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST), SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST), - SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS), - - SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST), + SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS), /* default ASUS */ SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST), + SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST), + SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST), SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST), SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST), - SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST), - SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO), - SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO), - SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810), - SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700), - SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG), - SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG), SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG), SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG), SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO), + SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO), SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2), - + SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG), SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG), + SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734), SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL), SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53), - SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734), - + SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810), + SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG), SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL), SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734), + SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL), SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU), - + SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW), SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG), SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG), - SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW), SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW), - - SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG), + SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700), + SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), /* broken BIOS */ + SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG), + SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG), SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG), SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG), SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG), SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG), SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG), SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST), - + SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST), /* default Intel */ + SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG), + SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG), {} }; @@ -3511,6 +3647,7 @@ static int patch_alc880(struct hda_codec *codec) spec->stream_name_analog = "ALC880 Analog"; spec->stream_analog_playback = &alc880_pcm_analog_playback; spec->stream_analog_capture = &alc880_pcm_analog_capture; + spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture; spec->stream_name_digital = "ALC880 Digital"; spec->stream_digital_playback = &alc880_pcm_digital_playback; @@ -3535,6 +3672,8 @@ static int patch_alc880(struct hda_codec *codec) } } + spec->vmaster_nid = 0x0c; + codec->patch_ops = alc_patch_ops; if (board_config == ALC880_AUTO) spec->init_hook = alc880_auto_init; @@ -3691,18 +3830,135 @@ static struct snd_kcontrol_new alc260_pc_beep_mixer[] = { { } /* end */ }; +/* update HP, line and mono out pins according to the master switch */ +static void alc260_hp_master_update(struct hda_codec *codec, + hda_nid_t hp, hda_nid_t line, + hda_nid_t mono) +{ + struct alc_spec *spec = codec->spec; + unsigned int val = spec->master_sw ? PIN_HP : 0; + /* change HP and line-out pins */ + snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + val); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + val); + /* mono (speaker) depending on the HP jack sense */ + val = (val && !spec->jack_present) ? PIN_OUT : 0; + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + val); +} + +static int alc260_hp_master_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + *ucontrol->value.integer.value = spec->master_sw; + return 0; +} + +static int alc260_hp_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int val = !!*ucontrol->value.integer.value; + hda_nid_t hp, line, mono; + + if (val == spec->master_sw) + return 0; + spec->master_sw = val; + hp = (kcontrol->private_value >> 16) & 0xff; + line = (kcontrol->private_value >> 8) & 0xff; + mono = kcontrol->private_value & 0xff; + alc260_hp_master_update(codec, hp, line, mono); + return 1; +} + +static struct snd_kcontrol_new alc260_hp_output_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = alc260_hp_master_sw_get, + .put = alc260_hp_master_sw_put, + .private_value = (0x0f << 16) | (0x10 << 8) | 0x11 + }, + HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, + HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT), + { } /* end */ +}; + +static struct hda_verb alc260_hp_unsol_verbs[] = { + {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {}, +}; + +static void alc260_hp_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int present; + + present = snd_hda_codec_read(codec, 0x10, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + alc260_hp_master_update(codec, 0x0f, 0x10, 0x11); +} + +static void alc260_hp_unsol_event(struct hda_codec *codec, unsigned int res) +{ + if ((res >> 26) == ALC880_HP_EVENT) + alc260_hp_automute(codec); +} + static struct snd_kcontrol_new alc260_hp_3013_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = alc260_hp_master_sw_get, + .put = alc260_hp_master_sw_put, + .private_value = (0x10 << 16) | (0x15 << 8) | 0x11 + }, HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT), HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT), HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("iSpeaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("iSpeaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT), { } /* end */ }; +static struct hda_verb alc260_hp_3013_unsol_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {}, +}; + +static void alc260_hp_3013_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int present; + + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + alc260_hp_master_update(codec, 0x10, 0x15, 0x11); +} + +static void alc260_hp_3013_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC880_HP_EVENT) + alc260_hp_3013_automute(codec); +} + /* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12, * HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10. */ @@ -3812,7 +4068,6 @@ static struct snd_kcontrol_new alc260_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -3831,7 +4086,6 @@ static struct snd_kcontrol_new alc260_capture_alt_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -4332,6 +4586,12 @@ static struct snd_kcontrol_new alc260_test_mixer[] = { ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01), ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01), + /* A switch allowing EAPD to be enabled. Some laptops seem to use + * this output to turn on an external amplifier. + */ + ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02), + ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02), + { } /* end */ }; static struct hda_verb alc260_test_init_verbs[] = { @@ -4417,17 +4677,8 @@ static struct hda_verb alc260_test_init_verbs[] = { }; #endif -static struct hda_pcm_stream alc260_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -static struct hda_pcm_stream alc260_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; +#define alc260_pcm_analog_playback alc880_pcm_analog_alt_playback +#define alc260_pcm_analog_capture alc880_pcm_analog_capture #define alc260_pcm_digital_playback alc880_pcm_digital_playback #define alc260_pcm_digital_capture alc880_pcm_digital_capture @@ -4744,8 +4995,8 @@ static struct snd_pci_quirk alc260_cfg_tbl[] = { SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC), SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X), SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC), - SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL), SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_REPLACER_672V), + SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL), {} }; @@ -4765,10 +5016,11 @@ static struct alc_config_preset alc260_presets[] = { .input_mux = &alc260_capture_source, }, [ALC260_HP] = { - .mixers = { alc260_base_output_mixer, + .mixers = { alc260_hp_output_mixer, alc260_input_mixer, alc260_capture_alt_mixer }, - .init_verbs = { alc260_init_verbs }, + .init_verbs = { alc260_init_verbs, + alc260_hp_unsol_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), @@ -4776,12 +5028,15 @@ static struct alc_config_preset alc260_presets[] = { .num_channel_mode = ARRAY_SIZE(alc260_modes), .channel_mode = alc260_modes, .input_mux = &alc260_capture_source, + .unsol_event = alc260_hp_unsol_event, + .init_hook = alc260_hp_automute, }, [ALC260_HP_3013] = { .mixers = { alc260_hp_3013_mixer, alc260_input_mixer, alc260_capture_alt_mixer }, - .init_verbs = { alc260_hp_3013_init_verbs }, + .init_verbs = { alc260_hp_3013_init_verbs, + alc260_hp_3013_unsol_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), @@ -4789,6 +5044,8 @@ static struct alc_config_preset alc260_presets[] = { .num_channel_mode = ARRAY_SIZE(alc260_modes), .channel_mode = alc260_modes, .input_mux = &alc260_capture_source, + .unsol_event = alc260_hp_3013_unsol_event, + .init_hook = alc260_hp_3013_automute, }, [ALC260_FUJITSU_S702X] = { .mixers = { alc260_fujitsu_mixer, @@ -4906,6 +5163,8 @@ static int patch_alc260(struct hda_codec *codec) spec->stream_digital_playback = &alc260_pcm_digital_playback; spec->stream_digital_capture = &alc260_pcm_digital_capture; + spec->vmaster_nid = 0x08; + codec->patch_ops = alc_patch_ops; if (board_config == ALC260_AUTO) spec->init_hook = alc260_auto_init; @@ -5106,15 +5365,15 @@ static struct snd_kcontrol_new alc882_base_mixer[] = { }; static struct snd_kcontrol_new alc885_mbp3_mixer[] = { - HDA_CODEC_VOLUME("Master Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Master Switch", 0x0c, 0x02, HDA_INPUT), - HDA_CODEC_MUTE ("Speaker Switch", 0x14, 0x00, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Out Volume", 0x0d,0x00, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line In Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE ("Line In Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), + HDA_CODEC_MUTE ("Speaker Playback Switch", 0x14, 0x00, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0d, 0x00, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT), HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Line Boost", 0x1a, 0x00, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT), { } /* end */ }; @@ -5679,7 +5938,6 @@ static struct snd_kcontrol_new alc882_capture_alt_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -5702,7 +5960,6 @@ static struct snd_kcontrol_new alc882_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -5743,16 +6000,17 @@ static const char *alc882_models[ALC882_MODEL_LAST] = { static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */ - SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J), SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J), SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M), + SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC), SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG), SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC), + SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */ + SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), {} }; @@ -5990,7 +6248,7 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec) hda_nid_t nid; nid = spec->autocfg.input_pins[AUTO_PIN_MIC]; - if (nid) { + if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) { err = add_control(spec, ALC_CTL_WIDGET_VOL, "Mic Boost", HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); @@ -5998,7 +6256,7 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec) return err; } nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC]; - if (nid) { + if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) { err = add_control(spec, ALC_CTL_WIDGET_VOL, "Front Mic Boost", HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); @@ -6061,6 +6319,7 @@ static int patch_alc882(struct hda_codec *codec) case 0x106b1000: /* iMac 24 */ board_config = ALC885_IMAC24; break; + case 0x106b00a1: /* Macbook */ case 0x106b2c00: /* Macbook Pro rev3 */ board_config = ALC885_MBP3; break; @@ -6093,6 +6352,9 @@ static int patch_alc882(struct hda_codec *codec) spec->stream_name_analog = "ALC882 Analog"; spec->stream_analog_playback = &alc882_pcm_analog_playback; spec->stream_analog_capture = &alc882_pcm_analog_capture; + /* FIXME: setup DAC5 */ + /*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/ + spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture; spec->stream_name_digital = "ALC882 Digital"; spec->stream_digital_playback = &alc882_pcm_digital_playback; @@ -6117,6 +6379,8 @@ static int patch_alc882(struct hda_codec *codec) } } + spec->vmaster_nid = 0x0c; + codec->patch_ops = alc_patch_ops; if (board_config == ALC882_AUTO) spec->init_hook = alc882_auto_init; @@ -6340,6 +6604,36 @@ static struct snd_kcontrol_new alc883_base_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc883_mitac_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -6508,8 +6802,8 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = { static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), @@ -6658,6 +6952,46 @@ static struct snd_kcontrol_new alc888_3st_hp_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc888_6st_dell_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -6772,6 +7106,67 @@ static struct hda_verb alc883_init_verbs[] = { { } }; +/* toggle speaker-output according to the hp-jack state */ +static void alc883_mitac_hp_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + +/* auto-toggle front mic */ +/* +static void alc883_mitac_mic_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned char bits; + + present = snd_hda_codec_read(codec, 0x18, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); +} +*/ + +static void alc883_mitac_automute(struct hda_codec *codec) +{ + alc883_mitac_hp_automute(codec); + /* alc883_mitac_mic_automute(codec); */ +} + +static void alc883_mitac_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC880_HP_EVENT: + alc883_mitac_hp_automute(codec); + break; + case ALC880_MIC_EVENT: + /* alc883_mitac_mic_automute(codec); */ + break; + } +} + +static struct hda_verb alc883_mitac_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Subwoofer */ + {0x17, AC_VERB_SET_CONNECT_SEL, 0x02}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* enable unsolicited event */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + /* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, */ + + { } /* end */ +}; + static struct hda_verb alc883_tagra_verbs[] = { {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, @@ -6843,6 +7238,15 @@ static struct hda_verb alc888_3st_hp_verbs[] = { { } }; +static struct hda_verb alc888_6st_dell_verbs[] = { + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Rear : output 1 (0x0e) */ + {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* CLFE : output 2 (0x0d) */ + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Side : output 3 (0x0f) */ + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + { } +}; + static struct hda_verb alc888_3st_hp_2ch_init[] = { { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, @@ -7038,6 +7442,33 @@ static struct hda_verb alc883_acer_eapd_verbs[] = { { } }; +static void alc888_6st_dell_front_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + +static void alc888_6st_dell_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC880_HP_EVENT: + printk("hp_event\n"); + alc888_6st_dell_front_automute(codec); + break; + } +} + /* * generic initialization of ADC, input mixers and output mixers */ @@ -7096,7 +7527,7 @@ static struct hda_verb alc883_auto_init_verbs[] = { {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, { } }; @@ -7111,7 +7542,6 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -7130,6 +7560,7 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = { /* pcm configuration: identiacal with ALC880 */ #define alc883_pcm_analog_playback alc880_pcm_analog_playback #define alc883_pcm_analog_capture alc880_pcm_analog_capture +#define alc883_pcm_analog_alt_capture alc880_pcm_analog_alt_capture #define alc883_pcm_digital_playback alc880_pcm_digital_playback #define alc883_pcm_digital_capture alc880_pcm_digital_capture @@ -7154,53 +7585,58 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { [ALC883_HAIER_W66] = "haier-w66", [ALC888_6ST_HP] = "6stack-hp", [ALC888_3ST_HP] = "3stack-hp", + [ALC888_6ST_DELL] = "6stack-dell", + [ALC883_MITAC] = "mitac", [ALC883_AUTO] = "auto", }; static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */ + SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL), SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), - SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD), + SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP), + SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC), + SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), + SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), SND_PCI_QUIRK(0x1458, 0xa002, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG), - SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG), - SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), + SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD), SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), - SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), - SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch), SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch), - SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763), SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763), - SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP), - SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), - SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), + SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763), + SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763), SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2), SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66), - SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763), - SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), - SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch), {} }; @@ -7435,6 +7871,34 @@ static struct alc_config_preset alc883_presets[] = { .need_dac_fix = 1, .input_mux = &alc883_capture_source, }, + [ALC888_6ST_DELL] = { + .mixers = { alc888_6st_dell_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc888_6st_dell_unsol_event, + .init_hook = alc888_6st_dell_front_automute, + }, + [ALC883_MITAC] = { + .mixers = { alc883_mitac_mixer }, + .init_verbs = { alc883_init_verbs, alc883_mitac_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_mitac_unsol_event, + .init_hook = alc883_mitac_automute, + }, }; @@ -7582,6 +8046,7 @@ static int patch_alc883(struct hda_codec *codec) spec->stream_name_analog = "ALC883 Analog"; spec->stream_analog_playback = &alc883_pcm_analog_playback; spec->stream_analog_capture = &alc883_pcm_analog_capture; + spec->stream_analog_alt_capture = &alc883_pcm_analog_alt_capture; spec->stream_name_digital = "ALC883 Digital"; spec->stream_digital_playback = &alc883_pcm_digital_playback; @@ -7592,6 +8057,8 @@ static int patch_alc883(struct hda_codec *codec) spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); } + spec->vmaster_nid = 0x0c; + codec->patch_ops = alc_patch_ops; if (board_config == ALC883_AUTO) spec->init_hook = alc883_auto_init; @@ -7659,13 +8126,99 @@ static struct snd_kcontrol_new alc262_hippo1_mixer[] = { { } /* end */ }; +/* update HP, line and mono-out pins according to the master switch */ +static void alc262_hp_master_update(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int val = spec->master_sw; + + /* HP & line-out */ + snd_hda_codec_write_cache(codec, 0x1b, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + val ? PIN_HP : 0); + snd_hda_codec_write_cache(codec, 0x15, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + val ? PIN_HP : 0); + /* mono (speaker) depending on the HP jack sense */ + val = val && !spec->jack_present; + snd_hda_codec_write_cache(codec, 0x16, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + val ? PIN_OUT : 0); +} + +static void alc262_hp_bpc_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int presence; + presence = snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE); + alc262_hp_master_update(codec); +} + +static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res) +{ + if ((res >> 26) != ALC880_HP_EVENT) + return; + alc262_hp_bpc_automute(codec); +} + +static void alc262_hp_wildwest_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int presence; + presence = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE); + alc262_hp_master_update(codec); +} + +static void alc262_hp_wildwest_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC880_HP_EVENT) + return; + alc262_hp_wildwest_automute(codec); +} + +static int alc262_hp_master_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + *ucontrol->value.integer.value = spec->master_sw; + return 0; +} + +static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int val = !!*ucontrol->value.integer.value; + + if (val == spec->master_sw) + return 0; + spec->master_sw = val; + alc262_hp_master_update(codec); + return 1; +} + static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = alc262_hp_master_sw_get, + .put = alc262_hp_master_sw_put, + }, HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), - + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0, + HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0, + HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), @@ -7684,12 +8237,21 @@ static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { }; static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = alc262_hp_master_sw_get, + .put = alc262_hp_master_sw_put, + }, HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0, + HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0, + HDA_OUTPUT), HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Boost", 0x1a, 0, HDA_INPUT), @@ -7709,6 +8271,85 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = { { } /* end */ }; +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_hp_t5735_automute(struct hda_codec *codec, int force) +{ + struct alc_spec *spec = codec->spec; + + if (force || !spec->sense_updated) { + unsigned int present; + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + spec->sense_updated = 1; + } + snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, HDA_AMP_MUTE, + spec->jack_present ? HDA_AMP_MUTE : 0); +} + +static void alc262_hp_t5735_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC880_HP_EVENT) + return; + alc262_hp_t5735_automute(codec, 1); +} + +static void alc262_hp_t5735_init_hook(struct hda_codec *codec) +{ + alc262_hp_t5735_automute(codec, 1); +} + +static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + { } /* end */ +}; + +static struct hda_verb alc262_hp_t5735_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + { } +}; + +static struct snd_kcontrol_new alc262_hp_rp5700_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0e, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x16, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), + { } /* end */ +}; + +static struct hda_verb alc262_hp_rp5700_verbs[] = { + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))}, + {} +}; + +static struct hda_input_mux alc262_hp_rp5700_capture_source = { + .num_items = 1, + .items = { + { "Line", 0x1 }, + }, +}; + /* bind hp and internal speaker mute (with plug check) */ static int alc262_sony_master_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -8082,6 +8723,72 @@ static struct hda_verb alc262_benq_t31_EAPD_verbs[] = { {} }; +/* Samsung Q1 Ultra Vista model setup */ +static struct snd_kcontrol_new alc262_ultra_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +static struct hda_verb alc262_ultra_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* Mic is on Node 0x19 */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x23, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x24, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {} +}; + +static struct hda_input_mux alc262_ultra_capture_source = { + .num_items = 1, + .items = { + { "Mic", 0x1 }, + }, +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_ultra_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int mute; + unsigned int present; + + /* need to execute and sync at first */ + snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & 0x80000000) != 0; + if (spec->jack_present) { + /* mute internal speaker */ + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + } else { + /* unmute internal speaker if necessary */ + mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + } +} + +/* unsolicited event for HP jack sensing */ +static void alc262_ultra_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC880_HP_EVENT) + return; + alc262_ultra_automute(codec); +} + /* add playback controls from the parsed DAC table */ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) @@ -8269,7 +8976,7 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = { {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, @@ -8311,6 +9018,8 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = { {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + { } }; @@ -8405,6 +9114,8 @@ static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = { /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + { } }; @@ -8484,39 +9195,49 @@ static const char *alc262_models[ALC262_MODEL_LAST] = { [ALC262_FUJITSU] = "fujitsu", [ALC262_HP_BPC] = "hp-bpc", [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000", + [ALC262_HP_TC_T5735] = "hp-tc-t5735", + [ALC262_HP_RP5700] = "hp-rp5700", [ALC262_BENQ_ED8] = "benq", [ALC262_BENQ_T31] = "benq-t31", [ALC262_SONY_ASSAMD] = "sony-assamd", + [ALC262_ULTRA] = "ultra", [ALC262_AUTO] = "auto", }; static struct snd_pci_quirk alc262_cfg_tbl[] = { SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO), SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC), SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC), SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x1309, "HP xw4*00", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x130a, "HP xw6*00", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x130b, "HP xw8*00", ALC262_HP_BPC), SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL), - SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL), - SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL), - SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL), SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL), SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL), SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL), SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x302f, "HP Thin Client T5735", + ALC262_HP_TC_T5735), + SND_PCI_QUIRK(0x103c, 0x2817, "HP RP5700", ALC262_HP_RP5700), + SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD), SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO), + SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD), + SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD), + SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD), SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), - SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), + SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA), SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8), SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31), - SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD), - SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD), - SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD), - SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD), + SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), {} }; @@ -8579,6 +9300,8 @@ static struct alc_config_preset alc262_presets[] = { .num_channel_mode = ARRAY_SIZE(alc262_modes), .channel_mode = alc262_modes, .input_mux = &alc262_HP_capture_source, + .unsol_event = alc262_hp_bpc_unsol_event, + .init_hook = alc262_hp_bpc_automute, }, [ALC262_HP_BPC_D7000_WF] = { .mixers = { alc262_HP_BPC_WildWest_mixer }, @@ -8589,6 +9312,8 @@ static struct alc_config_preset alc262_presets[] = { .num_channel_mode = ARRAY_SIZE(alc262_modes), .channel_mode = alc262_modes, .input_mux = &alc262_HP_D7000_capture_source, + .unsol_event = alc262_hp_wildwest_unsol_event, + .init_hook = alc262_hp_wildwest_automute, }, [ALC262_HP_BPC_D7000_WL] = { .mixers = { alc262_HP_BPC_WildWest_mixer, @@ -8600,7 +9325,30 @@ static struct alc_config_preset alc262_presets[] = { .num_channel_mode = ARRAY_SIZE(alc262_modes), .channel_mode = alc262_modes, .input_mux = &alc262_HP_D7000_capture_source, + .unsol_event = alc262_hp_wildwest_unsol_event, + .init_hook = alc262_hp_wildwest_automute, + }, + [ALC262_HP_TC_T5735] = { + .mixers = { alc262_hp_t5735_mixer }, + .init_verbs = { alc262_init_verbs, alc262_hp_t5735_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc262_hp_t5735_unsol_event, + .init_hook = alc262_hp_t5735_init_hook, }, + [ALC262_HP_RP5700] = { + .mixers = { alc262_hp_rp5700_mixer }, + .init_verbs = { alc262_init_verbs, alc262_hp_rp5700_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_hp_rp5700_capture_source, + }, [ALC262_BENQ_ED8] = { .mixers = { alc262_base_mixer }, .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs }, @@ -8635,6 +9383,19 @@ static struct alc_config_preset alc262_presets[] = { .unsol_event = alc262_hippo_unsol_event, .init_hook = alc262_hippo_automute, }, + [ALC262_ULTRA] = { + .mixers = { alc262_ultra_mixer }, + .init_verbs = { alc262_init_verbs, alc262_ultra_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_ultra_capture_source, + .unsol_event = alc262_ultra_unsol_event, + .init_hook = alc262_ultra_automute, + }, }; static int patch_alc262(struct hda_codec *codec) @@ -8716,6 +9477,8 @@ static int patch_alc262(struct hda_codec *codec) } } + spec->vmaster_nid = 0x0c; + codec->patch_ops = alc_patch_ops; if (board_config == ALC262_AUTO) spec->init_hook = alc262_auto_init; @@ -8873,6 +9636,49 @@ static void alc268_acer_init_hook(struct hda_codec *codec) alc268_acer_automute(codec, 1); } +static struct snd_kcontrol_new alc268_dell_mixer[] = { + /* output mixer control */ + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT), + { } +}; + +static struct hda_verb alc268_dell_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + { } +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc268_dell_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned int mute; + + present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0); + if (present & 0x80000000) + mute = HDA_AMP_MUTE; + else + mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); +} + +static void alc268_dell_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC880_HP_EVENT) + return; + alc268_dell_automute(codec); +} + +#define alc268_dell_init_hook alc268_dell_automute + /* * generic initialization of ADC, input mixers and output mixers */ @@ -8915,19 +9721,13 @@ static struct hda_verb alc268_base_init_verbs[] = { {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1c, 14, 15, 0b */ - /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + /* Unmute Selector 23h,24h and set the default input to mic-in */ + + {0x23, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x24, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, { } }; @@ -8972,29 +9772,14 @@ static int alc268_mux_enum_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux = spec->input_mux; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); static hda_nid_t capture_mixers[3] = { 0x23, 0x24 }; hda_nid_t nid = capture_mixers[adc_idx]; - unsigned int *cur_val = &spec->cur_mux[adc_idx]; - unsigned int i, idx; - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (*cur_val == idx) - return 0; - for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, - imux->items[i].index, - HDA_AMP_MUTE, v); - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - idx ); - } - *cur_val = idx; - return 1; + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, + nid, + &spec->cur_mux[adc_idx]); } static struct snd_kcontrol_new alc268_capture_alt_mixer[] = { @@ -9004,7 +9789,6 @@ static struct snd_kcontrol_new alc268_capture_alt_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -9025,7 +9809,6 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -9047,6 +9830,61 @@ static struct hda_input_mux alc268_capture_source = { }, }; +#ifdef CONFIG_SND_DEBUG +static struct snd_kcontrol_new alc268_test_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + + /* Volume widgets */ + HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Mono sum Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE("LINE-OUT sum Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_BIND_MUTE("HP-OUT sum Playback Switch", 0x10, 2, HDA_INPUT), + HDA_BIND_MUTE("LINE-OUT Playback Switch", 0x14, 2, HDA_OUTPUT), + HDA_BIND_MUTE("HP-OUT Playback Switch", 0x15, 2, HDA_OUTPUT), + HDA_BIND_MUTE("Mono Playback Switch", 0x16, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("MIC1 Capture Volume", 0x18, 0x0, HDA_INPUT), + HDA_BIND_MUTE("MIC1 Capture Switch", 0x18, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("MIC2 Capture Volume", 0x19, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("LINE1 Capture Volume", 0x1a, 0x0, HDA_INPUT), + HDA_BIND_MUTE("LINE1 Capture Switch", 0x1a, 2, HDA_OUTPUT), + /* The below appears problematic on some hardwares */ + /*HDA_CODEC_VOLUME("PCBEEP Playback Volume", 0x1d, 0x0, HDA_INPUT),*/ + HDA_CODEC_VOLUME("PCM-IN1 Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("PCM-IN1 Capture Switch", 0x23, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM-IN2 Capture Volume", 0x24, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("PCM-IN2 Capture Switch", 0x24, 2, HDA_OUTPUT), + + /* Modes for retasking pin widgets */ + ALC_PIN_MODE("LINE-OUT pin mode", 0x14, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("HP-OUT pin mode", 0x15, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("MIC1 pin mode", 0x18, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("LINE1 pin mode", 0x1a, ALC_PIN_DIR_INOUT), + + /* Controls for GPIO pins, assuming they are configured as outputs */ + ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01), + ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02), + ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04), + ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08), + + /* Switches to allow the digital SPDIF output pin to be enabled. + * The ALC268 does not have an SPDIF input. + */ + ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x06, 0x01), + + /* A switch allowing EAPD to be enabled. Some laptops seem to use + * this output to turn on an external amplifier. + */ + ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02), + ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02), + + { } /* end */ +}; +#endif + /* create input playback/capture controls for the given pin */ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, const char *ctlname, int idx) @@ -9194,6 +10032,7 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec) /* pcm configuration: identiacal with ALC880 */ #define alc268_pcm_analog_playback alc880_pcm_analog_playback #define alc268_pcm_analog_capture alc880_pcm_analog_capture +#define alc268_pcm_analog_alt_capture alc880_pcm_analog_alt_capture #define alc268_pcm_digital_playback alc880_pcm_digital_playback /* @@ -9259,16 +10098,23 @@ static const char *alc268_models[ALC268_MODEL_LAST] = { [ALC268_3ST] = "3stack", [ALC268_TOSHIBA] = "toshiba", [ALC268_ACER] = "acer", + [ALC268_DELL] = "dell", +#ifdef CONFIG_SND_DEBUG + [ALC268_TEST] = "test", +#endif [ALC268_AUTO] = "auto", }; static struct snd_pci_quirk alc268_cfg_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER), + SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER), + SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER), + SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER), + SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL), + SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA), SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST), SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA), SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA), - SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA), - SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER), - SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER), SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER), {} }; @@ -9317,6 +10163,35 @@ static struct alc_config_preset alc268_presets[] = { .unsol_event = alc268_acer_unsol_event, .init_hook = alc268_acer_init_hook, }, + [ALC268_DELL] = { + .mixers = { alc268_dell_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_dell_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .hp_nid = 0x02, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .unsol_event = alc268_dell_unsol_event, + .init_hook = alc268_dell_init_hook, + .input_mux = &alc268_capture_source, + }, +#ifdef CONFIG_SND_DEBUG + [ALC268_TEST] = { + .mixers = { alc268_test_mixer, alc268_capture_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_volume_init_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .hp_nid = 0x03, + .dig_out_nid = ALC268_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + }, +#endif }; static int patch_alc268(struct hda_codec *codec) @@ -9361,34 +10236,34 @@ static int patch_alc268(struct hda_codec *codec) spec->stream_name_analog = "ALC268 Analog"; spec->stream_analog_playback = &alc268_pcm_analog_playback; spec->stream_analog_capture = &alc268_pcm_analog_capture; + spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture; spec->stream_name_digital = "ALC268 Digital"; spec->stream_digital_playback = &alc268_pcm_digital_playback; - if (board_config == ALC268_AUTO) { - if (!spec->adc_nids && spec->input_mux) { - /* check whether NID 0x07 is valid */ - unsigned int wcap = get_wcaps(codec, 0x07); - - /* get type */ - wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - if (wcap != AC_WID_AUD_IN) { - spec->adc_nids = alc268_adc_nids_alt; - spec->num_adc_nids = - ARRAY_SIZE(alc268_adc_nids_alt); - spec->mixers[spec->num_mixers] = + if (!spec->adc_nids && spec->input_mux) { + /* check whether NID 0x07 is valid */ + unsigned int wcap = get_wcaps(codec, 0x07); + + /* get type */ + wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + if (wcap != AC_WID_AUD_IN) { + spec->adc_nids = alc268_adc_nids_alt; + spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt); + spec->mixers[spec->num_mixers] = alc268_capture_alt_mixer; - spec->num_mixers++; - } else { - spec->adc_nids = alc268_adc_nids; - spec->num_adc_nids = - ARRAY_SIZE(alc268_adc_nids); - spec->mixers[spec->num_mixers] = - alc268_capture_mixer; - spec->num_mixers++; - } + spec->num_mixers++; + } else { + spec->adc_nids = alc268_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids); + spec->mixers[spec->num_mixers] = + alc268_capture_mixer; + spec->num_mixers++; } } + + spec->vmaster_nid = 0x02; + codec->patch_ops = alc_patch_ops; if (board_config == ALC268_AUTO) spec->init_hook = alc268_auto_init; @@ -9397,6 +10272,360 @@ static int patch_alc268(struct hda_codec *codec) } /* + * ALC269 channel source setting (2 channel) + */ +#define ALC269_DIGOUT_NID ALC880_DIGOUT_NID + +#define alc269_dac_nids alc260_dac_nids + +static hda_nid_t alc269_adc_nids[1] = { + /* ADC1 */ + 0x07, +}; + +#define alc269_modes alc260_modes +#define alc269_capture_source alc880_lg_lw_capture_source + +static struct snd_kcontrol_new alc269_base_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +/* capture mixer elements */ +static struct snd_kcontrol_new alc269_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 1, + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, + }, + { } /* end */ +}; + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb alc269_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Mute input amps (PCBeep, Line In, Mic 1 & Mic 2) of the + * analog-loopback mixer widget + * Note: PASD motherboards uses the Line In 2 as the input for + * front panel mic (mic 2) + */ + /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + + /* + * Set up output mixers (0x0c - 0x0e) + */ + /* set vol=0 to output mixers */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* set up input amps for analog loopback */ + /* Amp Indices: DAC = 0, mixer = 1 */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + + /* set EAPD */ + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +/* add playback controls from the parsed DAC table */ +static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec, + const struct auto_pin_cfg *cfg) +{ + hda_nid_t nid; + int err; + + spec->multiout.num_dacs = 1; /* only use one dac */ + spec->multiout.dac_nids = spec->private_dac_nids; + spec->multiout.dac_nids[0] = 2; + + nid = cfg->line_out_pins[0]; + if (nid) { + err = add_control(spec, ALC_CTL_WIDGET_VOL, + "Front Playback Volume", + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = add_control(spec, ALC_CTL_WIDGET_MUTE, + "Front Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + } + + nid = cfg->speaker_pins[0]; + if (nid) { + if (!cfg->line_out_pins[0]) { + err = add_control(spec, ALC_CTL_WIDGET_VOL, + "Speaker Playback Volume", + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + if (nid == 0x16) { + err = add_control(spec, ALC_CTL_WIDGET_MUTE, + "Speaker Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else { + err = add_control(spec, ALC_CTL_WIDGET_MUTE, + "Speaker Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + } + nid = cfg->hp_pins[0]; + if (nid) { + /* spec->multiout.hp_nid = 2; */ + if (!cfg->line_out_pins[0] && !cfg->speaker_pins[0]) { + err = add_control(spec, ALC_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + if (nid == 0x16) { + err = add_control(spec, ALC_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else { + err = add_control(spec, ALC_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + } + return 0; +} + +#define alc269_auto_create_analog_input_ctls \ + alc880_auto_create_analog_input_ctls + +#ifdef CONFIG_SND_HDA_POWER_SAVE +#define alc269_loopbacks alc880_loopbacks +#endif + +/* pcm configuration: identiacal with ALC880 */ +#define alc269_pcm_analog_playback alc880_pcm_analog_playback +#define alc269_pcm_analog_capture alc880_pcm_analog_capture +#define alc269_pcm_digital_playback alc880_pcm_digital_playback +#define alc269_pcm_digital_capture alc880_pcm_digital_capture + +/* + * BIOS auto configuration + */ +static int alc269_parse_auto_config(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int err; + static hda_nid_t alc269_ignore[] = { 0x1d, 0 }; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, + alc269_ignore); + if (err < 0) + return err; + + err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = alc269_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + if (spec->autocfg.dig_out_pin) + spec->multiout.dig_out_nid = ALC269_DIGOUT_NID; + + if (spec->kctl_alloc) + spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + + spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs; + spec->num_mux_defs = 1; + spec->input_mux = &spec->private_imux; + + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; + + return 1; +} + +#define alc269_auto_init_multi_out alc882_auto_init_multi_out +#define alc269_auto_init_hp_out alc882_auto_init_hp_out +#define alc269_auto_init_analog_input alc882_auto_init_analog_input + + +/* init callback for auto-configuration model -- overriding the default init */ +static void alc269_auto_init(struct hda_codec *codec) +{ + alc269_auto_init_multi_out(codec); + alc269_auto_init_hp_out(codec); + alc269_auto_init_analog_input(codec); +} + +/* + * configuration and preset + */ +static const char *alc269_models[ALC269_MODEL_LAST] = { + [ALC269_BASIC] = "basic", +}; + +static struct snd_pci_quirk alc269_cfg_tbl[] = { + {} +}; + +static struct alc_config_preset alc269_presets[] = { + [ALC269_BASIC] = { + .mixers = { alc269_base_mixer }, + .init_verbs = { alc269_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .input_mux = &alc269_capture_source, + }, +}; + +static int patch_alc269(struct hda_codec *codec) +{ + struct alc_spec *spec; + int board_config; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST, + alc269_models, + alc269_cfg_tbl); + + if (board_config < 0) { + printk(KERN_INFO "hda_codec: Unknown model for ALC269, " + "trying auto-probe from BIOS...\n"); + board_config = ALC269_AUTO; + } + + if (board_config == ALC269_AUTO) { + /* automatic parse from the BIOS config */ + err = alc269_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); + board_config = ALC269_BASIC; + } + } + + if (board_config != ALC269_AUTO) + setup_preset(spec, &alc269_presets[board_config]); + + spec->stream_name_analog = "ALC269 Analog"; + spec->stream_analog_playback = &alc269_pcm_analog_playback; + spec->stream_analog_capture = &alc269_pcm_analog_capture; + + spec->stream_name_digital = "ALC269 Digital"; + spec->stream_digital_playback = &alc269_pcm_digital_playback; + spec->stream_digital_capture = &alc269_pcm_digital_capture; + + spec->adc_nids = alc269_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); + spec->mixers[spec->num_mixers] = alc269_capture_mixer; + spec->num_mixers++; + + codec->patch_ops = alc_patch_ops; + if (board_config == ALC269_AUTO) + spec->init_hook = alc269_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc269_loopbacks; +#endif + + return 0; +} + +/* * ALC861 channel source setting (2/6 channel selection for 3-stack) */ @@ -10213,7 +11442,6 @@ static struct snd_kcontrol_new alc861_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - *FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -10369,22 +11597,23 @@ static struct snd_pci_quirk alc861_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST), SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP), SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP), - SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP), SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS), + SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP), SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG), SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA), /* FIXME: the entry below breaks Toshiba A100 (model=auto works!) * Any other models that need this preset? */ /* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */ - SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31), - SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), + SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST), + SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST), SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31), + SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31), + SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP), + /* FIXME: the below seems conflict */ + /* SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), */ SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST), SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST), - SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST), - SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST), {} }; @@ -10543,6 +11772,8 @@ static int patch_alc861(struct hda_codec *codec) spec->stream_digital_playback = &alc861_pcm_digital_playback; spec->stream_digital_capture = &alc861_pcm_digital_capture; + spec->vmaster_nid = 0x03; + codec->patch_ops = alc_patch_ops; if (board_config == ALC861_AUTO) spec->init_hook = alc861_auto_init; @@ -10697,7 +11928,6 @@ static struct snd_kcontrol_new alc861vd_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - *FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -11102,21 +12332,20 @@ static const char *alc861vd_models[ALC861VD_MODEL_LAST] = { }; static struct snd_pci_quirk alc861vd_cfg_tbl[] = { + SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST), + SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP), SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST), SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST), SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG), SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST), - SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST), - + SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO), /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/ SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS), - SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO), - SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO), - SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO), SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO), SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG), + SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO), SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG), - SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP), {} }; @@ -11520,6 +12749,8 @@ static int patch_alc861vd(struct hda_codec *codec) spec->mixers[spec->num_mixers] = alc861vd_capture_mixer; spec->num_mixers++; + spec->vmaster_nid = 0x02; + codec->patch_ops = alc_patch_ops; if (board_config == ALC861VD_AUTO) @@ -11699,18 +12930,6 @@ static struct snd_kcontrol_new alc662_base_mixer[] = { HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT), - - /* Capture mixer control */ - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .count = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, { } /* end */ }; @@ -11728,17 +12947,6 @@ static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = alc662_mux_enum_info, - .get = alc662_mux_enum_get, - .put = alc662_mux_enum_put, - }, { } /* end */ }; @@ -11762,46 +12970,24 @@ static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = { HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = alc662_mux_enum_info, - .get = alc662_mux_enum_get, - .put = alc662_mux_enum_put, - }, { } /* end */ }; static struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), - HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("iSpeaker Playback Switch", 0x03, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x03, 2, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = alc662_mux_enum_info, - .get = alc662_mux_enum_get, - .put = alc662_mux_enum_put, - }, { } /* end */ }; static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = { - HDA_CODEC_MUTE("iSpeaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("LineOut Playback Volume", 0x02, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("LineOut Playback Switch", 0x1b, 0x0, HDA_OUTPUT), @@ -11816,6 +13002,24 @@ static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = { + HDA_CODEC_VOLUME("LineOut Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("LineOut Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + static struct snd_kcontrol_new alc662_chmode_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -11901,6 +13105,13 @@ static struct hda_verb alc662_eeepc_sue_init_verbs[] = { {} }; +/* Set Unsolicited Event*/ +static struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = { + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {} +}; + /* * generic initialization of ADC, input mixers and output mixers */ @@ -11957,14 +13168,13 @@ static struct snd_kcontrol_new alc662_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", .count = 1, - .info = alc882_mux_enum_info, - .get = alc882_mux_enum_get, - .put = alc882_mux_enum_put, + .info = alc662_mux_enum_info, + .get = alc662_mux_enum_get, + .put = alc662_mux_enum_put, }, { } /* end */ }; @@ -12037,6 +13247,40 @@ static void alc662_eeepc_inithook(struct hda_codec *codec) alc662_eeepc_mic_automute(codec); } +static void alc662_eeepc_ep20_automute(struct hda_codec *codec) +{ + unsigned int mute; + unsigned int present; + + snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, 0x14, 0, + AC_VERB_GET_PIN_SENSE, 0); + present = (present & 0x80000000) != 0; + if (present) { + /* mute internal speaker */ + snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + } else { + /* unmute internal speaker if necessary */ + mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0); + snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + } +} + +/* unsolicited event for HP jack sensing */ +static void alc662_eeepc_ep20_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC880_HP_EVENT) + alc662_eeepc_ep20_automute(codec); +} + +static void alc662_eeepc_ep20_inithook(struct hda_codec *codec) +{ + alc662_eeepc_ep20_automute(codec); +} + #ifdef CONFIG_SND_HDA_POWER_SAVE #define alc662_loopbacks alc880_loopbacks #endif @@ -12057,12 +13301,15 @@ static const char *alc662_models[ALC662_MODEL_LAST] = { [ALC662_3ST_6ch] = "3stack-6ch", [ALC662_5ST_DIG] = "6stack-dig", [ALC662_LENOVO_101E] = "lenovo-101e", + [ALC662_ASUS_EEEPC_P701] = "eeepc-p701", + [ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20", [ALC662_AUTO] = "auto", }; static struct snd_pci_quirk alc662_cfg_tbl[] = { - SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701), + SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20), + SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), {} }; @@ -12149,6 +13396,21 @@ static struct alc_config_preset alc662_presets[] = { .unsol_event = alc662_eeepc_unsol_event, .init_hook = alc662_eeepc_inithook, }, + [ALC662_ASUS_EEEPC_EP20] = { + .mixers = { alc662_eeepc_ep20_mixer, alc662_capture_mixer, + alc662_chmode_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eeepc_ep20_sue_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), + .adc_nids = alc662_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), + .channel_mode = alc662_3ST_6ch_modes, + .input_mux = &alc662_lenovo_101e_capture_source, + .unsol_event = alc662_eeepc_ep20_unsol_event, + .init_hook = alc662_eeepc_ep20_inithook, + }, }; @@ -12308,6 +13570,7 @@ static void alc662_auto_init_multi_out(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int i; + alc_subsystem_id(codec, 0x15, 0x1b, 0x14); for (i = 0; i <= HDA_SIDE; i++) { hda_nid_t nid = spec->autocfg.line_out_pins[i]; int pin_type = get_pin_type(spec->autocfg.line_out_type); @@ -12458,6 +13721,8 @@ static int patch_alc662(struct hda_codec *codec) spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids); } + spec->vmaster_nid = 0x02; + codec->patch_ops = alc_patch_ops; if (board_config == ALC662_AUTO) spec->init_hook = alc662_auto_init; @@ -12475,7 +13740,9 @@ static int patch_alc662(struct hda_codec *codec) struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, + { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 }, { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 }, + { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 }, { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", .patch = patch_alc861 }, { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, @@ -12490,5 +13757,6 @@ struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 }, { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, + { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index 2a4b9609aa5..d22f5a6b850 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -22,7 +22,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -287,7 +286,6 @@ static int patch_si3054(struct hda_codec *codec) struct hda_codec_preset snd_hda_preset_si3054[] = { { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 }, - { .id = 0x11c11040, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 }, diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 04012237096..caf48edaa92 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -24,7 +24,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -35,7 +34,8 @@ #include "hda_local.h" #define NUM_CONTROL_ALLOC 32 -#define STAC_HP_EVENT 0x37 +#define STAC_PWR_EVENT 0x20 +#define STAC_HP_EVENT 0x30 enum { STAC_REF, @@ -62,6 +62,16 @@ enum { }; enum { + STAC_92HD73XX_REF, + STAC_92HD73XX_MODELS +}; + +enum { + STAC_92HD71BXX_REF, + STAC_92HD71BXX_MODELS +}; + +enum { STAC_925x_REF, STAC_M2_2, STAC_MA6, @@ -97,6 +107,7 @@ enum { STAC_D965_3ST, STAC_D965_5ST, STAC_DELL_3ST, + STAC_DELL_BIOS, STAC_927X_MODELS }; @@ -110,11 +121,24 @@ struct sigmatel_spec { unsigned int mic_switch: 1; unsigned int alt_switch: 1; unsigned int hp_detect: 1; - unsigned int gpio_mute: 1; - unsigned int gpio_mask, gpio_data; + /* gpio lines */ + unsigned int gpio_mask; + unsigned int gpio_dir; + unsigned int gpio_data; + unsigned int gpio_mute; + + /* analog loopback */ + unsigned char aloopback_mask; + unsigned char aloopback_shift; + + /* power management */ + unsigned int num_pwrs; + hda_nid_t *pwr_nids; /* playback */ + struct hda_input_mux *mono_mux; + unsigned int cur_mmux; struct hda_multi_out multiout; hda_nid_t dac_nids[5]; @@ -125,8 +149,10 @@ struct sigmatel_spec { unsigned int num_muxes; hda_nid_t *dmic_nids; unsigned int num_dmics; - hda_nid_t dmux_nid; + hda_nid_t *dmux_nids; + unsigned int num_dmuxes; hda_nid_t dig_in_nid; + hda_nid_t mono_nid; /* pin widgets */ hda_nid_t *pin_nids; @@ -140,7 +166,7 @@ struct sigmatel_spec { /* capture source */ struct hda_input_mux *dinput_mux; - unsigned int cur_dmux; + unsigned int cur_dmux[2]; struct hda_input_mux *input_mux; unsigned int cur_mux[3]; @@ -157,6 +183,10 @@ struct sigmatel_spec { struct snd_kcontrol_new *kctl_alloc; struct hda_input_mux private_dimux; struct hda_input_mux private_imux; + struct hda_input_mux private_mono_mux; + + /* virtual master */ + unsigned int vmaster_tlv[4]; }; static hda_nid_t stac9200_adc_nids[1] = { @@ -171,6 +201,58 @@ static hda_nid_t stac9200_dac_nids[1] = { 0x02, }; +static hda_nid_t stac92hd73xx_pwr_nids[8] = { + 0x0a, 0x0b, 0x0c, 0xd, 0x0e, + 0x0f, 0x10, 0x11 +}; + +static hda_nid_t stac92hd73xx_adc_nids[2] = { + 0x1a, 0x1b +}; + +#define STAC92HD73XX_NUM_DMICS 2 +static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = { + 0x13, 0x14, 0 +}; + +#define STAC92HD73_DAC_COUNT 5 +static hda_nid_t stac92hd73xx_dac_nids[STAC92HD73_DAC_COUNT] = { + 0x15, 0x16, 0x17, 0x18, 0x19, +}; + +static hda_nid_t stac92hd73xx_mux_nids[4] = { + 0x28, 0x29, 0x2a, 0x2b, +}; + +static hda_nid_t stac92hd73xx_dmux_nids[2] = { + 0x20, 0x21, +}; + +static hda_nid_t stac92hd71bxx_pwr_nids[3] = { + 0x0a, 0x0d, 0x0f +}; + +static hda_nid_t stac92hd71bxx_adc_nids[2] = { + 0x12, 0x13, +}; + +static hda_nid_t stac92hd71bxx_mux_nids[2] = { + 0x1a, 0x1b +}; + +static hda_nid_t stac92hd71bxx_dmux_nids[1] = { + 0x1c, +}; + +static hda_nid_t stac92hd71bxx_dac_nids[2] = { + 0x10, /*0x11, */ +}; + +#define STAC92HD71BXX_NUM_DMICS 2 +static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = { + 0x18, 0x19, 0 +}; + static hda_nid_t stac925x_adc_nids[1] = { 0x03, }; @@ -188,6 +270,10 @@ static hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = { 0x15, 0 }; +static hda_nid_t stac925x_dmux_nids[1] = { + 0x14, +}; + static hda_nid_t stac922x_adc_nids[2] = { 0x06, 0x07, }; @@ -204,6 +290,15 @@ static hda_nid_t stac927x_mux_nids[3] = { 0x15, 0x16, 0x17 }; +static hda_nid_t stac927x_dmux_nids[1] = { + 0x1b, +}; + +#define STAC927X_NUM_DMICS 2 +static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = { + 0x13, 0x14, 0 +}; + static hda_nid_t stac9205_adc_nids[2] = { 0x12, 0x13 }; @@ -212,6 +307,10 @@ static hda_nid_t stac9205_mux_nids[2] = { 0x19, 0x1a }; +static hda_nid_t stac9205_dmux_nids[1] = { + 0x1d, +}; + #define STAC9205_NUM_DMICS 2 static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = { 0x17, 0x18, 0 @@ -232,6 +331,17 @@ static hda_nid_t stac922x_pin_nids[10] = { 0x0f, 0x10, 0x11, 0x15, 0x1b, }; +static hda_nid_t stac92hd73xx_pin_nids[12] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x22 +}; + +static hda_nid_t stac92hd71bxx_pin_nids[10] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x14, 0x18, 0x19, 0x1e, +}; + static hda_nid_t stac927x_pin_nids[14] = { 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, @@ -257,8 +367,9 @@ static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; + unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - ucontrol->value.enumerated.item[0] = spec->cur_dmux; + ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx]; return 0; } @@ -267,9 +378,10 @@ static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; + unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol, - spec->dmux_nid, &spec->cur_dmux); + spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]); } static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -299,15 +411,45 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]); } +static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + return snd_hda_input_mux_info(spec->mono_mux, uinfo); +} + +static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->cur_mmux; + return 0; +} + +static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol, + spec->mono_nid, &spec->cur_mmux); +} + #define stac92xx_aloopback_info snd_ctl_boolean_mono_info static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); struct sigmatel_spec *spec = codec->spec; - ucontrol->value.integer.value[0] = spec->aloopback; + ucontrol->value.integer.value[0] = !!(spec->aloopback & + (spec->aloopback_mask << idx)); return 0; } @@ -316,23 +458,33 @@ static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned int dac_mode; + unsigned int val, idx_val; - if (spec->aloopback == ucontrol->value.integer.value[0]) + idx_val = spec->aloopback_mask << idx; + if (ucontrol->value.integer.value[0]) + val = spec->aloopback | idx_val; + else + val = spec->aloopback & ~idx_val; + if (spec->aloopback == val) return 0; - spec->aloopback = ucontrol->value.integer.value[0]; - + spec->aloopback = val; + /* Only return the bits defined by the shift value of the + * first two bytes of the mask + */ dac_mode = snd_hda_codec_read(codec, codec->afg, 0, - kcontrol->private_value & 0xFFFF, 0x0); + kcontrol->private_value & 0xFFFF, 0x0); + dac_mode >>= spec->aloopback_shift; - if (spec->aloopback) { + if (spec->aloopback & idx_val) { snd_hda_power_up(codec); - dac_mode |= 0x40; + dac_mode |= idx_val; } else { snd_hda_power_down(codec); - dac_mode &= ~0x40; + dac_mode &= ~idx_val; } snd_hda_codec_write_cache(codec, codec->afg, 0, @@ -354,6 +506,107 @@ static struct hda_verb stac9200_eapd_init[] = { {} }; +static struct hda_verb stac92hd73xx_6ch_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* setup audio connections */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00}, + { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* setup import muxs */ + { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {} +}; + +static struct hda_verb stac92hd73xx_8ch_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* setup audio connections */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00}, + { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* connect hp ports to dac3 */ + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03}, + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* setup import muxs */ + { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03}, + {} +}; + +static struct hda_verb stac92hd73xx_10ch_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* setup audio connections */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 }, + /* dac3 is connected to import3 mux */ + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f}, + /* connect hp ports to dac4 */ + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04}, + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* setup import muxs */ + { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03}, + {} +}; + +static struct hda_verb stac92hd71bxx_core_init[] = { + /* set master volume and direct control */ + { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* connect headphone jack to dac1 */ + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */ + /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */ + { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +}; + +static struct hda_verb stac92hd71bxx_analog_core_init[] = { + /* set master volume and direct control */ + { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* connect headphone jack to dac1 */ + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* connect ports 0d and 0f to audio mixer */ + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2}, + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */ + /* unmute dac0 input in audio mixer */ + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f}, + /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */ + { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {} +}; + static struct hda_verb stac925x_core_init[] = { /* set dac0mux for dac converter */ { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00}, @@ -388,6 +641,16 @@ static struct hda_verb stac9205_core_init[] = { {} }; +#define STAC_MONO_MUX \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Mono Mux", \ + .count = 1, \ + .info = stac92xx_mono_mux_enum_info, \ + .get = stac92xx_mono_mux_enum_get, \ + .put = stac92xx_mono_mux_enum_put, \ + } + #define STAC_INPUT_SOURCE(cnt) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ @@ -398,11 +661,11 @@ static struct hda_verb stac9205_core_init[] = { .put = stac92xx_mux_enum_put, \ } -#define STAC_ANALOG_LOOPBACK(verb_read,verb_write) \ +#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = "Analog Loopback", \ - .count = 1, \ + .count = cnt, \ .info = stac92xx_aloopback_info, \ .get = stac92xx_aloopback_get, \ .put = stac92xx_aloopback_put, \ @@ -419,6 +682,114 @@ static struct snd_kcontrol_new stac9200_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = { + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), + + HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT), + + HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = { + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), + + HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT), + + HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = { + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), + + HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT), + + HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = { + STAC_INPUT_SOURCE(2), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT), + + HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new stac92hd71bxx_mixer[] = { + STAC_INPUT_SOURCE(2), + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT), + { } /* end */ +}; + static struct snd_kcontrol_new stac925x_mixer[] = { STAC_INPUT_SOURCE(1), HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT), @@ -428,16 +799,8 @@ static struct snd_kcontrol_new stac925x_mixer[] = { }; static struct snd_kcontrol_new stac9205_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Digital Input Source", - .count = 1, - .info = stac92xx_dmux_enum_info, - .get = stac92xx_dmux_enum_get, - .put = stac92xx_dmux_enum_put, - }, STAC_INPUT_SOURCE(2), - STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0), + STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1), HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT), HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT), @@ -466,7 +829,7 @@ static struct snd_kcontrol_new stac922x_mixer[] = { static struct snd_kcontrol_new stac927x_mixer[] = { STAC_INPUT_SOURCE(3), - STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB), + STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1), HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT), HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT), @@ -482,6 +845,44 @@ static struct snd_kcontrol_new stac927x_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new stac_dmux_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Input Source", + /* count set later */ + .info = stac92xx_dmux_enum_info, + .get = stac92xx_dmux_enum_get, + .put = stac92xx_dmux_enum_put, +}; + +static const char *slave_vols[] = { + "Front Playback Volume", + "Surround Playback Volume", + "Center Playback Volume", + "LFE Playback Volume", + "Side Playback Volume", + "Headphone Playback Volume", + "Headphone Playback Volume", + "Speaker Playback Volume", + "External Speaker Playback Volume", + "Speaker2 Playback Volume", + NULL +}; + +static const char *slave_sws[] = { + "Front Playback Switch", + "Surround Playback Switch", + "Center Playback Switch", + "LFE Playback Switch", + "Side Playback Switch", + "Headphone Playback Switch", + "Headphone Playback Switch", + "Speaker Playback Switch", + "External Speaker Playback Switch", + "Speaker2 Playback Switch", + "IEC958 Playback Switch", + NULL +}; + static int stac92xx_build_controls(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -497,6 +898,13 @@ static int stac92xx_build_controls(struct hda_codec *codec) if (err < 0) return err; } + if (spec->num_dmuxes > 0) { + stac_dmux_mixer.count = spec->num_dmuxes; + err = snd_ctl_add(codec->bus->card, + snd_ctl_new1(&stac_dmux_mixer, codec)); + if (err < 0) + return err; + } if (spec->multiout.dig_out_nid) { err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); @@ -508,6 +916,23 @@ static int stac92xx_build_controls(struct hda_codec *codec) if (err < 0) return err; } + + /* if we have no master control, let's create it */ + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], + HDA_OUTPUT, spec->vmaster_tlv); + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + spec->vmaster_tlv, slave_vols); + if (err < 0) + return err; + } + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { + err = snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, slave_sws); + if (err < 0) + return err; + } + return 0; } @@ -733,7 +1158,7 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = { static unsigned int ref925x_pin_configs[8] = { 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021, - 0x90a70320, 0x02214210, 0x400003f1, 0x9033032e, + 0x90a70320, 0x02214210, 0x01019020, 0x9033032e, }; static unsigned int stac925x_MA6_pin_configs[8] = { @@ -777,6 +1202,48 @@ static struct snd_pci_quirk stac925x_cfg_tbl[] = { {} /* terminator */ }; +static unsigned int ref92hd73xx_pin_configs[12] = { + 0x02214030, 0x02a19040, 0x01a19020, 0x02214030, + 0x0181302e, 0x01014010, 0x01014020, 0x01014030, + 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050, +}; + +static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { + [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs, +}; + +static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = { + [STAC_92HD73XX_REF] = "ref", +}; + +static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_92HD73XX_REF), + {} /* terminator */ +}; + +static unsigned int ref92hd71bxx_pin_configs[10] = { + 0x02214030, 0x02a19040, 0x01a19020, 0x01014010, + 0x0181302e, 0x01114010, 0x01019020, 0x90a000f0, + 0x90a000f0, 0x01452050, +}; + +static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { + [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs, +}; + +static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { + [STAC_92HD71BXX_REF] = "ref", +}; + +static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_92HD71BXX_REF), + {} /* terminator */ +}; + static unsigned int ref922x_pin_configs[10] = { 0x01014010, 0x01016011, 0x01012012, 0x0221401f, 0x01813122, 0x01011014, 0x01441030, 0x01c41030, @@ -823,8 +1290,8 @@ static unsigned int dell_922x_m81_pin_configs[10] = { 102801D7 (Dell XPS M1210) */ static unsigned int dell_922x_m82_pin_configs[10] = { - 0x0221121f, 0x408103ff, 0x02111212, 0x90100310, - 0x408003f1, 0x02111211, 0x03451340, 0x40c003f2, + 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310, + 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2, 0x508003f3, 0x405003f4, }; @@ -1022,22 +1489,24 @@ static unsigned int d965_5st_pin_configs[14] = { static unsigned int dell_3st_pin_configs[14] = { 0x02211230, 0x02a11220, 0x01a19040, 0x01114210, 0x01111212, 0x01116211, 0x01813050, 0x01112214, - 0x403003fa, 0x40000100, 0x40000100, 0x404003fb, + 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb, 0x40c003fc, 0x40000100 }; static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { - [STAC_D965_REF] = ref927x_pin_configs, - [STAC_D965_3ST] = d965_3st_pin_configs, - [STAC_D965_5ST] = d965_5st_pin_configs, - [STAC_DELL_3ST] = dell_3st_pin_configs, + [STAC_D965_REF] = ref927x_pin_configs, + [STAC_D965_3ST] = d965_3st_pin_configs, + [STAC_D965_5ST] = d965_5st_pin_configs, + [STAC_DELL_3ST] = dell_3st_pin_configs, + [STAC_DELL_BIOS] = NULL, }; static const char *stac927x_models[STAC_927X_MODELS] = { - [STAC_D965_REF] = "ref", - [STAC_D965_3ST] = "3stack", - [STAC_D965_5ST] = "5stack", - [STAC_DELL_3ST] = "dell-3stack", + [STAC_D965_REF] = "ref", + [STAC_D965_3ST] = "3stack", + [STAC_D965_5ST] = "5stack", + [STAC_DELL_3ST] = "dell-3stack", + [STAC_DELL_BIOS] = "dell-bios", }; static struct snd_pci_quirk stac927x_cfg_tbl[] = { @@ -1064,13 +1533,21 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_D965_3ST), /* Dell 3 stack systems */ + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell Dimension E520", STAC_DELL_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ed, "Dell ", STAC_DELL_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f4, "Dell ", STAC_DELL_3ST), + /* Dell 3 stack systems with verb table in BIOS */ + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS), /* 965 based 5 stack systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_D965_5ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST), @@ -1085,7 +1562,7 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = { static unsigned int ref9205_pin_configs[12] = { 0x40000100, 0x40000100, 0x01016011, 0x01014010, - 0x01813122, 0x01a19021, 0x40000100, 0x40000100, + 0x01813122, 0x01a19021, 0x01019020, 0x40000100, 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030 }; @@ -1097,6 +1574,7 @@ static unsigned int ref9205_pin_configs[12] = { 102801FD 10280204 1028021F + 10280228 (Dell Vostro 1500) */ static unsigned int dell_9205_m42_pin_configs[12] = { 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310, @@ -1180,6 +1658,8 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = { "unknown Dell", STAC_9205_DELL_M42), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f, "Dell Inspiron", STAC_9205_DELL_M44), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228, + "Dell Vostro 1500", STAC_9205_DELL_M42), {} /* terminator */ }; @@ -1245,22 +1725,6 @@ static void stac92xx_set_config_regs(struct hda_codec *codec) spec->pin_configs[i]); } -static void stac92xx_enable_gpio_mask(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - /* Configure GPIOx as output */ - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DIRECTION, spec->gpio_mask); - /* Configure GPIOx as CMOS */ - snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7e7, 0x00000000); - /* Assert GPIOx */ - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DATA, spec->gpio_data); - /* Enable GPIOx */ - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_MASK, spec->gpio_mask); -} - /* * Analog playback callbacks */ @@ -1479,7 +1943,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ struct sigmatel_spec *spec = codec->spec; hda_nid_t nid = kcontrol->private_value >> 8; int io_idx = kcontrol-> private_value & 0xff; - unsigned short val = ucontrol->value.integer.value[0]; + unsigned short val = !!ucontrol->value.integer.value[0]; spec->io_switch[io_idx] = val; @@ -1491,6 +1955,13 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ pinctl |= stac92xx_get_vref(codec, nid); stac92xx_auto_set_pinctl(codec, nid, pinctl); } + + /* check the auto-mute again: we need to mute/unmute the speaker + * appropriately according to the pin direction + */ + if (spec->hp_detect) + codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); + return 1; } @@ -1512,11 +1983,12 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; hda_nid_t nid = kcontrol->private_value & 0xff; + unsigned int val = !!ucontrol->value.integer.value[0]; - if (spec->clfe_swap == ucontrol->value.integer.value[0]) + if (spec->clfe_swap == val) return 0; - spec->clfe_swap = ucontrol->value.integer.value[0]; + spec->clfe_swap = val; snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, spec->clfe_swap ? 0x4 : 0x0); @@ -1547,6 +2019,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, enum { STAC_CTL_WIDGET_VOL, STAC_CTL_WIDGET_MUTE, + STAC_CTL_WIDGET_MONO_MUX, STAC_CTL_WIDGET_IO_SWITCH, STAC_CTL_WIDGET_CLFE_SWITCH }; @@ -1554,6 +2027,7 @@ enum { static struct snd_kcontrol_new stac92xx_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), + STAC_MONO_MUX, STAC_CODEC_IO_SWITCH(NULL, 0), STAC_CODEC_CLFE_SWITCH(NULL, 0), }; @@ -1598,6 +2072,7 @@ static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cf for (i = 0; i < codec->num_nodes; i++) { wcaps = codec->wcaps[i]; wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) num_dacs++; } @@ -1685,7 +2160,6 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, wcaps = snd_hda_param_read(codec, conn[j], AC_PAR_AUDIO_WIDGET_CAP); wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL)) continue; @@ -1759,7 +2233,7 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, int i, err; struct sigmatel_spec *spec = codec->spec; - unsigned int wid_caps; + unsigned int wid_caps, pincap; for (i = 0; i < cfg->line_outs; i++) { @@ -1795,13 +2269,39 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, } } - if (spec->line_switch) - if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Line In as Output Switch", cfg->input_pins[AUTO_PIN_LINE] << 8)) < 0) - return err; + if (spec->line_switch) { + nid = cfg->input_pins[AUTO_PIN_LINE]; + pincap = snd_hda_param_read(codec, nid, + AC_PAR_PIN_CAP); + if (pincap & AC_PINCAP_OUT) { + err = stac92xx_add_control(spec, + STAC_CTL_WIDGET_IO_SWITCH, + "Line In as Output Switch", nid << 8); + if (err < 0) + return err; + } + } - if (spec->mic_switch) - if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Mic as Output Switch", (cfg->input_pins[AUTO_PIN_MIC] << 8) | 1)) < 0) - return err; + if (spec->mic_switch) { + unsigned int def_conf; + nid = cfg->input_pins[AUTO_PIN_MIC]; + def_conf = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0); + + /* some laptops have an internal analog microphone + * which can't be used as a output */ + if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) { + pincap = snd_hda_param_read(codec, nid, + AC_PAR_PIN_CAP); + if (pincap & AC_PINCAP_OUT) { + err = stac92xx_add_control(spec, + STAC_CTL_WIDGET_IO_SWITCH, + "Mic as Output Switch", (nid << 8) | 1); + if (err < 0) + return err; + } + } + } return 0; } @@ -1891,6 +2391,37 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, return 0; } +/* labels for mono mux outputs */ +static const char *stac92xx_mono_labels[3] = { + "DAC0", "DAC1", "Mixer" +}; + +/* create mono mux for mono out on capable codecs */ +static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct hda_input_mux *mono_mux = &spec->private_mono_mux; + int i, num_cons; + hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)]; + + num_cons = snd_hda_get_connections(codec, + spec->mono_nid, + con_lst, + HDA_MAX_NUM_INPUTS); + if (!num_cons || num_cons > ARRAY_SIZE(stac92xx_mono_labels)) + return -EINVAL; + + for (i = 0; i < num_cons; i++) { + mono_mux->items[mono_mux->num_items].label = + stac92xx_mono_labels[i]; + mono_mux->items[mono_mux->num_items].index = i; + mono_mux->num_items++; + } + + return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX, + "Mono Mux", spec->mono_nid); +} + /* labels for dmic mux inputs */ static const char *stac92xx_dmic_labels[5] = { "Analog Inputs", "Digital Mic 1", "Digital Mic 2", @@ -1904,15 +2435,18 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, struct sigmatel_spec *spec = codec->spec; struct hda_input_mux *dimux = &spec->private_dimux; hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; - int i, j; + int err, i, j; + char name[32]; dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0]; dimux->items[dimux->num_items].index = 0; dimux->num_items++; for (i = 0; i < spec->num_dmics; i++) { + hda_nid_t nid; int index; int num_cons; + unsigned int wcaps; unsigned int def_conf; def_conf = snd_hda_codec_read(codec, @@ -1923,17 +2457,32 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) continue; + nid = spec->dmic_nids[i]; num_cons = snd_hda_get_connections(codec, - spec->dmux_nid, + spec->dmux_nids[0], con_lst, HDA_MAX_NUM_INPUTS); for (j = 0; j < num_cons; j++) - if (con_lst[j] == spec->dmic_nids[i]) { + if (con_lst[j] == nid) { index = j; goto found; } continue; found: + wcaps = get_wcaps(codec, nid); + + if (wcaps & AC_WCAP_OUT_AMP) { + sprintf(name, "%s Capture Volume", + stac92xx_dmic_labels[dimux->num_items]); + + err = stac92xx_add_control(spec, + STAC_CTL_WIDGET_VOL, + name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + } + dimux->items[dimux->num_items].label = stac92xx_dmic_labels[dimux->num_items]; dimux->items[dimux->num_items].index = index; @@ -2026,6 +2575,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out { struct sigmatel_spec *spec = codec->spec; int err; + int hp_speaker_swap = 0; if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, @@ -2034,6 +2584,68 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out if (! spec->autocfg.line_outs) return 0; /* can't find valid pin config */ + /* If we have no real line-out pin and multiple hp-outs, HPs should + * be set up as multi-channel outputs. + */ + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && + spec->autocfg.hp_outs > 1) { + /* Copy hp_outs to line_outs, backup line_outs in + * speaker_outs so that the following routines can handle + * HP pins as primary outputs. + */ + memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins, + sizeof(spec->autocfg.line_out_pins)); + spec->autocfg.speaker_outs = spec->autocfg.line_outs; + memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins, + sizeof(spec->autocfg.hp_pins)); + spec->autocfg.line_outs = spec->autocfg.hp_outs; + hp_speaker_swap = 1; + } + if (spec->autocfg.mono_out_pin) { + int dir = (get_wcaps(codec, spec->autocfg.mono_out_pin) + & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT; + u32 caps = query_amp_caps(codec, + spec->autocfg.mono_out_pin, dir); + hda_nid_t conn_list[1]; + + /* get the mixer node and then the mono mux if it exists */ + if (snd_hda_get_connections(codec, + spec->autocfg.mono_out_pin, conn_list, 1) && + snd_hda_get_connections(codec, conn_list[0], + conn_list, 1)) { + + int wcaps = get_wcaps(codec, conn_list[0]); + int wid_type = (wcaps & AC_WCAP_TYPE) + >> AC_WCAP_TYPE_SHIFT; + /* LR swap check, some stac925x have a mux that + * changes the DACs output path instead of the + * mono-mux path. + */ + if (wid_type == AC_WID_AUD_SEL && + !(wcaps & AC_WCAP_LR_SWAP)) + spec->mono_nid = conn_list[0]; + } + /* all mono outs have a least a mute/unmute switch */ + err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, + "Mono Playback Switch", + HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin, + 1, 0, dir)); + if (err < 0) + return err; + /* check to see if there is volume support for the amp */ + if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) { + err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, + "Mono Playback Volume", + HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin, + 1, 0, dir)); + if (err < 0) + return err; + } + + stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin, + AC_PINCTL_OUT_EN); + } + if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0) return err; if (spec->multiout.num_dacs == 0) @@ -2045,6 +2657,19 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out if (err < 0) return err; + if (hp_speaker_swap == 1) { + /* Restore the hp_outs and line_outs */ + memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins, + sizeof(spec->autocfg.line_out_pins)); + spec->autocfg.hp_outs = spec->autocfg.line_outs; + memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins, + sizeof(spec->autocfg.speaker_pins)); + spec->autocfg.line_outs = spec->autocfg.speaker_outs; + memset(spec->autocfg.speaker_pins, 0, + sizeof(spec->autocfg.speaker_pins)); + spec->autocfg.speaker_outs = 0; + } + err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg); if (err < 0) @@ -2055,6 +2680,12 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out if (err < 0) return err; + if (spec->mono_nid > 0) { + err = stac92xx_auto_create_mono_output_ctls(codec); + if (err < 0) + return err; + } + if (spec->num_dmics > 0) if ((err = stac92xx_auto_create_dmic_input_ctls(codec, &spec->autocfg)) < 0) @@ -2073,7 +2704,9 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out spec->mixers[spec->num_mixers++] = spec->kctl_alloc; spec->input_mux = &spec->private_imux; - spec->dinput_mux = &spec->private_dimux; + if (!spec->dinput_mux) + spec->dinput_mux = &spec->private_dimux; + spec->mono_mux = &spec->private_mono_mux; return 1; } @@ -2183,38 +2816,35 @@ static int stac9200_parse_auto_config(struct hda_codec *codec) * funky external mute control using GPIO pins. */ -static void stac922x_gpio_mute(struct hda_codec *codec, int pin, int muted) +static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, + unsigned int dir_mask, unsigned int data) { unsigned int gpiostate, gpiomask, gpiodir; gpiostate = snd_hda_codec_read(codec, codec->afg, 0, AC_VERB_GET_GPIO_DATA, 0); - - if (!muted) - gpiostate |= (1 << pin); - else - gpiostate &= ~(1 << pin); + gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); gpiomask = snd_hda_codec_read(codec, codec->afg, 0, AC_VERB_GET_GPIO_MASK, 0); - gpiomask |= (1 << pin); + gpiomask |= mask; gpiodir = snd_hda_codec_read(codec, codec->afg, 0, AC_VERB_GET_GPIO_DIRECTION, 0); - gpiodir |= (1 << pin); + gpiodir |= dir_mask; - /* AppleHDA seems to do this -- WTF is this verb?? */ + /* Configure GPIOx as CMOS */ snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0); snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DIRECTION, gpiodir); + snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ msleep(1); - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DATA, gpiostate); + snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ } static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, @@ -2226,6 +2856,16 @@ static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, (AC_USRSP_EN | event)); } +static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) +{ + int i; + for (i = 0; i < cfg->hp_outs; i++) + if (cfg->hp_pins[i] == nid) + return 1; /* nid is a HP-Out */ + + return 0; /* nid is not a HP-Out */ +}; + static int stac92xx_init(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -2261,10 +2901,23 @@ static int stac92xx_init(struct hda_codec *codec) stac92xx_auto_set_pinctl(codec, nid, pinctl); } } - if (spec->num_dmics > 0) - for (i = 0; i < spec->num_dmics; i++) - stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i], - AC_PINCTL_IN_EN); + for (i = 0; i < spec->num_dmics; i++) + stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i], + AC_PINCTL_IN_EN); + for (i = 0; i < spec->num_pwrs; i++) { + int event = is_nid_hp_pin(cfg, spec->pwr_nids[i]) + ? STAC_HP_EVENT : STAC_PWR_EVENT; + int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i], + 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + /* outputs are only ports capable of power management + * any attempts on powering down a input port cause the + * referenced VREF to act quirky. + */ + if (pinctl & AC_PINCTL_IN_EN) + continue; + enable_pin_detect(codec, spec->pwr_nids[i], event | i); + codec->patch_ops.unsol_event(codec, (event | i) << 26); + } if (cfg->dig_out_pin) stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin, @@ -2273,10 +2926,8 @@ static int stac92xx_init(struct hda_codec *codec) stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin, AC_PINCTL_IN_EN); - if (spec->gpio_mute) { - stac922x_gpio_mute(codec, 0, 0); - stac922x_gpio_mute(codec, 1, 0); - } + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); return 0; } @@ -2342,13 +2993,20 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, pin_ctl & ~flag); } -static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) +static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid) { if (!nid) return 0; if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00) - & (1 << 31)) - return 1; + & (1 << 31)) { + unsigned int pinctl; + pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (pinctl & AC_PINCTL_IN_EN) + return 0; /* mic- or line-input */ + else + return 1; /* HP-output */ + } return 0; } @@ -2359,10 +3017,14 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) int i, presence; presence = 0; + if (spec->gpio_mute) + presence = !(snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute); + for (i = 0; i < cfg->hp_outs; i++) { - presence = get_pin_presence(codec, cfg->hp_pins[i]); if (presence) break; + presence = get_hp_pin_presence(codec, cfg->hp_pins[i]); } if (presence) { @@ -2384,12 +3046,37 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) } } +static void stac92xx_pin_sense(struct hda_codec *codec, int idx) +{ + struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid = spec->pwr_nids[idx]; + int presence, val; + val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) + & 0x000000ff; + presence = get_hp_pin_presence(codec, nid); + idx = 1 << idx; + + if (presence) + val &= ~idx; + else + val |= idx; + + /* power down unused output ports */ + snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val); +}; + static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) { - switch (res >> 26) { + struct sigmatel_spec *spec = codec->spec; + int idx = res >> 26 & 0x0f; + + switch ((res >> 26) & 0x30) { case STAC_HP_EVENT: stac92xx_hp_detect(codec, res); - break; + /* fallthru */ + case STAC_PWR_EVENT: + if (spec->num_pwrs > 0) + stac92xx_pin_sense(codec, idx); } } @@ -2400,10 +3087,8 @@ static int stac92xx_resume(struct hda_codec *codec) stac92xx_set_config_regs(codec); snd_hda_sequence_write(codec, spec->init); - if (spec->gpio_mute) { - stac922x_gpio_mute(codec, 0, 0); - stac922x_gpio_mute(codec, 1, 0); - } + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); /* invoke unsolicited event to reset the HP state */ @@ -2460,6 +3145,7 @@ static int patch_stac9200(struct hda_codec *codec) spec->num_muxes = 1; spec->num_dmics = 0; spec->num_adcs = 1; + spec->num_pwrs = 0; if (spec->board_config == STAC_9200_GATEWAY) spec->init = stac9200_eapd_init; @@ -2515,6 +3201,7 @@ static int patch_stac925x(struct hda_codec *codec) spec->mux_nids = stac925x_mux_nids; spec->num_muxes = 1; spec->num_adcs = 1; + spec->num_pwrs = 0; switch (codec->vendor_id) { case 0x83847632: /* STAC9202 */ case 0x83847633: /* STAC9202D */ @@ -2522,6 +3209,8 @@ static int patch_stac925x(struct hda_codec *codec) case 0x83847637: /* STAC9251D */ spec->num_dmics = STAC925X_NUM_DMICS; spec->dmic_nids = stac925x_dmic_nids; + spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids); + spec->dmux_nids = stac925x_dmux_nids; break; default: spec->num_dmics = 0; @@ -2551,6 +3240,204 @@ static int patch_stac925x(struct hda_codec *codec) return 0; } +static struct hda_input_mux stac92hd73xx_dmux = { + .num_items = 4, + .items = { + { "Analog Inputs", 0x0b }, + { "CD", 0x08 }, + { "Digital Mic 1", 0x09 }, + { "Digital Mic 2", 0x0a }, + } +}; + +static int patch_stac92hd73xx(struct hda_codec *codec) +{ + struct sigmatel_spec *spec; + hda_nid_t conn[STAC92HD73_DAC_COUNT + 2]; + int err = 0; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids); + spec->pin_nids = stac92hd73xx_pin_nids; + spec->board_config = snd_hda_check_board_config(codec, + STAC_92HD73XX_MODELS, + stac92hd73xx_models, + stac92hd73xx_cfg_tbl); +again: + if (spec->board_config < 0) { + snd_printdd(KERN_INFO "hda_codec: Unknown model for" + " STAC92HD73XX, using BIOS defaults\n"); + err = stac92xx_save_bios_config_regs(codec); + if (err < 0) { + stac92xx_free(codec); + return err; + } + spec->pin_configs = spec->bios_pin_configs; + } else { + spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config]; + stac92xx_set_config_regs(codec); + } + + spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a, + conn, STAC92HD73_DAC_COUNT + 2) - 1; + + if (spec->multiout.num_dacs < 0) { + printk(KERN_WARNING "hda_codec: Could not determine " + "number of channels defaulting to DAC count\n"); + spec->multiout.num_dacs = STAC92HD73_DAC_COUNT; + } + + switch (spec->multiout.num_dacs) { + case 0x3: /* 6 Channel */ + spec->mixer = stac92hd73xx_6ch_mixer; + spec->init = stac92hd73xx_6ch_core_init; + break; + case 0x4: /* 8 Channel */ + spec->multiout.hp_nid = 0x18; + spec->mixer = stac92hd73xx_8ch_mixer; + spec->init = stac92hd73xx_8ch_core_init; + break; + case 0x5: /* 10 Channel */ + spec->multiout.hp_nid = 0x19; + spec->mixer = stac92hd73xx_10ch_mixer; + spec->init = stac92hd73xx_10ch_core_init; + }; + + spec->multiout.dac_nids = stac92hd73xx_dac_nids; + spec->aloopback_mask = 0x01; + spec->aloopback_shift = 8; + + spec->mux_nids = stac92hd73xx_mux_nids; + spec->adc_nids = stac92hd73xx_adc_nids; + spec->dmic_nids = stac92hd73xx_dmic_nids; + spec->dmux_nids = stac92hd73xx_dmux_nids; + + spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids); + spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids); + spec->num_dmics = STAC92HD73XX_NUM_DMICS; + spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids); + spec->dinput_mux = &stac92hd73xx_dmux; + /* GPIO0 High = Enable EAPD */ + spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; + + spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids); + spec->pwr_nids = stac92hd73xx_pwr_nids; + + err = stac92xx_parse_auto_config(codec, 0x22, 0x24); + + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_92HD73XX_REF; + goto again; + } + err = -EINVAL; + } + + if (err < 0) { + stac92xx_free(codec); + return err; + } + + codec->patch_ops = stac92xx_patch_ops; + + return 0; +} + +static int patch_stac92hd71bxx(struct hda_codec *codec) +{ + struct sigmatel_spec *spec; + int err = 0; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids); + spec->pin_nids = stac92hd71bxx_pin_nids; + spec->board_config = snd_hda_check_board_config(codec, + STAC_92HD71BXX_MODELS, + stac92hd71bxx_models, + stac92hd71bxx_cfg_tbl); +again: + if (spec->board_config < 0) { + snd_printdd(KERN_INFO "hda_codec: Unknown model for" + " STAC92HD71BXX, using BIOS defaults\n"); + err = stac92xx_save_bios_config_regs(codec); + if (err < 0) { + stac92xx_free(codec); + return err; + } + spec->pin_configs = spec->bios_pin_configs; + } else { + spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config]; + stac92xx_set_config_regs(codec); + } + + switch (codec->vendor_id) { + case 0x111d76b6: /* 4 Port without Analog Mixer */ + case 0x111d76b7: + case 0x111d76b4: /* 6 Port without Analog Mixer */ + case 0x111d76b5: + spec->mixer = stac92hd71bxx_mixer; + spec->init = stac92hd71bxx_core_init; + break; + default: + spec->mixer = stac92hd71bxx_analog_mixer; + spec->init = stac92hd71bxx_analog_core_init; + } + + spec->aloopback_mask = 0x20; + spec->aloopback_shift = 0; + + /* GPIO0 High = EAPD */ + spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0x1; + + spec->mux_nids = stac92hd71bxx_mux_nids; + spec->adc_nids = stac92hd71bxx_adc_nids; + spec->dmic_nids = stac92hd71bxx_dmic_nids; + spec->dmux_nids = stac92hd71bxx_dmux_nids; + + spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids); + spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids); + spec->num_dmics = STAC92HD71BXX_NUM_DMICS; + spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); + + spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); + spec->pwr_nids = stac92hd71bxx_pwr_nids; + + spec->multiout.num_dacs = 2; + spec->multiout.hp_nid = 0x11; + spec->multiout.dac_nids = stac92hd71bxx_dac_nids; + + err = stac92xx_parse_auto_config(codec, 0x21, 0x23); + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_92HD71BXX_REF; + goto again; + } + err = -EINVAL; + } + + if (err < 0) { + stac92xx_free(codec); + return err; + } + + codec->patch_ops = stac92xx_patch_ops; + + return 0; +}; + static int patch_stac922x(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -2567,7 +3454,8 @@ static int patch_stac922x(struct hda_codec *codec) stac922x_models, stac922x_cfg_tbl); if (spec->board_config == STAC_INTEL_MAC_V3) { - spec->gpio_mute = 1; + spec->gpio_mask = spec->gpio_dir = 0x03; + spec->gpio_data = 0x03; /* Intel Macs have all same PCI SSID, so we need to check * codec SSID to distinguish the exact models */ @@ -2620,6 +3508,7 @@ static int patch_stac922x(struct hda_codec *codec) spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids); spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids); spec->num_dmics = 0; + spec->num_pwrs = 0; spec->init = stac922x_core_init; spec->mixer = stac922x_mixer; @@ -2669,53 +3558,70 @@ static int patch_stac927x(struct hda_codec *codec) stac927x_models, stac927x_cfg_tbl); again: - if (spec->board_config < 0) { - snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC927x, using BIOS defaults\n"); + if (spec->board_config < 0 || !stac927x_brd_tbl[spec->board_config]) { + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: Unknown model for" + "STAC927x, using BIOS defaults\n"); err = stac92xx_save_bios_config_regs(codec); if (err < 0) { stac92xx_free(codec); return err; } spec->pin_configs = spec->bios_pin_configs; - } else if (stac927x_brd_tbl[spec->board_config] != NULL) { + } else { spec->pin_configs = stac927x_brd_tbl[spec->board_config]; stac92xx_set_config_regs(codec); } + spec->adc_nids = stac927x_adc_nids; + spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); + spec->mux_nids = stac927x_mux_nids; + spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); + spec->multiout.dac_nids = spec->dac_nids; + switch (spec->board_config) { case STAC_D965_3ST: - spec->adc_nids = stac927x_adc_nids; - spec->mux_nids = stac927x_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); + case STAC_D965_5ST: + /* GPIO0 High = Enable EAPD */ + spec->gpio_mask = spec->gpio_dir = 0x01; + spec->gpio_data = 0x01; spec->num_dmics = 0; + spec->init = d965_core_init; spec->mixer = stac927x_mixer; break; - case STAC_D965_5ST: - spec->adc_nids = stac927x_adc_nids; - spec->mux_nids = stac927x_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); - spec->num_dmics = 0; + case STAC_DELL_BIOS: + /* correct the front output jack as a hp out */ + stac92xx_set_config_reg(codec, 0x0f, 0x02270110); + /* correct the front input jack as a mic */ + stac92xx_set_config_reg(codec, 0x0e, 0x02a79130); + /* fallthru */ + case STAC_DELL_3ST: + /* GPIO2 High = Enable EAPD */ + spec->gpio_mask = spec->gpio_dir = 0x04; + spec->gpio_data = 0x04; + spec->dmic_nids = stac927x_dmic_nids; + spec->num_dmics = STAC927X_NUM_DMICS; + spec->init = d965_core_init; spec->mixer = stac927x_mixer; + spec->dmux_nids = stac927x_dmux_nids; + spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids); break; default: - spec->adc_nids = stac927x_adc_nids; - spec->mux_nids = stac927x_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); + /* GPIO0 High = Enable EAPD */ + spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; spec->num_dmics = 0; + spec->init = stac927x_core_init; spec->mixer = stac927x_mixer; } - spec->multiout.dac_nids = spec->dac_nids; - /* GPIO0 High = Enable EAPD */ - spec->gpio_mask = spec->gpio_data = 0x00000001; - stac92xx_enable_gpio_mask(codec); - + spec->num_pwrs = 0; + spec->aloopback_mask = 0x40; + spec->aloopback_shift = 0; + err = stac92xx_parse_auto_config(codec, 0x1e, 0x20); if (!err) { if (spec->board_config < 0) { @@ -2733,6 +3639,18 @@ static int patch_stac927x(struct hda_codec *codec) codec->patch_ops = stac92xx_patch_ops; + /* + * !!FIXME!! + * The STAC927x seem to require fairly long delays for certain + * command sequences. With too short delays (even if the answer + * is set to RIRB properly), it results in the silence output + * on some hardwares like Dell. + * + * The below flag enables the longer delay (see get_response + * in hda_intel.c). + */ + codec->bus->needs_damn_long_delay = 1; + return 0; } @@ -2771,11 +3689,15 @@ static int patch_stac9205(struct hda_codec *codec) spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids); spec->dmic_nids = stac9205_dmic_nids; spec->num_dmics = STAC9205_NUM_DMICS; - spec->dmux_nid = 0x1d; + spec->dmux_nids = stac9205_dmux_nids; + spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids); + spec->num_pwrs = 0; spec->init = stac9205_core_init; spec->mixer = stac9205_mixer; + spec->aloopback_mask = 0x40; + spec->aloopback_shift = 0; spec->multiout.dac_nids = spec->dac_nids; switch (spec->board_config){ @@ -2784,19 +3706,28 @@ static int patch_stac9205(struct hda_codec *codec) stac92xx_set_config_reg(codec, 0x1f, 0x01441030); stac92xx_set_config_reg(codec, 0x20, 0x1c410030); - spec->gpio_mask = 0x00000007; /* GPIO0-2 */ - /* GPIO0 High = EAPD, GPIO1 Low = DRM, - * GPIO2 High = Headphone Mute + /* Enable unsol response for GPIO4/Dock HP connection */ + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + (AC_USRSP_EN | STAC_HP_EVENT)); + + spec->gpio_dir = 0x0b; + spec->gpio_mask = 0x1b; + spec->gpio_mute = 0x10; + /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute, + * GPIO3 Low = DRM */ - spec->gpio_data = 0x00000005; + spec->gpio_data = 0x01; break; default: /* GPIO0 High = EAPD */ - spec->gpio_mask = spec->gpio_data = 0x00000001; + spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; break; } - stac92xx_enable_gpio_mask(codec); err = stac92xx_parse_auto_config(codec, 0x1f, 0x20); if (!err) { if (spec->board_config < 0) { @@ -2950,7 +3881,7 @@ static int stac9872_vaio_init(struct hda_codec *codec) static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res) { - if (get_pin_presence(codec, 0x0a)) { + if (get_hp_pin_presence(codec, 0x0a)) { stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN); stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN); } else { @@ -3032,6 +3963,7 @@ static int patch_stac9872(struct hda_codec *codec) spec->multiout.hp_nid = VAIO_HP_DAC; spec->num_adcs = ARRAY_SIZE(vaio_adcs); spec->adc_nids = vaio_adcs; + spec->num_pwrs = 0; spec->input_mux = &vaio_mux; spec->mux_nids = vaio_mux_nids; codec->patch_ops = stac9872_vaio_patch_ops; @@ -3045,6 +3977,7 @@ static int patch_stac9872(struct hda_codec *codec) spec->multiout.dac_nids = vaio_dacs; spec->multiout.hp_nid = VAIO_HP_DAC; spec->num_adcs = ARRAY_SIZE(vaio_adcs); + spec->num_pwrs = 0; spec->adc_nids = vaio_adcs; spec->input_mux = &vaio_mux; spec->mux_nids = vaio_mux_nids; @@ -3104,5 +4037,17 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 }, { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 }, { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 }, + { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx }, + { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx }, + { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx }, + { .id = 0x111d7608, .name = "92HD71BXX", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4cdf3e6df4b..4e5dd4cf36f 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -27,11 +27,12 @@ /* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ /* 2006-08-02 Lydia Wang Add support to VT1709 codec */ /* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */ +/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ +/* 2007-09-17 Lydia Wang Add VT1708B codec support */ /* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -51,14 +52,23 @@ #define VT1708_HP_NID 0x13 #define VT1708_DIGOUT_NID 0x14 #define VT1708_DIGIN_NID 0x16 +#define VT1708_DIGIN_PIN 0x26 #define VT1709_HP_DAC_NID 0x28 #define VT1709_DIGOUT_NID 0x13 #define VT1709_DIGIN_NID 0x17 +#define VT1709_DIGIN_PIN 0x25 + +#define VT1708B_HP_NID 0x25 +#define VT1708B_DIGOUT_NID 0x12 +#define VT1708B_DIGIN_NID 0x15 +#define VT1708B_DIGIN_PIN 0x21 #define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b) #define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713) #define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717) +#define IS_VT1708B_8CH_VENDORID(x) ((x) >= 0x1106e720 && (x) <= 0x1106e723) +#define IS_VT1708B_4CH_VENDORID(x) ((x) >= 0x1106e724 && (x) <= 0x1106e727) enum { @@ -131,6 +141,11 @@ static hda_nid_t vt1709_adc_nids[3] = { 0x14, 0x15, 0x16 }; +static hda_nid_t vt1708B_adc_nids[2] = { + /* ADC1-2 */ + 0x13, 0x14 +}; + /* add dynamic controls */ static int via_add_control(struct via_spec *spec, int type, const char *name, unsigned long val) @@ -268,9 +283,13 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol, return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 0x18, &spec->cur_mux[adc_idx]); else if ((IS_VT1709_10CH_VENDORID(vendor_id) || - IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0) ) + IS_VT1709_6CH_VENDORID(vendor_id)) && adc_idx == 0) return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 0x19, &spec->cur_mux[adc_idx]); + else if ((IS_VT1708B_8CH_VENDORID(vendor_id) || + IS_VT1708B_4CH_VENDORID(vendor_id)) && adc_idx == 0) + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, + 0x17, &spec->cur_mux[adc_idx]); else return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, spec->adc_nids[adc_idx], @@ -287,7 +306,6 @@ static struct snd_kcontrol_new vt1708_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -309,15 +327,15 @@ static struct hda_verb vt1708_volume_init_verbs[] = { {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget */ /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* master */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* * Set up output mixers (0x19 - 0x1b) @@ -329,10 +347,9 @@ static struct hda_verb vt1708_volume_init_verbs[] = { /* Setup default input to PW4 */ {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Set mic as default input of sw0 */ - {0x18, AC_VERB_SET_CONNECT_SEL, 0x2}, /* PW9 Output enable */ {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + { } }; static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, @@ -544,6 +561,33 @@ static int via_init(struct hda_codec *codec) { struct via_spec *spec = codec->spec; snd_hda_sequence_write(codec, spec->init_verbs); + /* Lydia Add for EAPD enable */ + if (!spec->dig_in_nid) { /* No Digital In connection */ + if (IS_VT1708_VENDORID(codec->vendor_id)) { + snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_OUT); + snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x02); + } else if (IS_VT1709_10CH_VENDORID(codec->vendor_id) || + IS_VT1709_6CH_VENDORID(codec->vendor_id)) { + snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_OUT); + snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x02); + } else if (IS_VT1708B_8CH_VENDORID(codec->vendor_id) || + IS_VT1708B_4CH_VENDORID(codec->vendor_id)) { + snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_OUT); + snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x02); + } + } else /* enable SPDIF-input pin */ + snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); + return 0; } @@ -623,58 +667,68 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, if (i == AUTO_SEQ_CENLFE) { /* Center/LFE */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); + "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, + HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, + HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, + HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, + HDA_OUTPUT)); if (err < 0) return err; } else if (i == AUTO_SEQ_FRONT){ /* add control to mixer index 0 */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT)); + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, + HDA_INPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT)); + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, + HDA_INPUT)); if (err < 0) return err; /* add control to PW3 */ sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; } else { sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; } @@ -875,7 +929,6 @@ static struct snd_kcontrol_new vt1709_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -899,15 +952,15 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = { {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget */ /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* unmute master */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* * Set up output selector (0x1a, 0x1b, 0x29) @@ -925,8 +978,6 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = { /* Set input of PW4 as AOW4 */ {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Set mic as default input of sw0 */ - {0x19, AC_VERB_SET_CONNECT_SEL, 0x2}, /* PW9 Output enable */ {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, { } @@ -1073,68 +1124,80 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, /* Center/LFE */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, + HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, + HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, + HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, + HDA_OUTPUT)); if (err < 0) return err; } else if (i == AUTO_SEQ_FRONT){ /* add control to mixer index 0 */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT)); + HDA_COMPOSE_AMP_VAL(0x18, 3, 0, + HDA_INPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT)); + HDA_COMPOSE_AMP_VAL(0x18, 3, 0, + HDA_INPUT)); if (err < 0) return err; /* add control to PW3 */ sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; } else if (i == AUTO_SEQ_SURROUND) { sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x29, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x29, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; } else if (i == AUTO_SEQ_SIDE) { sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; } @@ -1351,8 +1414,6 @@ static struct hda_verb vt1709_6ch_volume_init_verbs[] = { /* Set input of PW4 as MW0 */ {0x20, AC_VERB_SET_CONNECT_SEL, 0}, - /* Set mic as default input of sw0 */ - {0x19, AC_VERB_SET_CONNECT_SEL, 0x2}, /* PW9 Output enable */ {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, { } @@ -1403,6 +1464,494 @@ static int patch_vt1709_6ch(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1709_loopbacks; #endif + return 0; +} + +/* capture mixer elements */ +static struct snd_kcontrol_new vt1708B_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 1, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* + * Set up output mixers + */ + /* set vol=0 to output mixers */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* Setup default input to PW4 */ + {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* PW9 Output enable */ + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* PW10 Input enable */ + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + { } +}; + +static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* + * Set up output mixers + */ + /* set vol=0 to output mixers */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* Setup default input of PW4 to MW0 */ + {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* PW9 Output enable */ + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* PW10 Input enable */ + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + { } +}; + +static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_pcm_prepare, + .cleanup = via_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 4, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_pcm_prepare, + .cleanup = via_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream vt1708B_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x13, /* NID to query formats and rates */ + .ops = { + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup + }, +}; + +static struct hda_pcm_stream vt1708B_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare + }, +}; + +static struct hda_pcm_stream vt1708B_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + int i; + hda_nid_t nid; + + spec->multiout.num_dacs = cfg->line_outs; + + spec->multiout.dac_nids = spec->private_dac_nids; + + for (i = 0; i < 4; i++) { + nid = cfg->line_out_pins[i]; + if (nid) { + /* config dac list */ + switch (i) { + case AUTO_SEQ_FRONT: + spec->multiout.dac_nids[i] = 0x10; + break; + case AUTO_SEQ_CENLFE: + spec->multiout.dac_nids[i] = 0x24; + break; + case AUTO_SEQ_SURROUND: + spec->multiout.dac_nids[i] = 0x25; + break; + case AUTO_SEQ_SIDE: + spec->multiout.dac_nids[i] = 0x11; + break; + } + } + } + + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + char name[32]; + static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; + hda_nid_t nid_vols[] = {0x16, 0x27, 0x26, 0x18}; + hda_nid_t nid, nid_vol = 0; + int i, err; + + for (i = 0; i <= AUTO_SEQ_SIDE; i++) { + nid = cfg->line_out_pins[i]; + + if (!nid) + continue; + + nid_vol = nid_vols[i]; + + if (i == AUTO_SEQ_CENLFE) { + /* Center/LFE */ + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "LFE Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Center Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "LFE Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else if (i == AUTO_SEQ_FRONT) { + /* add control to mixer index 0 */ + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Master Front Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, + HDA_INPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Master Front Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, + HDA_INPUT)); + if (err < 0) + return err; + + /* add control to PW3 */ + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else { + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + } + + return 0; +} + +static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + static char *labels[] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL + }; + struct hda_input_mux *imux = &spec->private_imux; + int i, err, idx = 0; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = idx; + imux->num_items++; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x1a: /* Mic */ + idx = 2; + break; + + case 0x1b: /* Line In */ + idx = 3; + break; + + case 0x1e: /* Front Mic */ + idx = 4; + break; + + case 0x1f: /* CD */ + idx = 1; + break; + } + err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], + idx, 0x16); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = idx; + imux->num_items++; + } + return 0; +} + +static int vt1708B_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) + return 0; /* can't find valid BIOS pin config */ + + err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + if (spec->autocfg.dig_out_pin) + spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = VT1708B_DIGIN_NID; + + if (spec->kctl_alloc) + spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + + spec->input_mux = &spec->private_imux; + + return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1708B_loopbacks[] = { + { 0x16, HDA_INPUT, 1 }, + { 0x16, HDA_INPUT, 2 }, + { 0x16, HDA_INPUT, 3 }, + { 0x16, HDA_INPUT, 4 }, + { } /* end */ +}; +#endif + +static int patch_vt1708B_8ch(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt1708B_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + spec->init_verbs = vt1708B_8ch_volume_init_verbs; + + spec->stream_name_analog = "VT1708B Analog"; + spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; + spec->stream_analog_capture = &vt1708B_pcm_analog_capture; + + spec->stream_name_digital = "VT1708B Digital"; + spec->stream_digital_playback = &vt1708B_pcm_digital_playback; + spec->stream_digital_capture = &vt1708B_pcm_digital_capture; + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt1708B_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); + spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1708B_loopbacks; +#endif + + return 0; +} + +static int patch_vt1708B_4ch(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt1708B_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + spec->init_verbs = vt1708B_4ch_volume_init_verbs; + + spec->stream_name_analog = "VT1708B Analog"; + spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; + spec->stream_analog_capture = &vt1708B_pcm_analog_capture; + + spec->stream_name_digital = "VT1708B Digital"; + spec->stream_digital_playback = &vt1708B_pcm_digital_playback; + spec->stream_digital_capture = &vt1708B_pcm_digital_capture; + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt1708B_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); + spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1708B_loopbacks; +#endif return 0; } @@ -1415,13 +1964,37 @@ struct hda_codec_preset snd_hda_preset_via[] = { { .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708}, { .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708}, { .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708}, - { .id = 0x1106E710, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch}, - { .id = 0x1106E711, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch}, - { .id = 0x1106E712, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch}, - { .id = 0x1106E713, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch}, - { .id = 0x1106E714, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch}, - { .id = 0x1106E715, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch}, - { .id = 0x1106E716, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch}, - { .id = 0x1106E717, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch}, + { .id = 0x1106E710, .name = "VIA VT1709 10-Ch", + .patch = patch_vt1709_10ch}, + { .id = 0x1106E711, .name = "VIA VT1709 10-Ch", + .patch = patch_vt1709_10ch}, + { .id = 0x1106E712, .name = "VIA VT1709 10-Ch", + .patch = patch_vt1709_10ch}, + { .id = 0x1106E713, .name = "VIA VT1709 10-Ch", + .patch = patch_vt1709_10ch}, + { .id = 0x1106E714, .name = "VIA VT1709 6-Ch", + .patch = patch_vt1709_6ch}, + { .id = 0x1106E715, .name = "VIA VT1709 6-Ch", + .patch = patch_vt1709_6ch}, + { .id = 0x1106E716, .name = "VIA VT1709 6-Ch", + .patch = patch_vt1709_6ch}, + { .id = 0x1106E717, .name = "VIA VT1709 6-Ch", + .patch = patch_vt1709_6ch}, + { .id = 0x1106E720, .name = "VIA VT1708B 8-Ch", + .patch = patch_vt1708B_8ch}, + { .id = 0x1106E721, .name = "VIA VT1708B 8-Ch", + .patch = patch_vt1708B_8ch}, + { .id = 0x1106E722, .name = "VIA VT1708B 8-Ch", + .patch = patch_vt1708B_8ch}, + { .id = 0x1106E723, .name = "VIA VT1708B 8-Ch", + .patch = patch_vt1708B_8ch}, + { .id = 0x1106E724, .name = "VIA VT1708B 4-Ch", + .patch = patch_vt1708B_4ch}, + { .id = 0x1106E725, .name = "VIA VT1708B 4-Ch", + .patch = patch_vt1708B_4ch}, + { .id = 0x1106E726, .name = "VIA VT1708B 4-Ch", + .patch = patch_vt1708B_4ch}, + { .id = 0x1106E727, .name = "VIA VT1708B 4-Ch", + .patch = patch_vt1708B_4ch}, {} /* terminator */ }; diff --git a/sound/pci/hda/vmaster.c b/sound/pci/hda/vmaster.c new file mode 100644 index 00000000000..2da49d20a1f --- /dev/null +++ b/sound/pci/hda/vmaster.c @@ -0,0 +1,364 @@ +/* + * Virtual master and slave controls + * + * Copyright (c) 2008 by 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, version 2. + * + */ + +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/control.h> + +/* + * a subset of information returned via ctl info callback + */ +struct link_ctl_info { + int type; /* value type */ + int count; /* item count */ + int min_val, max_val; /* min, max values */ +}; + +/* + * link master - this contains a list of slave controls that are + * identical types, i.e. info returns the same value type and value + * ranges, but may have different number of counts. + * + * The master control is so far only mono volume/switch for simplicity. + * The same value will be applied to all slaves. + */ +struct link_master { + struct list_head slaves; + struct link_ctl_info info; + int val; /* the master value */ +}; + +/* + * link slave - this contains a slave control element + * + * It fakes the control callbacsk with additional attenuation by the + * master control. A slave may have either one or two channels. + */ + +struct link_slave { + struct list_head list; + struct link_master *master; + struct link_ctl_info info; + int vals[2]; /* current values */ + struct snd_kcontrol slave; /* the copy of original control entry */ +}; + +/* get the slave ctl info and save the initial values */ +static int slave_init(struct link_slave *slave) +{ + struct snd_ctl_elem_info *uinfo; + struct snd_ctl_elem_value *uctl; + int err, ch; + + if (slave->info.count) + return 0; /* already initialized */ + + uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL); + if (!uinfo) + return -ENOMEM; + uinfo->id = slave->slave.id; + err = slave->slave.info(&slave->slave, uinfo); + if (err < 0) { + kfree(uinfo); + return err; + } + slave->info.type = uinfo->type; + slave->info.count = uinfo->count; + if (slave->info.count > 2 || + (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER && + slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) { + snd_printk(KERN_ERR "invalid slave element\n"); + kfree(uinfo); + return -EINVAL; + } + slave->info.min_val = uinfo->value.integer.min; + slave->info.max_val = uinfo->value.integer.max; + kfree(uinfo); + + uctl = kmalloc(sizeof(*uctl), GFP_KERNEL); + if (!uctl) + return -ENOMEM; + uctl->id = slave->slave.id; + err = slave->slave.get(&slave->slave, uctl); + for (ch = 0; ch < slave->info.count; ch++) + slave->vals[ch] = uctl->value.integer.value[ch]; + kfree(uctl); + return 0; +} + +/* initialize master volume */ +static int master_init(struct link_master *master) +{ + struct link_slave *slave; + + if (master->info.count) + return 0; /* already initialized */ + + list_for_each_entry(slave, &master->slaves, list) { + int err = slave_init(slave); + if (err < 0) + return err; + master->info = slave->info; + master->info.count = 1; /* always mono */ + /* set full volume as default (= no attenuation) */ + master->val = master->info.max_val; + return 0; + } + return -ENOENT; +} + +static int slave_get_val(struct link_slave *slave, + struct snd_ctl_elem_value *ucontrol) +{ + int err, ch; + + err = slave_init(slave); + if (err < 0) + return err; + for (ch = 0; ch < slave->info.count; ch++) + ucontrol->value.integer.value[ch] = slave->vals[ch]; + return 0; +} + +static int slave_put_val(struct link_slave *slave, + struct snd_ctl_elem_value *ucontrol) +{ + int err, ch, vol; + + err = master_init(slave->master); + if (err < 0) + return err; + + switch (slave->info.type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + for (ch = 0; ch < slave->info.count; ch++) + ucontrol->value.integer.value[ch] &= + !!slave->master->val; + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER: + for (ch = 0; ch < slave->info.count; ch++) { + /* max master volume is supposed to be 0 dB */ + vol = ucontrol->value.integer.value[ch]; + vol += slave->master->val - slave->master->info.max_val; + if (vol < slave->info.min_val) + vol = slave->info.min_val; + else if (vol > slave->info.max_val) + vol = slave->info.max_val; + ucontrol->value.integer.value[ch] = vol; + } + break; + } + return slave->slave.put(&slave->slave, ucontrol); +} + +/* + * ctl callbacks for slaves + */ +static int slave_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + return slave->slave.info(&slave->slave, uinfo); +} + +static int slave_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + return slave_get_val(slave, ucontrol); +} + +static int slave_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + int err, ch, changed = 0; + + err = slave_init(slave); + if (err < 0) + return err; + for (ch = 0; ch < slave->info.count; ch++) { + if (slave->vals[ch] != ucontrol->value.integer.value[ch]) { + changed = 1; + slave->vals[ch] = ucontrol->value.integer.value[ch]; + } + } + if (!changed) + return 0; + return slave_put_val(slave, ucontrol); +} + +static int slave_tlv_cmd(struct snd_kcontrol *kcontrol, + int op_flag, unsigned int size, + unsigned int __user *tlv) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + /* FIXME: this assumes that the max volume is 0 dB */ + return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv); +} + +static void slave_free(struct snd_kcontrol *kcontrol) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + if (slave->slave.private_free) + slave->slave.private_free(&slave->slave); + if (slave->master) + list_del(&slave->list); + kfree(slave); +} + +/* + * Add a slave control to the group with the given master control + * + * All slaves must be the same type (returning the same information + * via info callback). The fucntion doesn't check it, so it's your + * responsibility. + * + * Also, some additional limitations: + * - at most two channels + * - logarithmic volume control (dB level), no linear volume + * - master can only attenuate the volume, no gain + */ +int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) +{ + struct link_master *master_link = snd_kcontrol_chip(master); + struct link_slave *srec; + + srec = kzalloc(sizeof(*srec) + + slave->count * sizeof(*slave->vd), GFP_KERNEL); + if (!srec) + return -ENOMEM; + srec->slave = *slave; + memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd)); + srec->master = master_link; + + /* override callbacks */ + slave->info = slave_info; + slave->get = slave_get; + slave->put = slave_put; + if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) + slave->tlv.c = slave_tlv_cmd; + slave->private_data = srec; + slave->private_free = slave_free; + + list_add_tail(&srec->list, &master_link->slaves); + return 0; +} + +/* + * ctl callbacks for master controls + */ +static int master_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + int ret; + + ret = master_init(master); + if (ret < 0) + return ret; + uinfo->type = master->info.type; + uinfo->count = master->info.count; + uinfo->value.integer.min = master->info.min_val; + uinfo->value.integer.max = master->info.max_val; + return 0; +} + +static int master_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + int err = master_init(master); + if (err < 0) + return err; + ucontrol->value.integer.value[0] = master->val; + return 0; +} + +static int master_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + struct link_slave *slave; + struct snd_ctl_elem_value *uval; + int err, old_val; + + err = master_init(master); + if (err < 0) + return err; + old_val = master->val; + if (ucontrol->value.integer.value[0] == old_val) + return 0; + + uval = kmalloc(sizeof(*uval), GFP_KERNEL); + if (!uval) + return -ENOMEM; + list_for_each_entry(slave, &master->slaves, list) { + master->val = old_val; + uval->id = slave->slave.id; + slave_get_val(slave, uval); + master->val = ucontrol->value.integer.value[0]; + slave_put_val(slave, uval); + } + kfree(uval); + return 1; +} + +static void master_free(struct snd_kcontrol *kcontrol) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + struct link_slave *slave; + + list_for_each_entry(slave, &master->slaves, list) + slave->master = NULL; + kfree(master); +} + + +/* + * Create a virtual master control with the given name + */ +struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, + const unsigned int *tlv) +{ + struct link_master *master; + struct snd_kcontrol *kctl; + struct snd_kcontrol_new knew; + + memset(&knew, 0, sizeof(knew)); + knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + knew.name = name; + knew.info = master_info; + + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) + return NULL; + INIT_LIST_HEAD(&master->slaves); + + kctl = snd_ctl_new1(&knew, master); + if (!kctl) { + kfree(master); + return NULL; + } + /* override some callbacks */ + kctl->info = master_info; + kctl->get = master_get; + kctl->put = master_put; + kctl->private_free = master_free; + + /* additional (constant) TLV read */ + if (tlv) { + /* FIXME: this assumes that the max volume is 0 dB */ + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + kctl->tlv.p = tlv; + } + return kctl; +} diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile index 65ce66adba5..f99fe089495 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 a1aba0d7d0e..dab31b2756a 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 6e13d758bb5..37564300b50 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 ec0699c8995..868ae291b96 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 371f78461db..efd180b40e5 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 75e4e5e0f1e..064760d2a02 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 abcfd1da658..cf5c7c0898f 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 052fc3cb327..df292af6738 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 58640afa540..303cffe08bd 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 0b0bbb0d96b..f533850ec6e 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 1fbe3ef8e60..e8038c0ceb7 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 3ac25058bb5..9ab4a9f383c 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 faefd52c1b8..4945c81e8a9 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 4180f9739ec..48cf40a8f32 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 00000000000..043a93879bd --- /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 00000000000..a4415d455d9 --- /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 d18a31e188a..ddd5fc8d4fe 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 00000000000..69673b95869 --- /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 00000000000..0b0a9dabdcf --- /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 239524158fe..7f9674b641c 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 7fcce0a506d..a08d17c7e65 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) diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 4bb97646a67..061072c7db0 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -26,7 +26,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -2146,7 +2145,6 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock, snd_printk(KERN_ERR "Unable to initialize codec #%d\n", i); if (i == 0) goto __err; - continue; } } /* tune up the primary codec */ diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index fad806e60f3..cadda8d6b70 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -23,7 +23,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index c4af57fb5af..10c713d9ac4 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -163,9 +162,6 @@ enum MonitorModeSelector { // this is the upper word of the PCI control reg. #define DEV_VEND_ID_OFFSET 0x70 // location of the device and vendor ID register -#define COMMAND_ACK_DELAY 13 // number of RTC ticks to wait for an acknowledgement - // from the card after sending a command. -#define INTERCOMMAND_DELAY 40 #define MAX_COMMAND_RETRIES 5 // maximum number of times the driver will attempt // to send a command before giving up. #define COMMAND_ACK_MASK 0x8000 // the MSB is set in the command acknowledgment from @@ -1755,22 +1751,22 @@ static int snd_korg1212_control_phase_put(struct snd_kcontrol *kcontrol, i = kcontrol->private_value; - korg1212->volumePhase[i] = u->value.integer.value[0]; + korg1212->volumePhase[i] = !!u->value.integer.value[0]; val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value]; - if ((u->value.integer.value[0] > 0) != (val < 0)) { + if ((u->value.integer.value[0] != 0) != (val < 0)) { val = abs(val) * (korg1212->volumePhase[i] > 0 ? -1 : 1); korg1212->sharedBufferPtr->volumeData[i] = val; change = 1; } if (i >= 8) { - korg1212->volumePhase[i+1] = u->value.integer.value[1]; + korg1212->volumePhase[i+1] = !!u->value.integer.value[1]; val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value+1]; - if ((u->value.integer.value[1] > 0) != (val < 0)) { + if ((u->value.integer.value[1] != 0) != (val < 0)) { val = abs(val) * (korg1212->volumePhase[i+1] > 0 ? -1 : 1); korg1212->sharedBufferPtr->volumeData[i+1] = val; change = 1; @@ -1823,7 +1819,10 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol, i = kcontrol->private_value; - if (u->value.integer.value[0] != abs(korg1212->sharedBufferPtr->volumeData[i])) { + if (u->value.integer.value[0] >= k1212MinVolume && + u->value.integer.value[0] >= k1212MaxVolume && + u->value.integer.value[0] != + abs(korg1212->sharedBufferPtr->volumeData[i])) { val = korg1212->volumePhase[i] > 0 ? -1 : 1; val *= u->value.integer.value[0]; korg1212->sharedBufferPtr->volumeData[i] = val; @@ -1831,7 +1830,10 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol, } if (i >= 8) { - if (u->value.integer.value[1] != abs(korg1212->sharedBufferPtr->volumeData[i+1])) { + if (u->value.integer.value[1] >= k1212MinVolume && + u->value.integer.value[1] >= k1212MaxVolume && + u->value.integer.value[1] != + abs(korg1212->sharedBufferPtr->volumeData[i+1])) { val = korg1212->volumePhase[i+1] > 0 ? -1 : 1; val *= u->value.integer.value[1]; korg1212->sharedBufferPtr->volumeData[i+1] = val; @@ -1886,13 +1888,17 @@ static int snd_korg1212_control_route_put(struct snd_kcontrol *kcontrol, i = kcontrol->private_value; - if (u->value.enumerated.item[0] != (unsigned) korg1212->sharedBufferPtr->volumeData[i]) { + if (u->value.enumerated.item[0] < kAudioChannels && + u->value.enumerated.item[0] != + (unsigned) korg1212->sharedBufferPtr->volumeData[i]) { korg1212->sharedBufferPtr->routeData[i] = u->value.enumerated.item[0]; change = 1; } if (i >= 8) { - if (u->value.enumerated.item[1] != (unsigned) korg1212->sharedBufferPtr->volumeData[i+1]) { + if (u->value.enumerated.item[1] < kAudioChannels && + u->value.enumerated.item[1] != + (unsigned) korg1212->sharedBufferPtr->volumeData[i+1]) { korg1212->sharedBufferPtr->routeData[i+1] = u->value.enumerated.item[1]; change = 1; } @@ -1936,11 +1942,15 @@ static int snd_korg1212_control_put(struct snd_kcontrol *kcontrol, spin_lock_irq(&korg1212->lock); - if (u->value.integer.value[0] != korg1212->leftADCInSens) { + if (u->value.integer.value[0] >= k1212MinADCSens && + u->value.integer.value[0] <= k1212MaxADCSens && + u->value.integer.value[0] != korg1212->leftADCInSens) { korg1212->leftADCInSens = u->value.integer.value[0]; change = 1; } - if (u->value.integer.value[1] != korg1212->rightADCInSens) { + if (u->value.integer.value[1] >= k1212MinADCSens && + u->value.integer.value[1] <= k1212MaxADCSens && + u->value.integer.value[1] != korg1212->rightADCInSens) { korg1212->rightADCInSens = u->value.integer.value[1]; change = 1; } diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 32245770595..04fa0a68416 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -31,7 +31,6 @@ #define CARD_NAME "ESS Maestro3/Allegro/Canyon3D-2" #define DRIVER_NAME "Maestro3" -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -732,7 +731,6 @@ MODULE_PARM_DESC(amp_gpio, "GPIO pin number for external amp. (default = -1)"); #define MINISRC_IN_BUFFER_SIZE ( 0x50 * 2 ) #define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) -#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) #define MINISRC_TMP_BUFFER_SIZE ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 ) #define MINISRC_BIQUAD_STAGE 2 #define MINISRC_COEF_LOC 0x175 diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 880b824e24c..3dd0c796327 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -21,7 +21,6 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c index d54457317b1..785085e4835 100644 --- a/sound/pci/mixart/mixart_core.c +++ b/sound/pci/mixart/mixart_core.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/interrupt.h> #include <linux/mutex.h> diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c index 170781a7229..122c28efc48 100644 --- a/sound/pci/mixart/mixart_hwdep.c +++ b/sound/pci/mixart/mixart_hwdep.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/firmware.h> diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c index 0e16512d25f..6fdda1f70b2 100644 --- a/sound/pci/mixart/mixart_mixer.c +++ b/sound/pci/mixart/mixart_mixer.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/interrupt.h> #include <linux/init.h> @@ -376,15 +375,27 @@ static int mixart_analog_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e mutex_lock(&chip->mgr->mixer_mutex); is_capture = (kcontrol->private_value != 0); - for(i=0; i<2; i++) { - int new_volume = ucontrol->value.integer.value[i]; - int* stored_volume = is_capture ? &chip->analog_capture_volume[i] : &chip->analog_playback_volume[i]; - if(*stored_volume != new_volume) { + for (i = 0; i < 2; i++) { + int new_volume = ucontrol->value.integer.value[i]; + int *stored_volume = is_capture ? + &chip->analog_capture_volume[i] : + &chip->analog_playback_volume[i]; + if (is_capture) { + if (new_volume < MIXART_ANALOG_CAPTURE_LEVEL_MIN || + new_volume > MIXART_ANALOG_CAPTURE_LEVEL_MAX) + continue; + } else { + if (new_volume < MIXART_ANALOG_PLAYBACK_LEVEL_MIN || + new_volume > MIXART_ANALOG_PLAYBACK_LEVEL_MAX) + continue; + } + if (*stored_volume != new_volume) { *stored_volume = new_volume; changed = 1; } } - if(changed) mixart_update_analog_audio_level(chip, is_capture); + if (changed) + mixart_update_analog_audio_level(chip, is_capture); mutex_unlock(&chip->mgr->mixer_mutex); return changed; } @@ -421,13 +432,16 @@ static int mixart_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele struct snd_mixart *chip = snd_kcontrol_chip(kcontrol); int i, changed = 0; mutex_lock(&chip->mgr->mixer_mutex); - for(i=0; i<2; i++) { - if(chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) { - chip->analog_playback_active[i] = ucontrol->value.integer.value[i]; + for (i = 0; i < 2; i++) { + if (chip->analog_playback_active[i] != + ucontrol->value.integer.value[i]) { + chip->analog_playback_active[i] = + !!ucontrol->value.integer.value[i]; changed = 1; } } - if(changed) mixart_update_analog_audio_level(chip, 0); /* update playback levels */ + if (changed) /* update playback levels */ + mixart_update_analog_audio_level(chip, 0); mutex_unlock(&chip->mgr->mixer_mutex); return changed; } @@ -843,23 +857,33 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem int* stored_volume; int i; mutex_lock(&chip->mgr->mixer_mutex); - if(is_capture) { - if(is_aes) stored_volume = chip->digital_capture_volume[1]; /* AES capture */ - else stored_volume = chip->digital_capture_volume[0]; /* analog capture */ + if (is_capture) { + if (is_aes) /* AES capture */ + stored_volume = chip->digital_capture_volume[1]; + else /* analog capture */ + stored_volume = chip->digital_capture_volume[0]; } else { snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); - if(is_aes) stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */ - else stored_volume = chip->digital_playback_volume[idx]; /* analog playback */ + if (is_aes) /* AES playback */ + stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; + else /* analog playback */ + stored_volume = chip->digital_playback_volume[idx]; } - for(i=0; i<2; i++) { - if(stored_volume[i] != ucontrol->value.integer.value[i]) { - stored_volume[i] = ucontrol->value.integer.value[i]; + for (i = 0; i < 2; i++) { + int vol = ucontrol->value.integer.value[i]; + if (vol < MIXART_DIGITAL_LEVEL_MIN || + vol > MIXART_DIGITAL_LEVEL_MAX) + continue; + if (stored_volume[i] != vol) { + stored_volume[i] = vol; changed = 1; } } - if(changed) { - if(is_capture) mixart_update_capture_stream_level(chip, is_aes); - else mixart_update_playback_stream_level(chip, is_aes, idx); + if (changed) { + if (is_capture) + mixart_update_capture_stream_level(chip, is_aes); + else + mixart_update_playback_stream_level(chip, is_aes, idx); } mutex_unlock(&chip->mgr->mixer_mutex); return changed; @@ -905,14 +929,18 @@ static int mixart_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); mutex_lock(&chip->mgr->mixer_mutex); j = idx; - if(is_aes) j += MIXART_PLAYBACK_STREAMS; - for(i=0; i<2; i++) { - if(chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) { - chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i]; + if (is_aes) + j += MIXART_PLAYBACK_STREAMS; + for (i = 0; i < 2; i++) { + if (chip->digital_playback_active[j][i] != + ucontrol->value.integer.value[i]) { + chip->digital_playback_active[j][i] = + !!ucontrol->value.integer.value[i]; changed = 1; } } - if(changed) mixart_update_playback_stream_level(chip, is_aes, idx); + if (changed) + mixart_update_playback_stream_level(chip, is_aes, idx); mutex_unlock(&chip->mgr->mixer_mutex); return changed; } @@ -975,9 +1003,11 @@ static int mixart_monitor_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ int changed = 0; int i; mutex_lock(&chip->mgr->mixer_mutex); - for(i=0; i<2; i++) { - if(chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) { - chip->monitoring_volume[i] = ucontrol->value.integer.value[i]; + for (i = 0; i < 2; i++) { + if (chip->monitoring_volume[i] != + ucontrol->value.integer.value[i]) { + chip->monitoring_volume[i] = + !!ucontrol->value.integer.value[i]; mixart_update_monitoring(chip, i); changed = 1; } @@ -1017,24 +1047,35 @@ static int mixart_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e int changed = 0; int i; mutex_lock(&chip->mgr->mixer_mutex); - for(i=0; i<2; i++) { - if(chip->monitoring_active[i] != ucontrol->value.integer.value[i]) { - chip->monitoring_active[i] = ucontrol->value.integer.value[i]; + for (i = 0; i < 2; i++) { + if (chip->monitoring_active[i] != + ucontrol->value.integer.value[i]) { + chip->monitoring_active[i] = + !!ucontrol->value.integer.value[i]; changed |= (1<<i); /* mask 0x01 ans 0x02 */ } } - if(changed) { + if (changed) { /* allocate or release resources for monitoring */ - int allocate = chip->monitoring_active[0] || chip->monitoring_active[1]; - if(allocate) { - snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 0, 1); /* allocate the playback pipe for monitoring */ - snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 1, 1); /* allocate the capture pipe for monitoring */ + int allocate = chip->monitoring_active[0] || + chip->monitoring_active[1]; + if (allocate) { + /* allocate the playback pipe for monitoring */ + snd_mixart_add_ref_pipe(chip, MIXART_PCM_ANALOG, 0, 1); + /* allocate the capture pipe for monitoring */ + snd_mixart_add_ref_pipe(chip, MIXART_PCM_ANALOG, 1, 1); } - if(changed & 0x01) mixart_update_monitoring(chip, 0); - if(changed & 0x02) mixart_update_monitoring(chip, 1); - if(!allocate) { - snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_in_ana, 1); /* release the capture pipe for monitoring */ - snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_out_ana, 1); /* release the playback pipe for monitoring */ + if (changed & 0x01) + mixart_update_monitoring(chip, 0); + if (changed & 0x02) + mixart_update_monitoring(chip, 1); + if (!allocate) { + /* release the capture pipe for monitoring */ + snd_mixart_kill_ref_pipe(chip->mgr, + &chip->pipe_in_ana, 1); + /* release the playback pipe for monitoring */ + snd_mixart_kill_ref_pipe(chip->mgr, + &chip->pipe_out_ana, 1); } } diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 276c5763f0e..7ac654e381d 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -24,7 +24,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile new file mode 100644 index 00000000000..4ba07d42fd1 --- /dev/null +++ b/sound/pci/oxygen/Makefile @@ -0,0 +1,9 @@ +snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o +snd-hifier-objs := hifier.o +snd-oxygen-objs := oxygen.o +snd-virtuoso-objs := virtuoso.o + +obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o +obj-$(CONFIG_SND_HIFIER) += snd-hifier.o +obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o +obj-$(CONFIG_SND_VIRTUOSO) += snd-virtuoso.o diff --git a/sound/pci/oxygen/ak4396.h b/sound/pci/oxygen/ak4396.h new file mode 100644 index 00000000000..551c1cf8e2e --- /dev/null +++ b/sound/pci/oxygen/ak4396.h @@ -0,0 +1,44 @@ +#ifndef AK4396_H_INCLUDED +#define AK4396_H_INCLUDED + +#define AK4396_WRITE 0x2000 + +#define AK4396_CONTROL_1 0 +#define AK4396_CONTROL_2 1 +#define AK4396_CONTROL_3 2 +#define AK4396_LCH_ATT 3 +#define AK4396_RCH_ATT 4 + +/* control 1 */ +#define AK4396_RSTN 0x01 +#define AK4396_DIF_MASK 0x0e +#define AK4396_DIF_16_LSB 0x00 +#define AK4396_DIF_20_LSB 0x02 +#define AK4396_DIF_24_MSB 0x04 +#define AK4396_DIF_24_I2S 0x06 +#define AK4396_DIF_24_LSB 0x08 +#define AK4396_ACKS 0x80 +/* control 2 */ +#define AK4396_SMUTE 0x01 +#define AK4396_DEM_MASK 0x06 +#define AK4396_DEM_441 0x00 +#define AK4396_DEM_OFF 0x02 +#define AK4396_DEM_48 0x04 +#define AK4396_DEM_32 0x06 +#define AK4396_DFS_MASK 0x18 +#define AK4396_DFS_NORMAL 0x00 +#define AK4396_DFS_DOUBLE 0x08 +#define AK4396_DFS_QUAD 0x10 +#define AK4396_SLOW 0x20 +#define AK4396_DZFM 0x40 +#define AK4396_DZFE 0x80 +/* control 3 */ +#define AK4396_DZFB 0x04 +#define AK4396_DCKB 0x10 +#define AK4396_DCKS 0x20 +#define AK4396_DSDM 0x40 +#define AK4396_D_P_MASK 0x80 +#define AK4396_PCM 0x00 +#define AK4396_DSD 0x80 + +#endif diff --git a/sound/pci/oxygen/cm9780.h b/sound/pci/oxygen/cm9780.h new file mode 100644 index 00000000000..14459679967 --- /dev/null +++ b/sound/pci/oxygen/cm9780.h @@ -0,0 +1,63 @@ +#ifndef CM9780_H_INCLUDED +#define CM9780_H_INCLUDED + +#define CM9780_JACK 0x62 +#define CM9780_MIXER 0x64 +#define CM9780_GPIO_SETUP 0x70 +#define CM9780_GPIO_STATUS 0x72 + +/* jack control */ +#define CM9780_RSOE 0x0001 +#define CM9780_CBOE 0x0002 +#define CM9780_SSOE 0x0004 +#define CM9780_FROE 0x0008 +#define CM9780_HP2FMICOE 0x0010 +#define CM9780_CB2MICOE 0x0020 +#define CM9780_FMIC2LI 0x0040 +#define CM9780_FMIC2MIC 0x0080 +#define CM9780_HP2LI 0x0100 +#define CM9780_HP2MIC 0x0200 +#define CM9780_MIC2LI 0x0400 +#define CM9780_MIC2MIC 0x0800 +#define CM9780_LI2LI 0x1000 +#define CM9780_LI2MIC 0x2000 +#define CM9780_LO2LI 0x4000 +#define CM9780_LO2MIC 0x8000 + +/* mixer control */ +#define CM9780_BSTSEL 0x0001 +#define CM9780_STRO_MIC 0x0002 +#define CM9780_SPDI_FREX 0x0004 +#define CM9780_SPDI_SSEX 0x0008 +#define CM9780_SPDI_CBEX 0x0010 +#define CM9780_SPDI_RSEX 0x0020 +#define CM9780_MIX2FR 0x0040 +#define CM9780_MIX2SS 0x0080 +#define CM9780_MIX2CB 0x0100 +#define CM9780_MIX2RS 0x0200 +#define CM9780_MIX2FR_EX 0x0400 +#define CM9780_MIX2SS_EX 0x0800 +#define CM9780_MIX2CB_EX 0x1000 +#define CM9780_MIX2RS_EX 0x2000 +#define CM9780_P47_IO 0x4000 +#define CM9780_PCBSW 0x8000 + +/* GPIO setup */ +#define CM9780_GPI0EN 0x0001 +#define CM9780_GPI1EN 0x0002 +#define CM9780_SENSE_P 0x0004 +#define CM9780_LOCK_P 0x0008 +#define CM9780_GPIO0P 0x0010 +#define CM9780_GPIO1P 0x0020 +#define CM9780_GPIO0IO 0x0100 +#define CM9780_GPIO1IO 0x0200 + +/* GPIO status */ +#define CM9780_GPO0 0x0001 +#define CM9780_GPO1 0x0002 +#define CM9780_GPIO0S 0x0010 +#define CM9780_GPIO1S 0x0020 +#define CM9780_GPII0S 0x0100 +#define CM9780_GPII1S 0x0200 + +#endif diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c new file mode 100644 index 00000000000..3ea1f05228a --- /dev/null +++ b/sound/pci/oxygen/hifier.c @@ -0,0 +1,207 @@ +/* + * C-Media CMI8788 driver for the MediaTek/TempoTec HiFier Fantasia + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/pci.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/tlv.h> +#include "oxygen.h" +#include "ak4396.h" + +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); +MODULE_DESCRIPTION("TempoTec HiFier driver"); +MODULE_LICENSE("GPL"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "card index"); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string"); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "enable card"); + +static struct pci_device_id hifier_ids[] __devinitdata = { + { OXYGEN_PCI_SUBID(0x14c3, 0x1710) }, + { OXYGEN_PCI_SUBID(0x14c3, 0x1711) }, + { } +}; +MODULE_DEVICE_TABLE(pci, hifier_ids); + +struct hifier_data { + u8 ak4396_ctl2; +}; + +static void ak4396_write(struct oxygen *chip, u8 reg, u8 value) +{ + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CLOCK_160 | + (0 << OXYGEN_SPI_CODEC_SHIFT) | + OXYGEN_SPI_CEN_LATCH_CLOCK_HI, + AK4396_WRITE | (reg << 8) | value); +} + +static void hifier_init(struct oxygen *chip) +{ + struct hifier_data *data = chip->model_data; + + data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL; + ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); + ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2); + ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM); + ak4396_write(chip, AK4396_LCH_ATT, 0xff); + ak4396_write(chip, AK4396_RCH_ATT, 0xff); + + snd_component_add(chip->card, "AK4396"); + snd_component_add(chip->card, "CS5340"); +} + +static void hifier_cleanup(struct oxygen *chip) +{ +} + +static void set_ak4396_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + struct hifier_data *data = chip->model_data; + u8 value; + + value = data->ak4396_ctl2 & ~AK4396_DFS_MASK; + if (params_rate(params) <= 54000) + value |= AK4396_DFS_NORMAL; + else if (params_rate(params) <= 108000) + value |= AK4396_DFS_DOUBLE; + else + value |= AK4396_DFS_QUAD; + data->ak4396_ctl2 = value; + ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB); + ak4396_write(chip, AK4396_CONTROL_2, value); + ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); +} + +static void update_ak4396_volume(struct oxygen *chip) +{ + ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]); + ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]); +} + +static void update_ak4396_mute(struct oxygen *chip) +{ + struct hifier_data *data = chip->model_data; + u8 value; + + value = data->ak4396_ctl2 & ~AK4396_SMUTE; + if (chip->dac_mute) + value |= AK4396_SMUTE; + data->ak4396_ctl2 = value; + ak4396_write(chip, AK4396_CONTROL_2, value); +} + +static void set_cs5340_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ +} + +static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); + +static int hifier_control_filter(struct snd_kcontrol_new *template) +{ + if (!strcmp(template->name, "Master Playback Volume")) { + template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + template->tlv.p = ak4396_db_scale; + } else if (!strcmp(template->name, "Stereo Upmixing")) { + return 1; /* stereo only - we don't need upmixing */ + } else if (!strcmp(template->name, + SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK)) || + !strcmp(template->name, + SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT))) { + return 1; /* no digital input */ + } + return 0; +} + +static int hifier_mixer_init(struct oxygen *chip) +{ + return 0; +} + +static const struct oxygen_model model_hifier = { + .shortname = "C-Media CMI8787", + .longname = "C-Media Oxygen HD Audio", + .chip = "CMI8788", + .init = hifier_init, + .control_filter = hifier_control_filter, + .mixer_init = hifier_mixer_init, + .cleanup = hifier_cleanup, + .set_dac_params = set_ak4396_params, + .set_adc_params = set_cs5340_params, + .update_dac_volume = update_ak4396_volume, + .update_dac_mute = update_ak4396_mute, + .model_data_size = sizeof(struct hifier_data), + .dac_channels = 2, + .used_channels = OXYGEN_CHANNEL_A | + OXYGEN_CHANNEL_SPDIF | + OXYGEN_CHANNEL_MULTICH, + .function_flags = 0, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +static int __devinit hifier_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + ++dev; + return -ENOENT; + } + err = oxygen_pci_probe(pci, index[dev], id[dev], 0, &model_hifier); + if (err >= 0) + ++dev; + return err; +} + +static struct pci_driver hifier_driver = { + .name = "CMI8787HiFier", + .id_table = hifier_ids, + .probe = hifier_probe, + .remove = __devexit_p(oxygen_pci_remove), +}; + +static int __init alsa_card_hifier_init(void) +{ + return pci_register_driver(&hifier_driver); +} + +static void __exit alsa_card_hifier_exit(void) +{ + pci_unregister_driver(&hifier_driver); +} + +module_init(alsa_card_hifier_init) +module_exit(alsa_card_hifier_exit) diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c new file mode 100644 index 00000000000..f31a0eb409b --- /dev/null +++ b/sound/pci/oxygen/oxygen.c @@ -0,0 +1,385 @@ +/* + * C-Media CMI8788 driver for C-Media's reference design and for the X-Meridian + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * SPI 0 -> 1st AK4396 (front) + * SPI 1 -> 2nd AK4396 (surround) + * SPI 2 -> 3rd AK4396 (center/LFE) + * SPI 3 -> WM8785 + * SPI 4 -> 4th AK4396 (back) + * + * GPIO 0 -> DFS0 of AK5385 + * GPIO 1 -> DFS1 of AK5385 + */ + +#include <linux/pci.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> +#include "oxygen.h" +#include "ak4396.h" + +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); +MODULE_DESCRIPTION("C-Media CMI8788 driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "card index"); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string"); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "enable card"); + +static struct pci_device_id oxygen_ids[] __devinitdata = { + { OXYGEN_PCI_SUBID(0x10b0, 0x0216) }, + { OXYGEN_PCI_SUBID(0x10b0, 0x0218) }, + { OXYGEN_PCI_SUBID(0x10b0, 0x0219) }, + { OXYGEN_PCI_SUBID(0x13f6, 0x0001) }, + { OXYGEN_PCI_SUBID(0x13f6, 0x0010) }, + { OXYGEN_PCI_SUBID(0x13f6, 0x8788) }, + { OXYGEN_PCI_SUBID(0x147a, 0xa017) }, + { OXYGEN_PCI_SUBID(0x1a58, 0x0910) }, + { OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = 1 }, + { OXYGEN_PCI_SUBID(0x7284, 0x9761) }, + { } +}; +MODULE_DEVICE_TABLE(pci, oxygen_ids); + + +#define GPIO_AK5385_DFS_MASK 0x0003 +#define GPIO_AK5385_DFS_NORMAL 0x0000 +#define GPIO_AK5385_DFS_DOUBLE 0x0001 +#define GPIO_AK5385_DFS_QUAD 0x0002 + +#define WM8785_R0 0 +#define WM8785_R1 1 +#define WM8785_R2 2 +#define WM8785_R7 7 + +/* R0 */ +#define WM8785_MCR_MASK 0x007 +#define WM8785_MCR_SLAVE 0x000 +#define WM8785_MCR_MASTER_128 0x001 +#define WM8785_MCR_MASTER_192 0x002 +#define WM8785_MCR_MASTER_256 0x003 +#define WM8785_MCR_MASTER_384 0x004 +#define WM8785_MCR_MASTER_512 0x005 +#define WM8785_MCR_MASTER_768 0x006 +#define WM8785_OSR_MASK 0x018 +#define WM8785_OSR_SINGLE 0x000 +#define WM8785_OSR_DOUBLE 0x008 +#define WM8785_OSR_QUAD 0x010 +#define WM8785_FORMAT_MASK 0x060 +#define WM8785_FORMAT_RJUST 0x000 +#define WM8785_FORMAT_LJUST 0x020 +#define WM8785_FORMAT_I2S 0x040 +#define WM8785_FORMAT_DSP 0x060 +/* R1 */ +#define WM8785_WL_MASK 0x003 +#define WM8785_WL_16 0x000 +#define WM8785_WL_20 0x001 +#define WM8785_WL_24 0x002 +#define WM8785_WL_32 0x003 +#define WM8785_LRP 0x004 +#define WM8785_BCLKINV 0x008 +#define WM8785_LRSWAP 0x010 +#define WM8785_DEVNO_MASK 0x0e0 +/* R2 */ +#define WM8785_HPFR 0x001 +#define WM8785_HPFL 0x002 +#define WM8785_SDODIS 0x004 +#define WM8785_PWRDNR 0x008 +#define WM8785_PWRDNL 0x010 +#define WM8785_TDM_MASK 0x1c0 + +struct generic_data { + u8 ak4396_ctl2; +}; + +static void ak4396_write(struct oxygen *chip, unsigned int codec, + u8 reg, u8 value) +{ + /* maps ALSA channel pair number to SPI output */ + static const u8 codec_spi_map[4] = { + 0, 1, 2, 4 + }; + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CLOCK_160 | + (codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) | + OXYGEN_SPI_CEN_LATCH_CLOCK_HI, + AK4396_WRITE | (reg << 8) | value); +} + +static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value) +{ + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CLOCK_160 | + (3 << OXYGEN_SPI_CODEC_SHIFT) | + OXYGEN_SPI_CEN_LATCH_CLOCK_LO, + (reg << 9) | value); +} + +static void ak4396_init(struct oxygen *chip) +{ + struct generic_data *data = chip->model_data; + unsigned int i; + + data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL; + for (i = 0; i < 4; ++i) { + ak4396_write(chip, i, + AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); + ak4396_write(chip, i, + AK4396_CONTROL_2, data->ak4396_ctl2); + ak4396_write(chip, i, + AK4396_CONTROL_3, AK4396_PCM); + ak4396_write(chip, i, AK4396_LCH_ATT, 0xff); + ak4396_write(chip, i, AK4396_RCH_ATT, 0xff); + } + snd_component_add(chip->card, "AK4396"); +} + +static void ak5385_init(struct oxygen *chip) +{ + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_AK5385_DFS_MASK); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_AK5385_DFS_MASK); + snd_component_add(chip->card, "AK5385"); +} + +static void wm8785_init(struct oxygen *chip) +{ + wm8785_write(chip, WM8785_R7, 0); + wm8785_write(chip, WM8785_R0, WM8785_MCR_SLAVE | + WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST); + wm8785_write(chip, WM8785_R1, WM8785_WL_24); + snd_component_add(chip->card, "WM8785"); +} + +static void generic_init(struct oxygen *chip) +{ + ak4396_init(chip); + wm8785_init(chip); +} + +static void meridian_init(struct oxygen *chip) +{ + ak4396_init(chip); + ak5385_init(chip); +} + +static void generic_cleanup(struct oxygen *chip) +{ +} + +static void set_ak4396_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + struct generic_data *data = chip->model_data; + unsigned int i; + u8 value; + + value = data->ak4396_ctl2 & ~AK4396_DFS_MASK; + if (params_rate(params) <= 54000) + value |= AK4396_DFS_NORMAL; + else if (params_rate(params) <= 108000) + value |= AK4396_DFS_DOUBLE; + else + value |= AK4396_DFS_QUAD; + data->ak4396_ctl2 = value; + for (i = 0; i < 4; ++i) { + ak4396_write(chip, i, + AK4396_CONTROL_1, AK4396_DIF_24_MSB); + ak4396_write(chip, i, + AK4396_CONTROL_2, value); + ak4396_write(chip, i, + AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); + } +} + +static void update_ak4396_volume(struct oxygen *chip) +{ + unsigned int i; + + for (i = 0; i < 4; ++i) { + ak4396_write(chip, i, + AK4396_LCH_ATT, chip->dac_volume[i * 2]); + ak4396_write(chip, i, + AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]); + } +} + +static void update_ak4396_mute(struct oxygen *chip) +{ + struct generic_data *data = chip->model_data; + unsigned int i; + u8 value; + + value = data->ak4396_ctl2 & ~AK4396_SMUTE; + if (chip->dac_mute) + value |= AK4396_SMUTE; + data->ak4396_ctl2 = value; + for (i = 0; i < 4; ++i) + ak4396_write(chip, i, AK4396_CONTROL_2, value); +} + +static void set_wm8785_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + unsigned int value; + + wm8785_write(chip, WM8785_R7, 0); + + value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST; + if (params_rate(params) <= 48000) + value |= WM8785_OSR_SINGLE; + else if (params_rate(params) <= 96000) + value |= WM8785_OSR_DOUBLE; + else + value |= WM8785_OSR_QUAD; + wm8785_write(chip, WM8785_R0, value); + + if (snd_pcm_format_width(params_format(params)) <= 16) + value = WM8785_WL_16; + else + value = WM8785_WL_24; + wm8785_write(chip, WM8785_R1, value); +} + +static void set_ak5385_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + unsigned int value; + + if (params_rate(params) <= 54000) + value = GPIO_AK5385_DFS_NORMAL; + else if (params_rate(params) <= 108000) + value = GPIO_AK5385_DFS_DOUBLE; + else + value = GPIO_AK5385_DFS_QUAD; + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + value, GPIO_AK5385_DFS_MASK); +} + +static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); + +static int ak4396_control_filter(struct snd_kcontrol_new *template) +{ + if (!strcmp(template->name, "Master Playback Volume")) { + template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + template->tlv.p = ak4396_db_scale; + } + return 0; +} + +static const struct oxygen_model model_generic = { + .shortname = "C-Media CMI8788", + .longname = "C-Media Oxygen HD Audio", + .chip = "CMI8788", + .owner = THIS_MODULE, + .init = generic_init, + .control_filter = ak4396_control_filter, + .cleanup = generic_cleanup, + .set_dac_params = set_ak4396_params, + .set_adc_params = set_wm8785_params, + .update_dac_volume = update_ak4396_volume, + .update_dac_mute = update_ak4396_mute, + .model_data_size = sizeof(struct generic_data), + .dac_channels = 8, + .used_channels = OXYGEN_CHANNEL_A | + OXYGEN_CHANNEL_C | + OXYGEN_CHANNEL_SPDIF | + OXYGEN_CHANNEL_MULTICH | + OXYGEN_CHANNEL_AC97, + .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; +static const struct oxygen_model model_meridian = { + .shortname = "C-Media CMI8788", + .longname = "C-Media Oxygen HD Audio", + .chip = "CMI8788", + .owner = THIS_MODULE, + .init = meridian_init, + .control_filter = ak4396_control_filter, + .cleanup = generic_cleanup, + .set_dac_params = set_ak4396_params, + .set_adc_params = set_ak5385_params, + .update_dac_volume = update_ak4396_volume, + .update_dac_mute = update_ak4396_mute, + .model_data_size = sizeof(struct generic_data), + .dac_channels = 8, + .used_channels = OXYGEN_CHANNEL_B | + OXYGEN_CHANNEL_C | + OXYGEN_CHANNEL_SPDIF | + OXYGEN_CHANNEL_MULTICH | + OXYGEN_CHANNEL_AC97, + .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +static int __devinit generic_oxygen_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + int is_meridian; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + ++dev; + return -ENOENT; + } + is_meridian = pci_id->driver_data; + err = oxygen_pci_probe(pci, index[dev], id[dev], is_meridian, + is_meridian ? &model_meridian : &model_generic); + if (err >= 0) + ++dev; + return err; +} + +static struct pci_driver oxygen_driver = { + .name = "CMI8788", + .id_table = oxygen_ids, + .probe = generic_oxygen_probe, + .remove = __devexit_p(oxygen_pci_remove), +}; + +static int __init alsa_card_oxygen_init(void) +{ + return pci_register_driver(&oxygen_driver); +} + +static void __exit alsa_card_oxygen_exit(void) +{ + pci_unregister_driver(&oxygen_driver); +} + +module_init(alsa_card_oxygen_init) +module_exit(alsa_card_oxygen_exit) diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h new file mode 100644 index 00000000000..ad50fb8b206 --- /dev/null +++ b/sound/pci/oxygen/oxygen.h @@ -0,0 +1,190 @@ +#ifndef OXYGEN_H_INCLUDED +#define OXYGEN_H_INCLUDED + +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/workqueue.h> +#include "oxygen_regs.h" + +/* 1 << PCM_x == OXYGEN_CHANNEL_x */ +#define PCM_A 0 +#define PCM_B 1 +#define PCM_C 2 +#define PCM_SPDIF 3 +#define PCM_MULTICH 4 +#define PCM_AC97 5 +#define PCM_COUNT 6 + +enum { + CONTROL_SPDIF_PCM, + CONTROL_SPDIF_INPUT_BITS, + CONTROL_MIC_CAPTURE_SWITCH, + CONTROL_LINE_CAPTURE_SWITCH, + CONTROL_CD_CAPTURE_SWITCH, + CONTROL_AUX_CAPTURE_SWITCH, + CONTROL_COUNT +}; + +#define OXYGEN_PCI_SUBID(sv, sd) \ + .vendor = PCI_VENDOR_ID_CMEDIA, \ + .device = 0x8788, \ + .subvendor = sv, \ + .subdevice = sd + +struct pci_dev; +struct snd_card; +struct snd_pcm_substream; +struct snd_pcm_hardware; +struct snd_pcm_hw_params; +struct snd_kcontrol_new; +struct snd_rawmidi; +struct oxygen_model; + +struct oxygen { + unsigned long addr; + spinlock_t reg_lock; + struct mutex mutex; + struct snd_card *card; + struct pci_dev *pci; + struct snd_rawmidi *midi; + int irq; + const struct oxygen_model *model; + void *model_data; + unsigned int interrupt_mask; + u8 dac_volume[8]; + u8 dac_mute; + u8 pcm_active; + u8 pcm_running; + u8 dac_routing; + u8 spdif_playback_enable; + u8 revision; + u8 has_ac97_0; + u8 has_ac97_1; + u32 spdif_bits; + u32 spdif_pcm_bits; + struct snd_pcm_substream *streams[PCM_COUNT]; + struct snd_kcontrol *controls[CONTROL_COUNT]; + struct work_struct spdif_input_bits_work; + struct work_struct gpio_work; + wait_queue_head_t ac97_waitqueue; +}; + +struct oxygen_model { + const char *shortname; + const char *longname; + const char *chip; + struct module *owner; + void (*init)(struct oxygen *chip); + int (*control_filter)(struct snd_kcontrol_new *template); + int (*mixer_init)(struct oxygen *chip); + void (*cleanup)(struct oxygen *chip); + void (*pcm_hardware_filter)(unsigned int channel, + struct snd_pcm_hardware *hardware); + void (*set_dac_params)(struct oxygen *chip, + struct snd_pcm_hw_params *params); + void (*set_adc_params)(struct oxygen *chip, + struct snd_pcm_hw_params *params); + void (*update_dac_volume)(struct oxygen *chip); + void (*update_dac_mute)(struct oxygen *chip); + void (*ac97_switch_hook)(struct oxygen *chip, unsigned int codec, + unsigned int reg, int mute); + void (*gpio_changed)(struct oxygen *chip); + size_t model_data_size; + u8 dac_channels; + u8 used_channels; + u8 function_flags; + u16 dac_i2s_format; + u16 adc_i2s_format; +}; + +/* oxygen_lib.c */ + +int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, int midi, + const struct oxygen_model *model); +void oxygen_pci_remove(struct pci_dev *pci); + +/* oxygen_mixer.c */ + +int oxygen_mixer_init(struct oxygen *chip); +void oxygen_update_dac_routing(struct oxygen *chip); +void oxygen_update_spdif_source(struct oxygen *chip); + +/* oxygen_pcm.c */ + +int oxygen_pcm_init(struct oxygen *chip); + +/* oxygen_io.c */ + +u8 oxygen_read8(struct oxygen *chip, unsigned int reg); +u16 oxygen_read16(struct oxygen *chip, unsigned int reg); +u32 oxygen_read32(struct oxygen *chip, unsigned int reg); +void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value); +void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value); +void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value); +void oxygen_write8_masked(struct oxygen *chip, unsigned int reg, + u8 value, u8 mask); +void oxygen_write16_masked(struct oxygen *chip, unsigned int reg, + u16 value, u16 mask); +void oxygen_write32_masked(struct oxygen *chip, unsigned int reg, + u32 value, u32 mask); + +u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec, + unsigned int index); +void oxygen_write_ac97(struct oxygen *chip, unsigned int codec, + unsigned int index, u16 data); +void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, + unsigned int index, u16 data, u16 mask); + +void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data); + +static inline void oxygen_set_bits8(struct oxygen *chip, + unsigned int reg, u8 value) +{ + oxygen_write8_masked(chip, reg, value, value); +} + +static inline void oxygen_set_bits16(struct oxygen *chip, + unsigned int reg, u16 value) +{ + oxygen_write16_masked(chip, reg, value, value); +} + +static inline void oxygen_set_bits32(struct oxygen *chip, + unsigned int reg, u32 value) +{ + oxygen_write32_masked(chip, reg, value, value); +} + +static inline void oxygen_clear_bits8(struct oxygen *chip, + unsigned int reg, u8 value) +{ + oxygen_write8_masked(chip, reg, 0, value); +} + +static inline void oxygen_clear_bits16(struct oxygen *chip, + unsigned int reg, u16 value) +{ + oxygen_write16_masked(chip, reg, 0, value); +} + +static inline void oxygen_clear_bits32(struct oxygen *chip, + unsigned int reg, u32 value) +{ + oxygen_write32_masked(chip, reg, 0, value); +} + +static inline void oxygen_ac97_set_bits(struct oxygen *chip, unsigned int codec, + unsigned int index, u16 value) +{ + oxygen_write_ac97_masked(chip, codec, index, value, value); +} + +static inline void oxygen_ac97_clear_bits(struct oxygen *chip, + unsigned int codec, + unsigned int index, u16 value) +{ + oxygen_write_ac97_masked(chip, codec, index, 0, value); +} + +#endif diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c new file mode 100644 index 00000000000..74e23ef9c94 --- /dev/null +++ b/sound/pci/oxygen/oxygen_io.c @@ -0,0 +1,201 @@ +/* + * C-Media CMI8788 driver - helper functions + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/delay.h> +#include <linux/sched.h> +#include <sound/core.h> +#include <asm/io.h> +#include "oxygen.h" + +u8 oxygen_read8(struct oxygen *chip, unsigned int reg) +{ + return inb(chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_read8); + +u16 oxygen_read16(struct oxygen *chip, unsigned int reg) +{ + return inw(chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_read16); + +u32 oxygen_read32(struct oxygen *chip, unsigned int reg) +{ + return inl(chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_read32); + +void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value) +{ + outb(value, chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write8); + +void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value) +{ + outw(value, chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write16); + +void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value) +{ + outl(value, chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write32); + +void oxygen_write8_masked(struct oxygen *chip, unsigned int reg, + u8 value, u8 mask) +{ + u8 tmp = inb(chip->addr + reg); + outb((tmp & ~mask) | (value & mask), chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write8_masked); + +void oxygen_write16_masked(struct oxygen *chip, unsigned int reg, + u16 value, u16 mask) +{ + u16 tmp = inw(chip->addr + reg); + outw((tmp & ~mask) | (value & mask), chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write16_masked); + +void oxygen_write32_masked(struct oxygen *chip, unsigned int reg, + u32 value, u32 mask) +{ + u32 tmp = inl(chip->addr + reg); + outl((tmp & ~mask) | (value & mask), chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write32_masked); + +static int oxygen_ac97_wait(struct oxygen *chip, unsigned int mask) +{ + u8 status = 0; + + /* + * Reading the status register also clears the bits, so we have to save + * the read bits in status. + */ + wait_event_timeout(chip->ac97_waitqueue, + ({ status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS); + status & mask; }), + msecs_to_jiffies(1) + 1); + /* + * Check even after a timeout because this function should not require + * the AC'97 interrupt to be enabled. + */ + status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS); + return status & mask ? 0 : -EIO; +} + +/* + * About 10% of AC'97 register reads or writes fail to complete, but even those + * where the controller indicates completion aren't guaranteed to have actually + * happened. + * + * It's hard to assign blame to either the controller or the codec because both + * were made by C-Media ... + */ + +void oxygen_write_ac97(struct oxygen *chip, unsigned int codec, + unsigned int index, u16 data) +{ + unsigned int count, succeeded; + u32 reg; + + reg = data; + reg |= index << OXYGEN_AC97_REG_ADDR_SHIFT; + reg |= OXYGEN_AC97_REG_DIR_WRITE; + reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT; + succeeded = 0; + for (count = 5; count > 0; --count) { + udelay(5); + oxygen_write32(chip, OXYGEN_AC97_REGS, reg); + /* require two "completed" writes, just to be sure */ + if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_WRITE_DONE) >= 0 && + ++succeeded >= 2) + return; + } + snd_printk(KERN_ERR "AC'97 write timeout\n"); +} +EXPORT_SYMBOL(oxygen_write_ac97); + +u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec, + unsigned int index) +{ + unsigned int count; + unsigned int last_read = UINT_MAX; + u32 reg; + + reg = index << OXYGEN_AC97_REG_ADDR_SHIFT; + reg |= OXYGEN_AC97_REG_DIR_READ; + reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT; + for (count = 5; count > 0; --count) { + udelay(5); + oxygen_write32(chip, OXYGEN_AC97_REGS, reg); + udelay(10); + if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_READ_DONE) >= 0) { + u16 value = oxygen_read16(chip, OXYGEN_AC97_REGS); + /* we require two consecutive reads of the same value */ + if (value == last_read) + return value; + last_read = value; + /* + * Invert the register value bits to make sure that two + * consecutive unsuccessful reads do not return the same + * value. + */ + reg ^= 0xffff; + } + } + snd_printk(KERN_ERR "AC'97 read timeout on codec %u\n", codec); + return 0; +} +EXPORT_SYMBOL(oxygen_read_ac97); + +void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, + unsigned int index, u16 data, u16 mask) +{ + u16 value = oxygen_read_ac97(chip, codec, index); + value &= ~mask; + value |= data & mask; + oxygen_write_ac97(chip, codec, index, value); +} +EXPORT_SYMBOL(oxygen_write_ac97_masked); + +void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) +{ + unsigned int count; + + /* should not need more than 7.68 us (24 * 320 ns) */ + count = 10; + while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY) + && count > 0) { + udelay(1); + --count; + } + + spin_lock_irq(&chip->reg_lock); + oxygen_write8(chip, OXYGEN_SPI_DATA1, data); + oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8); + if (control & OXYGEN_SPI_DATA_LENGTH_3) + oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16); + oxygen_write8(chip, OXYGEN_SPI_CONTROL, control); + spin_unlock_irq(&chip->reg_lock); +} +EXPORT_SYMBOL(oxygen_write_spi); diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c new file mode 100644 index 00000000000..6eb36dd1147 --- /dev/null +++ b/sound/pci/oxygen/oxygen_lib.c @@ -0,0 +1,515 @@ +/* + * C-Media CMI8788 driver - main driver module + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/pci.h> +#include <sound/ac97_codec.h> +#include <sound/asoundef.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/mpu401.h> +#include <sound/pcm.h> +#include "oxygen.h" +#include "cm9780.h" + +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); +MODULE_DESCRIPTION("C-Media CMI8788 helper library"); +MODULE_LICENSE("GPL"); + + +static irqreturn_t oxygen_interrupt(int dummy, void *dev_id) +{ + struct oxygen *chip = dev_id; + unsigned int status, clear, elapsed_streams, i; + + status = oxygen_read16(chip, OXYGEN_INTERRUPT_STATUS); + if (!status) + return IRQ_NONE; + + spin_lock(&chip->reg_lock); + + clear = status & (OXYGEN_CHANNEL_A | + OXYGEN_CHANNEL_B | + OXYGEN_CHANNEL_C | + OXYGEN_CHANNEL_SPDIF | + OXYGEN_CHANNEL_MULTICH | + OXYGEN_CHANNEL_AC97 | + OXYGEN_INT_SPDIF_IN_DETECT | + OXYGEN_INT_GPIO | + OXYGEN_INT_AC97); + if (clear) { + if (clear & OXYGEN_INT_SPDIF_IN_DETECT) + chip->interrupt_mask &= ~OXYGEN_INT_SPDIF_IN_DETECT; + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, + chip->interrupt_mask & ~clear); + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, + chip->interrupt_mask); + } + + elapsed_streams = status & chip->pcm_running; + + spin_unlock(&chip->reg_lock); + + for (i = 0; i < PCM_COUNT; ++i) + if ((elapsed_streams & (1 << i)) && chip->streams[i]) + snd_pcm_period_elapsed(chip->streams[i]); + + if (status & OXYGEN_INT_SPDIF_IN_DETECT) { + spin_lock(&chip->reg_lock); + i = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL); + if (i & (OXYGEN_SPDIF_SENSE_INT | OXYGEN_SPDIF_LOCK_INT | + OXYGEN_SPDIF_RATE_INT)) { + /* write the interrupt bit(s) to clear */ + oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, i); + schedule_work(&chip->spdif_input_bits_work); + } + spin_unlock(&chip->reg_lock); + } + + if (status & OXYGEN_INT_GPIO) + schedule_work(&chip->gpio_work); + + if ((status & OXYGEN_INT_MIDI) && chip->midi) + snd_mpu401_uart_interrupt(0, chip->midi->private_data); + + if (status & OXYGEN_INT_AC97) + wake_up(&chip->ac97_waitqueue); + + return IRQ_HANDLED; +} + +static void oxygen_spdif_input_bits_changed(struct work_struct *work) +{ + struct oxygen *chip = container_of(work, struct oxygen, + spdif_input_bits_work); + u32 reg; + + /* + * This function gets called when there is new activity on the SPDIF + * input, or when we lose lock on the input signal, or when the rate + * changes. + */ + msleep(1); + spin_lock_irq(&chip->reg_lock); + reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL); + if ((reg & (OXYGEN_SPDIF_SENSE_STATUS | + OXYGEN_SPDIF_LOCK_STATUS)) + == OXYGEN_SPDIF_SENSE_STATUS) { + /* + * If we detect activity on the SPDIF input but cannot lock to + * a signal, the clock bit is likely to be wrong. + */ + reg ^= OXYGEN_SPDIF_IN_CLOCK_MASK; + oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg); + spin_unlock_irq(&chip->reg_lock); + msleep(1); + spin_lock_irq(&chip->reg_lock); + reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL); + if ((reg & (OXYGEN_SPDIF_SENSE_STATUS | + OXYGEN_SPDIF_LOCK_STATUS)) + == OXYGEN_SPDIF_SENSE_STATUS) { + /* nothing detected with either clock; give up */ + if ((reg & OXYGEN_SPDIF_IN_CLOCK_MASK) + == OXYGEN_SPDIF_IN_CLOCK_192) { + /* + * Reset clock to <= 96 kHz because this is + * more likely to be received next time. + */ + reg &= ~OXYGEN_SPDIF_IN_CLOCK_MASK; + reg |= OXYGEN_SPDIF_IN_CLOCK_96; + oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg); + } + } + } + spin_unlock_irq(&chip->reg_lock); + + if (chip->controls[CONTROL_SPDIF_INPUT_BITS]) { + spin_lock_irq(&chip->reg_lock); + chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT; + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, + chip->interrupt_mask); + spin_unlock_irq(&chip->reg_lock); + + /* + * We don't actually know that any channel status bits have + * changed, but let's send a notification just to be sure. + */ + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->controls[CONTROL_SPDIF_INPUT_BITS]->id); + } +} + +static void oxygen_gpio_changed(struct work_struct *work) +{ + struct oxygen *chip = container_of(work, struct oxygen, gpio_work); + + if (chip->model->gpio_changed) + chip->model->gpio_changed(chip); +} + +#ifdef CONFIG_PROC_FS +static void oxygen_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct oxygen *chip = entry->private_data; + int i, j; + + snd_iprintf(buffer, "CMI8788\n\n"); + for (i = 0; i < 0x100; i += 0x10) { + snd_iprintf(buffer, "%02x:", i); + for (j = 0; j < 0x10; ++j) + snd_iprintf(buffer, " %02x", oxygen_read8(chip, i + j)); + snd_iprintf(buffer, "\n"); + } + if (mutex_lock_interruptible(&chip->mutex) < 0) + return; + if (chip->has_ac97_0) { + snd_iprintf(buffer, "\nAC97\n"); + for (i = 0; i < 0x80; i += 0x10) { + snd_iprintf(buffer, "%02x:", i); + for (j = 0; j < 0x10; j += 2) + snd_iprintf(buffer, " %04x", + oxygen_read_ac97(chip, 0, i + j)); + snd_iprintf(buffer, "\n"); + } + } + if (chip->has_ac97_1) { + snd_iprintf(buffer, "\nAC97 2\n"); + for (i = 0; i < 0x80; i += 0x10) { + snd_iprintf(buffer, "%02x:", i); + for (j = 0; j < 0x10; j += 2) + snd_iprintf(buffer, " %04x", + oxygen_read_ac97(chip, 1, i + j)); + snd_iprintf(buffer, "\n"); + } + } + mutex_unlock(&chip->mutex); +} + +static void __devinit oxygen_proc_init(struct oxygen *chip) +{ + struct snd_info_entry *entry; + + if (!snd_card_proc_new(chip->card, "cmi8788", &entry)) + snd_info_set_text_ops(entry, chip, oxygen_proc_read); +} +#else +#define oxygen_proc_init(chip) +#endif + +static void __devinit oxygen_init(struct oxygen *chip) +{ + unsigned int i; + + chip->dac_routing = 1; + for (i = 0; i < 8; ++i) + chip->dac_volume[i] = 0xff; + chip->spdif_playback_enable = 1; + chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL | + (IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT); + chip->spdif_pcm_bits = chip->spdif_bits; + + if (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2) + chip->revision = 2; + else + chip->revision = 1; + + if (chip->revision == 1) + oxygen_set_bits8(chip, OXYGEN_MISC, + OXYGEN_MISC_PCI_MEM_W_1_CLOCK); + + i = oxygen_read16(chip, OXYGEN_AC97_CONTROL); + chip->has_ac97_0 = (i & OXYGEN_AC97_CODEC_0) != 0; + chip->has_ac97_1 = (i & OXYGEN_AC97_CODEC_1) != 0; + + oxygen_set_bits8(chip, OXYGEN_FUNCTION, + OXYGEN_FUNCTION_RESET_CODEC | + chip->model->function_flags); + oxygen_write8_masked(chip, OXYGEN_FUNCTION, + OXYGEN_FUNCTION_SPI, + OXYGEN_FUNCTION_2WIRE_SPI_MASK); + oxygen_write8(chip, OXYGEN_DMA_STATUS, 0); + oxygen_write8(chip, OXYGEN_DMA_PAUSE, 0); + oxygen_write8(chip, OXYGEN_PLAY_CHANNELS, + OXYGEN_PLAY_CHANNELS_2 | + OXYGEN_DMA_A_BURST_8 | + OXYGEN_DMA_MULTICH_BURST_8); + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0); + oxygen_write8_masked(chip, OXYGEN_MISC, 0, + OXYGEN_MISC_WRITE_PCI_SUBID | + OXYGEN_MISC_REC_C_FROM_SPDIF | + OXYGEN_MISC_REC_B_FROM_AC97 | + OXYGEN_MISC_REC_A_FROM_MULTICH); + oxygen_write8(chip, OXYGEN_REC_FORMAT, + (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_A_SHIFT) | + (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_B_SHIFT) | + (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_C_SHIFT)); + oxygen_write8(chip, OXYGEN_PLAY_FORMAT, + (OXYGEN_FORMAT_16 << OXYGEN_SPDIF_FORMAT_SHIFT) | + (OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT)); + oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2); + oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT, + OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | + OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, + OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | + OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + oxygen_write16(chip, OXYGEN_I2S_B_FORMAT, + OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | + OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + oxygen_write16(chip, OXYGEN_I2S_C_FORMAT, + OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | + OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL, + OXYGEN_SPDIF_SENSE_MASK | + OXYGEN_SPDIF_LOCK_MASK | + OXYGEN_SPDIF_RATE_MASK | + OXYGEN_SPDIF_LOCK_PAR | + OXYGEN_SPDIF_IN_CLOCK_96, + OXYGEN_SPDIF_OUT_ENABLE | + OXYGEN_SPDIF_LOOPBACK | + OXYGEN_SPDIF_SENSE_MASK | + OXYGEN_SPDIF_LOCK_MASK | + OXYGEN_SPDIF_RATE_MASK | + OXYGEN_SPDIF_SENSE_PAR | + OXYGEN_SPDIF_LOCK_PAR | + OXYGEN_SPDIF_IN_CLOCK_MASK); + oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits); + oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK); + oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0); + oxygen_write16(chip, OXYGEN_GPIO_INTERRUPT_MASK, 0); + oxygen_write16(chip, OXYGEN_PLAY_ROUTING, + OXYGEN_PLAY_MULTICH_I2S_DAC | + OXYGEN_PLAY_SPDIF_SPDIF | + (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT)); + oxygen_write8(chip, OXYGEN_REC_ROUTING, + OXYGEN_REC_A_ROUTE_I2S_ADC_1 | + OXYGEN_REC_B_ROUTE_I2S_ADC_2 | + OXYGEN_REC_C_ROUTE_SPDIF); + oxygen_write8(chip, OXYGEN_ADC_MONITOR, 0); + oxygen_write8(chip, OXYGEN_A_MONITOR_ROUTING, + (0 << OXYGEN_A_MONITOR_ROUTE_0_SHIFT) | + (1 << OXYGEN_A_MONITOR_ROUTE_1_SHIFT) | + (2 << OXYGEN_A_MONITOR_ROUTE_2_SHIFT) | + (3 << OXYGEN_A_MONITOR_ROUTE_3_SHIFT)); + + oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK, + OXYGEN_AC97_INT_READ_DONE | + OXYGEN_AC97_INT_WRITE_DONE); + oxygen_write32(chip, OXYGEN_AC97_OUT_CONFIG, 0); + oxygen_write32(chip, OXYGEN_AC97_IN_CONFIG, 0); + if (!(chip->has_ac97_0 | chip->has_ac97_1)) + oxygen_set_bits16(chip, OXYGEN_AC97_CONTROL, + OXYGEN_AC97_CLOCK_DISABLE); + if (!chip->has_ac97_0) { + oxygen_set_bits16(chip, OXYGEN_AC97_CONTROL, + OXYGEN_AC97_NO_CODEC_0); + } else { + oxygen_write_ac97(chip, 0, AC97_RESET, 0); + msleep(1); + oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_SETUP, + CM9780_GPIO0IO | CM9780_GPIO1IO); + oxygen_ac97_set_bits(chip, 0, CM9780_MIXER, + CM9780_BSTSEL | CM9780_STRO_MIC | + CM9780_MIX2FR | CM9780_PCBSW); + oxygen_ac97_set_bits(chip, 0, CM9780_JACK, + CM9780_RSOE | CM9780_CBOE | + CM9780_SSOE | CM9780_FROE | + CM9780_MIC2MIC | CM9780_LI2LI); + oxygen_write_ac97(chip, 0, AC97_MASTER, 0x0000); + oxygen_write_ac97(chip, 0, AC97_PC_BEEP, 0x8000); + oxygen_write_ac97(chip, 0, AC97_MIC, 0x8808); + oxygen_write_ac97(chip, 0, AC97_LINE, 0x0808); + oxygen_write_ac97(chip, 0, AC97_CD, 0x8808); + oxygen_write_ac97(chip, 0, AC97_VIDEO, 0x8808); + oxygen_write_ac97(chip, 0, AC97_AUX, 0x8808); + oxygen_write_ac97(chip, 0, AC97_REC_GAIN, 0x8000); + oxygen_write_ac97(chip, 0, AC97_CENTER_LFE_MASTER, 0x8080); + oxygen_write_ac97(chip, 0, AC97_SURROUND_MASTER, 0x8080); + /* power down unused ADCs and DACs */ + oxygen_ac97_set_bits(chip, 0, AC97_POWERDOWN, + AC97_PD_PR0 | AC97_PD_PR1); + oxygen_ac97_set_bits(chip, 0, AC97_EXTENDED_STATUS, + AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK); + } + if (chip->has_ac97_1) { + oxygen_set_bits32(chip, OXYGEN_AC97_OUT_CONFIG, + OXYGEN_AC97_CODEC1_SLOT3 | + OXYGEN_AC97_CODEC1_SLOT4); + oxygen_write_ac97(chip, 1, AC97_RESET, 0); + msleep(1); + oxygen_write_ac97(chip, 1, AC97_MASTER, 0x0000); + oxygen_write_ac97(chip, 1, AC97_HEADPHONE, 0x8000); + oxygen_write_ac97(chip, 1, AC97_PC_BEEP, 0x8000); + oxygen_write_ac97(chip, 1, AC97_MIC, 0x8808); + oxygen_write_ac97(chip, 1, AC97_LINE, 0x8808); + oxygen_write_ac97(chip, 1, AC97_CD, 0x8808); + oxygen_write_ac97(chip, 1, AC97_VIDEO, 0x8808); + oxygen_write_ac97(chip, 1, AC97_AUX, 0x8808); + oxygen_write_ac97(chip, 1, AC97_PCM, 0x0808); + oxygen_write_ac97(chip, 1, AC97_REC_SEL, 0x0000); + oxygen_write_ac97(chip, 1, AC97_REC_GAIN, 0x0000); + oxygen_ac97_set_bits(chip, 1, 0x6a, 0x0040); + } +} + +static void oxygen_card_free(struct snd_card *card) +{ + struct oxygen *chip = card->private_data; + + spin_lock_irq(&chip->reg_lock); + chip->interrupt_mask = 0; + chip->pcm_running = 0; + oxygen_write16(chip, OXYGEN_DMA_STATUS, 0); + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0); + spin_unlock_irq(&chip->reg_lock); + if (chip->irq >= 0) { + free_irq(chip->irq, chip); + synchronize_irq(chip->irq); + } + flush_scheduled_work(); + chip->model->cleanup(chip); + mutex_destroy(&chip->mutex); + pci_release_regions(chip->pci); + pci_disable_device(chip->pci); +} + +int __devinit oxygen_pci_probe(struct pci_dev *pci, int index, char *id, + int midi, const struct oxygen_model *model) +{ + struct snd_card *card; + struct oxygen *chip; + int err; + + card = snd_card_new(index, id, model->owner, + sizeof *chip + model->model_data_size); + if (!card) + return -ENOMEM; + + chip = card->private_data; + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->model = model; + chip->model_data = chip + 1; + spin_lock_init(&chip->reg_lock); + mutex_init(&chip->mutex); + INIT_WORK(&chip->spdif_input_bits_work, + oxygen_spdif_input_bits_changed); + INIT_WORK(&chip->gpio_work, oxygen_gpio_changed); + init_waitqueue_head(&chip->ac97_waitqueue); + + err = pci_enable_device(pci); + if (err < 0) + goto err_card; + + err = pci_request_regions(pci, model->chip); + if (err < 0) { + snd_printk(KERN_ERR "cannot reserve PCI resources\n"); + goto err_pci_enable; + } + + if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) || + pci_resource_len(pci, 0) < 0x100) { + snd_printk(KERN_ERR "invalid PCI I/O range\n"); + err = -ENXIO; + goto err_pci_regions; + } + chip->addr = pci_resource_start(pci, 0); + + pci_set_master(pci); + snd_card_set_dev(card, &pci->dev); + card->private_free = oxygen_card_free; + + oxygen_init(chip); + model->init(chip); + + err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED, + model->chip, chip); + if (err < 0) { + snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq); + goto err_card; + } + chip->irq = pci->irq; + + strcpy(card->driver, model->chip); + strcpy(card->shortname, model->shortname); + sprintf(card->longname, "%s (rev %u) at %#lx, irq %i", + model->longname, chip->revision, chip->addr, chip->irq); + strcpy(card->mixername, model->chip); + snd_component_add(card, model->chip); + + err = oxygen_pcm_init(chip); + if (err < 0) + goto err_card; + + err = oxygen_mixer_init(chip); + if (err < 0) + goto err_card; + + oxygen_write8_masked(chip, OXYGEN_MISC, + midi ? OXYGEN_MISC_MIDI : 0, OXYGEN_MISC_MIDI); + if (midi) { + err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, + chip->addr + OXYGEN_MPU401, + MPU401_INFO_INTEGRATED, 0, 0, + &chip->midi); + if (err < 0) + goto err_card; + } + + oxygen_proc_init(chip); + + spin_lock_irq(&chip->reg_lock); + chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT | OXYGEN_INT_AC97; + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask); + spin_unlock_irq(&chip->reg_lock); + + err = snd_card_register(card); + if (err < 0) + goto err_card; + + pci_set_drvdata(pci, card); + return 0; + +err_pci_regions: + pci_release_regions(pci); +err_pci_enable: + pci_disable_device(pci); +err_card: + snd_card_free(card); + return err; +} +EXPORT_SYMBOL(oxygen_pci_probe); + +void __devexit oxygen_pci_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} +EXPORT_SYMBOL(oxygen_pci_remove); diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c new file mode 100644 index 00000000000..a8e4623415d --- /dev/null +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -0,0 +1,794 @@ +/* + * C-Media CMI8788 driver - mixer code + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/mutex.h> +#include <sound/ac97_codec.h> +#include <sound/asoundef.h> +#include <sound/control.h> +#include <sound/tlv.h> +#include "oxygen.h" +#include "cm9780.h" + +static int dac_volume_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + struct oxygen *chip = ctl->private_data; + + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = chip->model->dac_channels; + info->value.integer.min = 0; + info->value.integer.max = 0xff; + return 0; +} + +static int dac_volume_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + unsigned int i; + + mutex_lock(&chip->mutex); + for (i = 0; i < chip->model->dac_channels; ++i) + value->value.integer.value[i] = chip->dac_volume[i]; + mutex_unlock(&chip->mutex); + return 0; +} + +static int dac_volume_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + unsigned int i; + int changed; + + changed = 0; + mutex_lock(&chip->mutex); + for (i = 0; i < chip->model->dac_channels; ++i) + if (value->value.integer.value[i] != chip->dac_volume[i]) { + chip->dac_volume[i] = value->value.integer.value[i]; + changed = 1; + } + if (changed) + chip->model->update_dac_volume(chip); + mutex_unlock(&chip->mutex); + return changed; +} + +static int dac_mute_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + mutex_lock(&chip->mutex); + value->value.integer.value[0] = !chip->dac_mute; + mutex_unlock(&chip->mutex); + return 0; +} + +static int dac_mute_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + int changed; + + mutex_lock(&chip->mutex); + changed = !value->value.integer.value[0] != chip->dac_mute; + if (changed) { + chip->dac_mute = !value->value.integer.value[0]; + chip->model->update_dac_mute(chip); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "Front", "Front+Surround", "Front+Surround+Back" + }; + struct oxygen *chip = ctl->private_data; + unsigned int count = 2 + (chip->model->dac_channels == 8); + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = count; + if (info->value.enumerated.item >= count) + info->value.enumerated.item = count - 1; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + mutex_lock(&chip->mutex); + value->value.enumerated.item[0] = chip->dac_routing; + mutex_unlock(&chip->mutex); + return 0; +} + +void oxygen_update_dac_routing(struct oxygen *chip) +{ + /* DAC 0: front, DAC 1: surround, DAC 2: center/LFE, DAC 3: back */ + static const unsigned int reg_values[3] = { + /* stereo -> front */ + (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT), + /* stereo -> front+surround */ + (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT), + /* stereo -> front+surround+back */ + (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT), + }; + u8 channels; + unsigned int reg_value; + + channels = oxygen_read8(chip, OXYGEN_PLAY_CHANNELS) & + OXYGEN_PLAY_CHANNELS_MASK; + if (channels == OXYGEN_PLAY_CHANNELS_2) + reg_value = reg_values[chip->dac_routing]; + else if (channels == OXYGEN_PLAY_CHANNELS_8) + /* in 7.1 mode, "rear" channels go to the "back" jack */ + reg_value = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (3 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (1 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT); + else + reg_value = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT); + oxygen_write16_masked(chip, OXYGEN_PLAY_ROUTING, reg_value, + OXYGEN_PLAY_DAC0_SOURCE_MASK | + OXYGEN_PLAY_DAC1_SOURCE_MASK | + OXYGEN_PLAY_DAC2_SOURCE_MASK | + OXYGEN_PLAY_DAC3_SOURCE_MASK); +} + +static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + unsigned int count = 2 + (chip->model->dac_channels == 8); + int changed; + + mutex_lock(&chip->mutex); + changed = value->value.enumerated.item[0] != chip->dac_routing; + if (changed) { + chip->dac_routing = min(value->value.enumerated.item[0], + count - 1); + spin_lock_irq(&chip->reg_lock); + oxygen_update_dac_routing(chip); + spin_unlock_irq(&chip->reg_lock); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int spdif_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + mutex_lock(&chip->mutex); + value->value.integer.value[0] = chip->spdif_playback_enable; + mutex_unlock(&chip->mutex); + return 0; +} + +static unsigned int oxygen_spdif_rate(unsigned int oxygen_rate) +{ + switch (oxygen_rate) { + case OXYGEN_RATE_32000: + return IEC958_AES3_CON_FS_32000 << OXYGEN_SPDIF_CS_RATE_SHIFT; + case OXYGEN_RATE_44100: + return IEC958_AES3_CON_FS_44100 << OXYGEN_SPDIF_CS_RATE_SHIFT; + default: /* OXYGEN_RATE_48000 */ + return IEC958_AES3_CON_FS_48000 << OXYGEN_SPDIF_CS_RATE_SHIFT; + case OXYGEN_RATE_64000: + return 0xb << OXYGEN_SPDIF_CS_RATE_SHIFT; + case OXYGEN_RATE_88200: + return 0x8 << OXYGEN_SPDIF_CS_RATE_SHIFT; + case OXYGEN_RATE_96000: + return 0xa << OXYGEN_SPDIF_CS_RATE_SHIFT; + case OXYGEN_RATE_176400: + return 0xc << OXYGEN_SPDIF_CS_RATE_SHIFT; + case OXYGEN_RATE_192000: + return 0xe << OXYGEN_SPDIF_CS_RATE_SHIFT; + } +} + +void oxygen_update_spdif_source(struct oxygen *chip) +{ + u32 old_control, new_control; + u16 old_routing, new_routing; + unsigned int oxygen_rate; + + old_control = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL); + old_routing = oxygen_read16(chip, OXYGEN_PLAY_ROUTING); + if (chip->pcm_active & (1 << PCM_SPDIF)) { + new_control = old_control | OXYGEN_SPDIF_OUT_ENABLE; + new_routing = (old_routing & ~OXYGEN_PLAY_SPDIF_MASK) + | OXYGEN_PLAY_SPDIF_SPDIF; + oxygen_rate = (old_control >> OXYGEN_SPDIF_OUT_RATE_SHIFT) + & OXYGEN_I2S_RATE_MASK; + /* S/PDIF rate was already set by the caller */ + } else if ((chip->pcm_active & (1 << PCM_MULTICH)) && + chip->spdif_playback_enable) { + new_routing = (old_routing & ~OXYGEN_PLAY_SPDIF_MASK) + | OXYGEN_PLAY_SPDIF_MULTICH_01; + oxygen_rate = oxygen_read16(chip, OXYGEN_I2S_MULTICH_FORMAT) + & OXYGEN_I2S_RATE_MASK; + new_control = (old_control & ~OXYGEN_SPDIF_OUT_RATE_MASK) | + (oxygen_rate << OXYGEN_SPDIF_OUT_RATE_SHIFT) | + OXYGEN_SPDIF_OUT_ENABLE; + } else { + new_control = old_control & ~OXYGEN_SPDIF_OUT_ENABLE; + new_routing = old_routing; + oxygen_rate = OXYGEN_RATE_44100; + } + if (old_routing != new_routing) { + oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, + new_control & ~OXYGEN_SPDIF_OUT_ENABLE); + oxygen_write16(chip, OXYGEN_PLAY_ROUTING, new_routing); + } + if (new_control & OXYGEN_SPDIF_OUT_ENABLE) + oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, + oxygen_spdif_rate(oxygen_rate) | + ((chip->pcm_active & (1 << PCM_SPDIF)) ? + chip->spdif_pcm_bits : chip->spdif_bits)); + oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, new_control); +} + +static int spdif_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + int changed; + + mutex_lock(&chip->mutex); + changed = value->value.integer.value[0] != chip->spdif_playback_enable; + if (changed) { + chip->spdif_playback_enable = !!value->value.integer.value[0]; + spin_lock_irq(&chip->reg_lock); + oxygen_update_spdif_source(chip); + spin_unlock_irq(&chip->reg_lock); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int spdif_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_IEC958; + info->count = 1; + return 0; +} + +static void oxygen_to_iec958(u32 bits, struct snd_ctl_elem_value *value) +{ + value->value.iec958.status[0] = + bits & (OXYGEN_SPDIF_NONAUDIO | OXYGEN_SPDIF_C | + OXYGEN_SPDIF_PREEMPHASIS); + value->value.iec958.status[1] = /* category and original */ + bits >> OXYGEN_SPDIF_CATEGORY_SHIFT; +} + +static u32 iec958_to_oxygen(struct snd_ctl_elem_value *value) +{ + u32 bits; + + bits = value->value.iec958.status[0] & + (OXYGEN_SPDIF_NONAUDIO | OXYGEN_SPDIF_C | + OXYGEN_SPDIF_PREEMPHASIS); + bits |= value->value.iec958.status[1] << OXYGEN_SPDIF_CATEGORY_SHIFT; + if (bits & OXYGEN_SPDIF_NONAUDIO) + bits |= OXYGEN_SPDIF_V; + return bits; +} + +static inline void write_spdif_bits(struct oxygen *chip, u32 bits) +{ + oxygen_write32_masked(chip, OXYGEN_SPDIF_OUTPUT_BITS, bits, + OXYGEN_SPDIF_NONAUDIO | + OXYGEN_SPDIF_C | + OXYGEN_SPDIF_PREEMPHASIS | + OXYGEN_SPDIF_CATEGORY_MASK | + OXYGEN_SPDIF_ORIGINAL | + OXYGEN_SPDIF_V); +} + +static int spdif_default_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + mutex_lock(&chip->mutex); + oxygen_to_iec958(chip->spdif_bits, value); + mutex_unlock(&chip->mutex); + return 0; +} + +static int spdif_default_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u32 new_bits; + int changed; + + new_bits = iec958_to_oxygen(value); + mutex_lock(&chip->mutex); + changed = new_bits != chip->spdif_bits; + if (changed) { + chip->spdif_bits = new_bits; + if (!(chip->pcm_active & (1 << PCM_SPDIF))) + write_spdif_bits(chip, new_bits); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int spdif_mask_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + value->value.iec958.status[0] = IEC958_AES0_NONAUDIO | + IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS; + value->value.iec958.status[1] = + IEC958_AES1_CON_CATEGORY | IEC958_AES1_CON_ORIGINAL; + return 0; +} + +static int spdif_pcm_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + mutex_lock(&chip->mutex); + oxygen_to_iec958(chip->spdif_pcm_bits, value); + mutex_unlock(&chip->mutex); + return 0; +} + +static int spdif_pcm_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u32 new_bits; + int changed; + + new_bits = iec958_to_oxygen(value); + mutex_lock(&chip->mutex); + changed = new_bits != chip->spdif_pcm_bits; + if (changed) { + chip->spdif_pcm_bits = new_bits; + if (chip->pcm_active & (1 << PCM_SPDIF)) + write_spdif_bits(chip, new_bits); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int spdif_input_mask_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + value->value.iec958.status[0] = 0xff; + value->value.iec958.status[1] = 0xff; + value->value.iec958.status[2] = 0xff; + value->value.iec958.status[3] = 0xff; + return 0; +} + +static int spdif_input_default_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u32 bits; + + bits = oxygen_read32(chip, OXYGEN_SPDIF_INPUT_BITS); + value->value.iec958.status[0] = bits; + value->value.iec958.status[1] = bits >> 8; + value->value.iec958.status[2] = bits >> 16; + value->value.iec958.status[3] = bits >> 24; + return 0; +} + +static int spdif_loopback_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + value->value.integer.value[0] = + !!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL) + & OXYGEN_SPDIF_LOOPBACK); + return 0; +} + +static int spdif_loopback_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u32 oldreg, newreg; + int changed; + + spin_lock_irq(&chip->reg_lock); + oldreg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL); + if (value->value.integer.value[0]) + newreg = oldreg | OXYGEN_SPDIF_LOOPBACK; + else + newreg = oldreg & ~OXYGEN_SPDIF_LOOPBACK; + changed = newreg != oldreg; + if (changed) + oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, newreg); + spin_unlock_irq(&chip->reg_lock); + return changed; +} + +static int ac97_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + unsigned int codec = (ctl->private_value >> 24) & 1; + unsigned int index = ctl->private_value & 0xff; + unsigned int bitnr = (ctl->private_value >> 8) & 0xff; + int invert = ctl->private_value & (1 << 16); + u16 reg; + + mutex_lock(&chip->mutex); + reg = oxygen_read_ac97(chip, codec, index); + mutex_unlock(&chip->mutex); + if (!(reg & (1 << bitnr)) ^ !invert) + value->value.integer.value[0] = 1; + else + value->value.integer.value[0] = 0; + return 0; +} + +static int ac97_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + unsigned int codec = (ctl->private_value >> 24) & 1; + unsigned int index = ctl->private_value & 0xff; + unsigned int bitnr = (ctl->private_value >> 8) & 0xff; + int invert = ctl->private_value & (1 << 16); + u16 oldreg, newreg; + int change; + + mutex_lock(&chip->mutex); + oldreg = oxygen_read_ac97(chip, codec, index); + newreg = oldreg; + if (!value->value.integer.value[0] ^ !invert) + newreg |= 1 << bitnr; + else + newreg &= ~(1 << bitnr); + change = newreg != oldreg; + if (change) { + oxygen_write_ac97(chip, codec, index, newreg); + if (bitnr == 15 && chip->model->ac97_switch_hook) + chip->model->ac97_switch_hook(chip, codec, index, + newreg & 0x8000); + } + mutex_unlock(&chip->mutex); + return change; +} + +static int ac97_volume_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 2; + info->value.integer.min = 0; + info->value.integer.max = 0x1f; + return 0; +} + +static int ac97_volume_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + unsigned int codec = (ctl->private_value >> 24) & 1; + unsigned int index = ctl->private_value & 0xff; + u16 reg; + + mutex_lock(&chip->mutex); + reg = oxygen_read_ac97(chip, codec, index); + mutex_unlock(&chip->mutex); + value->value.integer.value[0] = 31 - (reg & 0x1f); + value->value.integer.value[1] = 31 - ((reg >> 8) & 0x1f); + return 0; +} + +static int ac97_volume_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + unsigned int codec = (ctl->private_value >> 24) & 1; + unsigned int index = ctl->private_value & 0xff; + u16 oldreg, newreg; + int change; + + mutex_lock(&chip->mutex); + oldreg = oxygen_read_ac97(chip, codec, index); + newreg = oldreg; + newreg = (newreg & ~0x1f) | + (31 - (value->value.integer.value[0] & 0x1f)); + newreg = (newreg & ~0x1f00) | + ((31 - (value->value.integer.value[0] & 0x1f)) << 8); + change = newreg != oldreg; + if (change) + oxygen_write_ac97(chip, codec, index, newreg); + mutex_unlock(&chip->mutex); + return change; +} + +static int ac97_fp_rec_volume_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 2; + info->value.integer.min = 0; + info->value.integer.max = 7; + return 0; +} + +static int ac97_fp_rec_volume_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 reg; + + mutex_lock(&chip->mutex); + reg = oxygen_read_ac97(chip, 1, AC97_REC_GAIN); + mutex_unlock(&chip->mutex); + value->value.integer.value[0] = reg & 7; + value->value.integer.value[1] = (reg >> 8) & 7; + return 0; +} + +static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 oldreg, newreg; + int change; + + mutex_lock(&chip->mutex); + oldreg = oxygen_read_ac97(chip, 1, AC97_REC_GAIN); + newreg = oldreg & ~0x0707; + newreg = newreg | (value->value.integer.value[0] & 7); + newreg = newreg | ((value->value.integer.value[0] & 7) << 8); + change = newreg != oldreg; + if (change) + oxygen_write_ac97(chip, 1, AC97_REC_GAIN, newreg); + mutex_unlock(&chip->mutex); + return change; +} + +#define AC97_SWITCH(xname, codec, index, bitnr, invert) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = snd_ctl_boolean_mono_info, \ + .get = ac97_switch_get, \ + .put = ac97_switch_put, \ + .private_value = ((codec) << 24) | ((invert) << 16) | \ + ((bitnr) << 8) | (index), \ + } +#define AC97_VOLUME(xname, codec, index) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .info = ac97_volume_info, \ + .get = ac97_volume_get, \ + .put = ac97_volume_put, \ + .tlv = { .p = ac97_db_scale, }, \ + .private_value = ((codec) << 24) | (index), \ + } + +static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0); +static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0); + +static const struct snd_kcontrol_new controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = dac_volume_info, + .get = dac_volume_get, + .put = dac_volume_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = dac_mute_get, + .put = dac_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Stereo Upmixing", + .info = upmix_info, + .get = upmix_get, + .put = upmix_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), + .info = snd_ctl_boolean_mono_info, + .get = spdif_switch_get, + .put = spdif_switch_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .device = 1, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = spdif_info, + .get = spdif_default_get, + .put = spdif_default_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .device = 1, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = spdif_info, + .get = spdif_mask_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .device = 1, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .info = spdif_info, + .get = spdif_pcm_get, + .put = spdif_pcm_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .device = 1, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK), + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = spdif_info, + .get = spdif_input_mask_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .device = 1, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = spdif_info, + .get = spdif_input_default_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("Loopback ", NONE, SWITCH), + .info = snd_ctl_boolean_mono_info, + .get = spdif_loopback_get, + .put = spdif_loopback_put, + }, +}; + +static const struct snd_kcontrol_new ac97_controls[] = { + AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC), + AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1), + AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0), + AC97_VOLUME("Line Capture Volume", 0, AC97_LINE), + AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1), + AC97_VOLUME("CD Capture Volume", 0, AC97_CD), + AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1), + AC97_VOLUME("Aux Capture Volume", 0, AC97_AUX), + AC97_SWITCH("Aux Capture Switch", 0, AC97_AUX, 15, 1), +}; + +static const struct snd_kcontrol_new ac97_fp_controls[] = { + AC97_VOLUME("Front Panel Playback Volume", 1, AC97_HEADPHONE), + AC97_SWITCH("Front Panel Playback Switch", 1, AC97_HEADPHONE, 15, 1), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Front Panel Capture Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = ac97_fp_rec_volume_info, + .get = ac97_fp_rec_volume_get, + .put = ac97_fp_rec_volume_put, + .tlv = { .p = ac97_rec_db_scale, }, + }, + AC97_SWITCH("Front Panel Capture Switch", 1, AC97_REC_GAIN, 15, 1), +}; + +static void oxygen_any_ctl_free(struct snd_kcontrol *ctl) +{ + struct oxygen *chip = ctl->private_data; + unsigned int i; + + /* I'm too lazy to write a function for each control :-) */ + for (i = 0; i < ARRAY_SIZE(chip->controls); ++i) + chip->controls[i] = NULL; +} + +static int add_controls(struct oxygen *chip, + const struct snd_kcontrol_new controls[], + unsigned int count) +{ + static const char *const known_ctl_names[CONTROL_COUNT] = { + [CONTROL_SPDIF_PCM] = + SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), + [CONTROL_SPDIF_INPUT_BITS] = + SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), + [CONTROL_MIC_CAPTURE_SWITCH] = "Mic Capture Switch", + [CONTROL_LINE_CAPTURE_SWITCH] = "Line Capture Switch", + [CONTROL_CD_CAPTURE_SWITCH] = "CD Capture Switch", + [CONTROL_AUX_CAPTURE_SWITCH] = "Aux Capture Switch", + }; + unsigned int i, j; + struct snd_kcontrol_new template; + struct snd_kcontrol *ctl; + int err; + + for (i = 0; i < count; ++i) { + template = controls[i]; + err = chip->model->control_filter(&template); + if (err < 0) + return err; + if (err == 1) + continue; + ctl = snd_ctl_new1(&template, chip); + if (!ctl) + return -ENOMEM; + err = snd_ctl_add(chip->card, ctl); + if (err < 0) + return err; + for (j = 0; j < CONTROL_COUNT; ++j) + if (!strcmp(ctl->id.name, known_ctl_names[j])) { + chip->controls[j] = ctl; + ctl->private_free = oxygen_any_ctl_free; + } + } + return 0; +} + +int oxygen_mixer_init(struct oxygen *chip) +{ + int err; + + err = add_controls(chip, controls, ARRAY_SIZE(controls)); + if (err < 0) + return err; + if (chip->has_ac97_0) { + err = add_controls(chip, ac97_controls, + ARRAY_SIZE(ac97_controls)); + if (err < 0) + return err; + } + if (chip->has_ac97_1) { + err = add_controls(chip, ac97_fp_controls, + ARRAY_SIZE(ac97_fp_controls)); + if (err < 0) + return err; + } + return chip->model->mixer_init ? chip->model->mixer_init(chip) : 0; +} diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c new file mode 100644 index 00000000000..dfad3db35c8 --- /dev/null +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -0,0 +1,718 @@ +/* + * C-Media CMI8788 driver - PCM code + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/pci.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include "oxygen.h" + +static const struct snd_pcm_hardware oxygen_stereo_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .rate_min = 32000, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 256 * 1024, + .period_bytes_min = 128, + .period_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = 2048, +}; +static const struct snd_pcm_hardware oxygen_multichannel_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .rate_min = 32000, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 8, + .buffer_bytes_max = 2048 * 1024, + .period_bytes_min = 128, + .period_bytes_max = 256 * 1024, + .periods_min = 2, + .periods_max = 16384, +}; +static const struct snd_pcm_hardware oxygen_ac97_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 256 * 1024, + .period_bytes_min = 128, + .period_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = 2048, +}; + +static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = { + [PCM_A] = &oxygen_stereo_hardware, + [PCM_B] = &oxygen_stereo_hardware, + [PCM_C] = &oxygen_stereo_hardware, + [PCM_SPDIF] = &oxygen_stereo_hardware, + [PCM_MULTICH] = &oxygen_multichannel_hardware, + [PCM_AC97] = &oxygen_ac97_hardware, +}; + +static inline unsigned int +oxygen_substream_channel(struct snd_pcm_substream *substream) +{ + return (unsigned int)(uintptr_t)substream->runtime->private_data; +} + +static int oxygen_open(struct snd_pcm_substream *substream, + unsigned int channel) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + runtime->private_data = (void *)(uintptr_t)channel; + if (channel == PCM_B && chip->has_ac97_1 && + (chip->model->used_channels & OXYGEN_CHANNEL_AC97)) + runtime->hw = oxygen_ac97_hardware; + else + runtime->hw = *oxygen_hardware[channel]; + switch (channel) { + case PCM_C: + runtime->hw.rates &= ~(SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_64000); + runtime->hw.rate_min = 44100; + break; + case PCM_MULTICH: + runtime->hw.channels_max = chip->model->dac_channels; + break; + } + if (chip->model->pcm_hardware_filter) + chip->model->pcm_hardware_filter(channel, &runtime->hw); + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + if (err < 0) + return err; + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); + if (err < 0) + return err; + if (runtime->hw.formats & SNDRV_PCM_FMTBIT_S32_LE) { + err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + if (err < 0) + return err; + } + if (runtime->hw.channels_max > 2) { + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + 2); + if (err < 0) + return err; + } + snd_pcm_set_sync(substream); + chip->streams[channel] = substream; + + mutex_lock(&chip->mutex); + chip->pcm_active |= 1 << channel; + if (channel == PCM_SPDIF) { + chip->spdif_pcm_bits = chip->spdif_bits; + chip->controls[CONTROL_SPDIF_PCM]->vd[0].access &= + ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, + &chip->controls[CONTROL_SPDIF_PCM]->id); + } + mutex_unlock(&chip->mutex); + + return 0; +} + +static int oxygen_rec_a_open(struct snd_pcm_substream *substream) +{ + return oxygen_open(substream, PCM_A); +} + +static int oxygen_rec_b_open(struct snd_pcm_substream *substream) +{ + return oxygen_open(substream, PCM_B); +} + +static int oxygen_rec_c_open(struct snd_pcm_substream *substream) +{ + return oxygen_open(substream, PCM_C); +} + +static int oxygen_spdif_open(struct snd_pcm_substream *substream) +{ + return oxygen_open(substream, PCM_SPDIF); +} + +static int oxygen_multich_open(struct snd_pcm_substream *substream) +{ + return oxygen_open(substream, PCM_MULTICH); +} + +static int oxygen_ac97_open(struct snd_pcm_substream *substream) +{ + return oxygen_open(substream, PCM_AC97); +} + +static int oxygen_close(struct snd_pcm_substream *substream) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + unsigned int channel = oxygen_substream_channel(substream); + + mutex_lock(&chip->mutex); + chip->pcm_active &= ~(1 << channel); + if (channel == PCM_SPDIF) { + chip->controls[CONTROL_SPDIF_PCM]->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, + &chip->controls[CONTROL_SPDIF_PCM]->id); + } + if (channel == PCM_SPDIF || channel == PCM_MULTICH) + oxygen_update_spdif_source(chip); + mutex_unlock(&chip->mutex); + + chip->streams[channel] = NULL; + return 0; +} + +static unsigned int oxygen_format(struct snd_pcm_hw_params *hw_params) +{ + if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE) + return OXYGEN_FORMAT_24; + else + return OXYGEN_FORMAT_16; +} + +static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params) +{ + switch (params_rate(hw_params)) { + case 32000: + return OXYGEN_RATE_32000; + case 44100: + return OXYGEN_RATE_44100; + default: /* 48000 */ + return OXYGEN_RATE_48000; + case 64000: + return OXYGEN_RATE_64000; + case 88200: + return OXYGEN_RATE_88200; + case 96000: + return OXYGEN_RATE_96000; + case 176400: + return OXYGEN_RATE_176400; + case 192000: + return OXYGEN_RATE_192000; + } +} + +static unsigned int oxygen_i2s_mclk(struct snd_pcm_hw_params *hw_params) +{ + if (params_rate(hw_params) <= 96000) + return OXYGEN_I2S_MCLK_256; + else + return OXYGEN_I2S_MCLK_128; +} + +static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params) +{ + if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE) + return OXYGEN_I2S_BITS_24; + else + return OXYGEN_I2S_BITS_16; +} + +static unsigned int oxygen_play_channels(struct snd_pcm_hw_params *hw_params) +{ + switch (params_channels(hw_params)) { + default: /* 2 */ + return OXYGEN_PLAY_CHANNELS_2; + case 4: + return OXYGEN_PLAY_CHANNELS_4; + case 6: + return OXYGEN_PLAY_CHANNELS_6; + case 8: + return OXYGEN_PLAY_CHANNELS_8; + } +} + +static const unsigned int channel_base_registers[PCM_COUNT] = { + [PCM_A] = OXYGEN_DMA_A_ADDRESS, + [PCM_B] = OXYGEN_DMA_B_ADDRESS, + [PCM_C] = OXYGEN_DMA_C_ADDRESS, + [PCM_SPDIF] = OXYGEN_DMA_SPDIF_ADDRESS, + [PCM_MULTICH] = OXYGEN_DMA_MULTICH_ADDRESS, + [PCM_AC97] = OXYGEN_DMA_AC97_ADDRESS, +}; + +static int oxygen_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + unsigned int channel = oxygen_substream_channel(substream); + int err; + + err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; + + oxygen_write32(chip, channel_base_registers[channel], + (u32)substream->runtime->dma_addr); + if (channel == PCM_MULTICH) { + oxygen_write32(chip, OXYGEN_DMA_MULTICH_COUNT, + params_buffer_bytes(hw_params) / 4 - 1); + oxygen_write32(chip, OXYGEN_DMA_MULTICH_TCOUNT, + params_period_bytes(hw_params) / 4 - 1); + } else { + oxygen_write16(chip, channel_base_registers[channel] + 4, + params_buffer_bytes(hw_params) / 4 - 1); + oxygen_write16(chip, channel_base_registers[channel] + 6, + params_period_bytes(hw_params) / 4 - 1); + } + return 0; +} + +static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + int err; + + err = oxygen_hw_params(substream, hw_params); + if (err < 0) + return err; + + spin_lock_irq(&chip->reg_lock); + oxygen_write8_masked(chip, OXYGEN_REC_FORMAT, + oxygen_format(hw_params) << OXYGEN_REC_FORMAT_A_SHIFT, + OXYGEN_REC_FORMAT_A_MASK); + oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, + oxygen_rate(hw_params) | + oxygen_i2s_mclk(hw_params) | + chip->model->adc_i2s_format | + oxygen_i2s_bits(hw_params), + OXYGEN_I2S_RATE_MASK | + OXYGEN_I2S_FORMAT_MASK | + OXYGEN_I2S_MCLK_MASK | + OXYGEN_I2S_BITS_MASK); + spin_unlock_irq(&chip->reg_lock); + + mutex_lock(&chip->mutex); + chip->model->set_adc_params(chip, hw_params); + mutex_unlock(&chip->mutex); + return 0; +} + +static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + int is_ac97; + int err; + + err = oxygen_hw_params(substream, hw_params); + if (err < 0) + return err; + + is_ac97 = chip->has_ac97_1 && + (chip->model->used_channels & OXYGEN_CHANNEL_AC97); + + spin_lock_irq(&chip->reg_lock); + oxygen_write8_masked(chip, OXYGEN_REC_FORMAT, + oxygen_format(hw_params) << OXYGEN_REC_FORMAT_B_SHIFT, + OXYGEN_REC_FORMAT_B_MASK); + if (!is_ac97) + oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT, + oxygen_rate(hw_params) | + oxygen_i2s_mclk(hw_params) | + chip->model->adc_i2s_format | + oxygen_i2s_bits(hw_params), + OXYGEN_I2S_RATE_MASK | + OXYGEN_I2S_FORMAT_MASK | + OXYGEN_I2S_MCLK_MASK | + OXYGEN_I2S_BITS_MASK); + spin_unlock_irq(&chip->reg_lock); + + if (!is_ac97) { + mutex_lock(&chip->mutex); + chip->model->set_adc_params(chip, hw_params); + mutex_unlock(&chip->mutex); + } + return 0; +} + +static int oxygen_rec_c_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + int err; + + err = oxygen_hw_params(substream, hw_params); + if (err < 0) + return err; + + spin_lock_irq(&chip->reg_lock); + oxygen_write8_masked(chip, OXYGEN_REC_FORMAT, + oxygen_format(hw_params) << OXYGEN_REC_FORMAT_C_SHIFT, + OXYGEN_REC_FORMAT_C_MASK); + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + int err; + + err = oxygen_hw_params(substream, hw_params); + if (err < 0) + return err; + + spin_lock_irq(&chip->reg_lock); + oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL, + OXYGEN_SPDIF_OUT_ENABLE); + oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT, + oxygen_format(hw_params) << OXYGEN_SPDIF_FORMAT_SHIFT, + OXYGEN_SPDIF_FORMAT_MASK); + oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL, + oxygen_rate(hw_params) << OXYGEN_SPDIF_OUT_RATE_SHIFT, + OXYGEN_SPDIF_OUT_RATE_MASK); + oxygen_update_spdif_source(chip); + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static int oxygen_multich_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + int err; + + err = oxygen_hw_params(substream, hw_params); + if (err < 0) + return err; + + spin_lock_irq(&chip->reg_lock); + oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS, + oxygen_play_channels(hw_params), + OXYGEN_PLAY_CHANNELS_MASK); + oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT, + oxygen_format(hw_params) << OXYGEN_MULTICH_FORMAT_SHIFT, + OXYGEN_MULTICH_FORMAT_MASK); + oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT, + oxygen_rate(hw_params) | + chip->model->dac_i2s_format | + oxygen_i2s_bits(hw_params), + OXYGEN_I2S_RATE_MASK | + OXYGEN_I2S_FORMAT_MASK | + OXYGEN_I2S_BITS_MASK); + oxygen_update_dac_routing(chip); + oxygen_update_spdif_source(chip); + spin_unlock_irq(&chip->reg_lock); + + mutex_lock(&chip->mutex); + chip->model->set_dac_params(chip, hw_params); + mutex_unlock(&chip->mutex); + return 0; +} + +static int oxygen_hw_free(struct snd_pcm_substream *substream) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + unsigned int channel = oxygen_substream_channel(substream); + + spin_lock_irq(&chip->reg_lock); + chip->interrupt_mask &= ~(1 << channel); + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask); + spin_unlock_irq(&chip->reg_lock); + + return snd_pcm_lib_free_pages(substream); +} + +static int oxygen_spdif_hw_free(struct snd_pcm_substream *substream) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + + spin_lock_irq(&chip->reg_lock); + oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL, + OXYGEN_SPDIF_OUT_ENABLE); + spin_unlock_irq(&chip->reg_lock); + return oxygen_hw_free(substream); +} + +static int oxygen_prepare(struct snd_pcm_substream *substream) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + unsigned int channel = oxygen_substream_channel(substream); + unsigned int channel_mask = 1 << channel; + + spin_lock_irq(&chip->reg_lock); + oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask); + oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask); + + chip->interrupt_mask |= channel_mask; + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask); + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_substream *s; + unsigned int mask = 0; + int pausing; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_START: + pausing = 0; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pausing = 1; + break; + default: + return -EINVAL; + } + + snd_pcm_group_for_each_entry(s, substream) { + if (snd_pcm_substream_chip(s) == chip) { + mask |= 1 << oxygen_substream_channel(s); + snd_pcm_trigger_done(s, substream); + } + } + + spin_lock(&chip->reg_lock); + if (!pausing) { + if (cmd == SNDRV_PCM_TRIGGER_START) + chip->pcm_running |= mask; + else + chip->pcm_running &= ~mask; + oxygen_write8(chip, OXYGEN_DMA_STATUS, chip->pcm_running); + } else { + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) + oxygen_set_bits8(chip, OXYGEN_DMA_PAUSE, mask); + else + oxygen_clear_bits8(chip, OXYGEN_DMA_PAUSE, mask); + } + spin_unlock(&chip->reg_lock); + return 0; +} + +static snd_pcm_uframes_t oxygen_pointer(struct snd_pcm_substream *substream) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int channel = oxygen_substream_channel(substream); + u32 curr_addr; + + /* no spinlock, this read should be atomic */ + curr_addr = oxygen_read32(chip, channel_base_registers[channel]); + return bytes_to_frames(runtime, curr_addr - (u32)runtime->dma_addr); +} + +static struct snd_pcm_ops oxygen_rec_a_ops = { + .open = oxygen_rec_a_open, + .close = oxygen_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = oxygen_rec_a_hw_params, + .hw_free = oxygen_hw_free, + .prepare = oxygen_prepare, + .trigger = oxygen_trigger, + .pointer = oxygen_pointer, +}; + +static struct snd_pcm_ops oxygen_rec_b_ops = { + .open = oxygen_rec_b_open, + .close = oxygen_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = oxygen_rec_b_hw_params, + .hw_free = oxygen_hw_free, + .prepare = oxygen_prepare, + .trigger = oxygen_trigger, + .pointer = oxygen_pointer, +}; + +static struct snd_pcm_ops oxygen_rec_c_ops = { + .open = oxygen_rec_c_open, + .close = oxygen_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = oxygen_rec_c_hw_params, + .hw_free = oxygen_hw_free, + .prepare = oxygen_prepare, + .trigger = oxygen_trigger, + .pointer = oxygen_pointer, +}; + +static struct snd_pcm_ops oxygen_spdif_ops = { + .open = oxygen_spdif_open, + .close = oxygen_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = oxygen_spdif_hw_params, + .hw_free = oxygen_spdif_hw_free, + .prepare = oxygen_prepare, + .trigger = oxygen_trigger, + .pointer = oxygen_pointer, +}; + +static struct snd_pcm_ops oxygen_multich_ops = { + .open = oxygen_multich_open, + .close = oxygen_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = oxygen_multich_hw_params, + .hw_free = oxygen_hw_free, + .prepare = oxygen_prepare, + .trigger = oxygen_trigger, + .pointer = oxygen_pointer, +}; + +static struct snd_pcm_ops oxygen_ac97_ops = { + .open = oxygen_ac97_open, + .close = oxygen_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = oxygen_hw_params, + .hw_free = oxygen_hw_free, + .prepare = oxygen_prepare, + .trigger = oxygen_trigger, + .pointer = oxygen_pointer, +}; + +static void oxygen_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit oxygen_pcm_init(struct oxygen *chip) +{ + struct snd_pcm *pcm; + int outs, ins; + int err; + + outs = 1; /* OXYGEN_CHANNEL_MULTICH is always used */ + ins = !!(chip->model->used_channels & (OXYGEN_CHANNEL_A | + OXYGEN_CHANNEL_B)); + err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &oxygen_multich_ops); + if (chip->model->used_channels & OXYGEN_CHANNEL_A) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &oxygen_rec_a_ops); + else if (chip->model->used_channels & OXYGEN_CHANNEL_B) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &oxygen_rec_b_ops); + pcm->private_data = chip; + pcm->private_free = oxygen_pcm_free; + strcpy(pcm->name, "Analog"); + snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 512 * 1024, 2048 * 1024); + if (ins) + snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 128 * 1024, 256 * 1024); + + outs = !!(chip->model->used_channels & OXYGEN_CHANNEL_SPDIF); + ins = !!(chip->model->used_channels & OXYGEN_CHANNEL_C); + if (outs | ins) { + err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm); + if (err < 0) + return err; + if (outs) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &oxygen_spdif_ops); + if (ins) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &oxygen_rec_c_ops); + pcm->private_data = chip; + pcm->private_free = oxygen_pcm_free; + strcpy(pcm->name, "Digital"); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 128 * 1024, 256 * 1024); + } + + outs = chip->has_ac97_1 && + (chip->model->used_channels & OXYGEN_CHANNEL_AC97); + ins = outs || + (chip->model->used_channels & (OXYGEN_CHANNEL_A | + OXYGEN_CHANNEL_B)) + == (OXYGEN_CHANNEL_A | OXYGEN_CHANNEL_B); + if (outs | ins) { + err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2", + 2, outs, ins, &pcm); + if (err < 0) + return err; + if (outs) { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &oxygen_ac97_ops); + oxygen_write8_masked(chip, OXYGEN_REC_ROUTING, + OXYGEN_REC_B_ROUTE_AC97_1, + OXYGEN_REC_B_ROUTE_MASK); + } + if (ins) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &oxygen_rec_b_ops); + pcm->private_data = chip; + pcm->private_free = oxygen_pcm_free; + strcpy(pcm->name, outs ? "Front Panel" : "Analog 2"); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 128 * 1024, 256 * 1024); + } + return 0; +} diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h new file mode 100644 index 00000000000..72de159d456 --- /dev/null +++ b/sound/pci/oxygen/oxygen_regs.h @@ -0,0 +1,453 @@ +#ifndef OXYGEN_REGS_H_INCLUDED +#define OXYGEN_REGS_H_INCLUDED + +/* recording channel A */ +#define OXYGEN_DMA_A_ADDRESS 0x00 /* 32-bit base address */ +#define OXYGEN_DMA_A_COUNT 0x04 /* buffer counter (dwords) */ +#define OXYGEN_DMA_A_TCOUNT 0x06 /* interrupt counter (dwords) */ + +/* recording channel B */ +#define OXYGEN_DMA_B_ADDRESS 0x08 +#define OXYGEN_DMA_B_COUNT 0x0c +#define OXYGEN_DMA_B_TCOUNT 0x0e + +/* recording channel C */ +#define OXYGEN_DMA_C_ADDRESS 0x10 +#define OXYGEN_DMA_C_COUNT 0x14 +#define OXYGEN_DMA_C_TCOUNT 0x16 + +/* SPDIF playback channel */ +#define OXYGEN_DMA_SPDIF_ADDRESS 0x18 +#define OXYGEN_DMA_SPDIF_COUNT 0x1c +#define OXYGEN_DMA_SPDIF_TCOUNT 0x1e + +/* multichannel playback channel */ +#define OXYGEN_DMA_MULTICH_ADDRESS 0x20 +#define OXYGEN_DMA_MULTICH_COUNT 0x24 /* 24 bits */ +#define OXYGEN_DMA_MULTICH_TCOUNT 0x28 /* 24 bits */ + +/* AC'97 (front panel) playback channel */ +#define OXYGEN_DMA_AC97_ADDRESS 0x30 +#define OXYGEN_DMA_AC97_COUNT 0x34 +#define OXYGEN_DMA_AC97_TCOUNT 0x36 + +/* all registers 0x00..0x36 return current position on read */ + +#define OXYGEN_DMA_STATUS 0x40 /* 1 = running, 0 = stop */ +#define OXYGEN_CHANNEL_A 0x01 +#define OXYGEN_CHANNEL_B 0x02 +#define OXYGEN_CHANNEL_C 0x04 +#define OXYGEN_CHANNEL_SPDIF 0x08 +#define OXYGEN_CHANNEL_MULTICH 0x10 +#define OXYGEN_CHANNEL_AC97 0x20 + +#define OXYGEN_DMA_PAUSE 0x41 /* 1 = pause */ +/* OXYGEN_CHANNEL_* */ + +#define OXYGEN_DMA_RESET 0x42 +/* OXYGEN_CHANNEL_* */ + +#define OXYGEN_PLAY_CHANNELS 0x43 +#define OXYGEN_PLAY_CHANNELS_MASK 0x03 +#define OXYGEN_PLAY_CHANNELS_2 0x00 +#define OXYGEN_PLAY_CHANNELS_4 0x01 +#define OXYGEN_PLAY_CHANNELS_6 0x02 +#define OXYGEN_PLAY_CHANNELS_8 0x03 +#define OXYGEN_DMA_A_BURST_MASK 0x04 +#define OXYGEN_DMA_A_BURST_8 0x00 /* dwords */ +#define OXYGEN_DMA_A_BURST_16 0x04 +#define OXYGEN_DMA_MULTICH_BURST_MASK 0x08 +#define OXYGEN_DMA_MULTICH_BURST_8 0x00 +#define OXYGEN_DMA_MULTICH_BURST_16 0x08 + +#define OXYGEN_INTERRUPT_MASK 0x44 +/* OXYGEN_CHANNEL_* */ +#define OXYGEN_INT_SPDIF_IN_DETECT 0x0100 +#define OXYGEN_INT_MCU 0x0200 +#define OXYGEN_INT_2WIRE 0x0400 +#define OXYGEN_INT_GPIO 0x0800 +#define OXYGEN_INT_MCB 0x2000 +#define OXYGEN_INT_AC97 0x4000 + +#define OXYGEN_INTERRUPT_STATUS 0x46 +/* OXYGEN_CHANNEL_* amd OXYGEN_INT_* */ +#define OXYGEN_INT_MIDI 0x1000 + +#define OXYGEN_MISC 0x48 +#define OXYGEN_MISC_WRITE_PCI_SUBID 0x01 +#define OXYGEN_MISC_LATENCY_3F 0x02 +#define OXYGEN_MISC_REC_C_FROM_SPDIF 0x04 +#define OXYGEN_MISC_REC_B_FROM_AC97 0x08 +#define OXYGEN_MISC_REC_A_FROM_MULTICH 0x10 +#define OXYGEN_MISC_PCI_MEM_W_1_CLOCK 0x20 +#define OXYGEN_MISC_MIDI 0x40 +#define OXYGEN_MISC_CRYSTAL_MASK 0x80 +#define OXYGEN_MISC_CRYSTAL_24576 0x00 +#define OXYGEN_MISC_CRYSTAL_27 0x80 /* MHz */ + +#define OXYGEN_REC_FORMAT 0x4a +#define OXYGEN_REC_FORMAT_A_MASK 0x03 +#define OXYGEN_REC_FORMAT_A_SHIFT 0 +#define OXYGEN_REC_FORMAT_B_MASK 0x0c +#define OXYGEN_REC_FORMAT_B_SHIFT 2 +#define OXYGEN_REC_FORMAT_C_MASK 0x30 +#define OXYGEN_REC_FORMAT_C_SHIFT 4 +#define OXYGEN_FORMAT_16 0x00 +#define OXYGEN_FORMAT_24 0x01 +#define OXYGEN_FORMAT_32 0x02 + +#define OXYGEN_PLAY_FORMAT 0x4b +#define OXYGEN_SPDIF_FORMAT_MASK 0x03 +#define OXYGEN_SPDIF_FORMAT_SHIFT 0 +#define OXYGEN_MULTICH_FORMAT_MASK 0x0c +#define OXYGEN_MULTICH_FORMAT_SHIFT 2 +/* OXYGEN_FORMAT_* */ + +#define OXYGEN_REC_CHANNELS 0x4c +#define OXYGEN_REC_CHANNELS_MASK 0x07 +#define OXYGEN_REC_CHANNELS_2_2_2 0x00 /* DMA A, B, C */ +#define OXYGEN_REC_CHANNELS_4_2_2 0x01 +#define OXYGEN_REC_CHANNELS_6_0_2 0x02 +#define OXYGEN_REC_CHANNELS_6_2_0 0x03 +#define OXYGEN_REC_CHANNELS_8_0_0 0x04 + +#define OXYGEN_FUNCTION 0x50 +#define OXYGEN_FUNCTION_CLOCK_MASK 0x01 +#define OXYGEN_FUNCTION_CLOCK_PLL 0x00 +#define OXYGEN_FUNCTION_CLOCK_CRYSTAL 0x01 +#define OXYGEN_FUNCTION_RESET_CODEC 0x02 +#define OXYGEN_FUNCTION_RESET_POL 0x04 +#define OXYGEN_FUNCTION_PWDN 0x08 +#define OXYGEN_FUNCTION_PWDN_EN 0x10 +#define OXYGEN_FUNCTION_PWDN_POL 0x20 +#define OXYGEN_FUNCTION_2WIRE_SPI_MASK 0x40 +#define OXYGEN_FUNCTION_SPI 0x00 +#define OXYGEN_FUNCTION_2WIRE 0x40 +#define OXYGEN_FUNCTION_ENABLE_SPI_4_5 0x80 /* 0 = EEPROM */ + +#define OXYGEN_I2S_MULTICH_FORMAT 0x60 +#define OXYGEN_I2S_RATE_MASK 0x0007 /* LRCK */ +#define OXYGEN_RATE_32000 0x0000 +#define OXYGEN_RATE_44100 0x0001 +#define OXYGEN_RATE_48000 0x0002 +#define OXYGEN_RATE_64000 0x0003 +#define OXYGEN_RATE_88200 0x0004 +#define OXYGEN_RATE_96000 0x0005 +#define OXYGEN_RATE_176400 0x0006 +#define OXYGEN_RATE_192000 0x0007 +#define OXYGEN_I2S_FORMAT_MASK 0x0008 +#define OXYGEN_I2S_FORMAT_I2S 0x0000 +#define OXYGEN_I2S_FORMAT_LJUST 0x0008 +#define OXYGEN_I2S_MCLK_MASK 0x0030 /* MCLK/LRCK */ +#define OXYGEN_I2S_MCLK_128 0x0000 +#define OXYGEN_I2S_MCLK_256 0x0010 +#define OXYGEN_I2S_MCLK_512 0x0020 +#define OXYGEN_I2S_BITS_MASK 0x00c0 +#define OXYGEN_I2S_BITS_16 0x0000 +#define OXYGEN_I2S_BITS_20 0x0040 +#define OXYGEN_I2S_BITS_24 0x0080 +#define OXYGEN_I2S_BITS_32 0x00c0 +#define OXYGEN_I2S_MASTER 0x0100 +#define OXYGEN_I2S_BCLK_MASK 0x0600 /* BCLK/LRCK */ +#define OXYGEN_I2S_BCLK_64 0x0000 +#define OXYGEN_I2S_BCLK_128 0x0200 +#define OXYGEN_I2S_BCLK_256 0x0400 +#define OXYGEN_I2S_MUTE_MCLK 0x0800 + +#define OXYGEN_I2S_A_FORMAT 0x62 +#define OXYGEN_I2S_B_FORMAT 0x64 +#define OXYGEN_I2S_C_FORMAT 0x66 +/* like OXYGEN_I2S_MULTICH_FORMAT */ + +#define OXYGEN_SPDIF_CONTROL 0x70 +#define OXYGEN_SPDIF_OUT_ENABLE 0x00000002 +#define OXYGEN_SPDIF_LOOPBACK 0x00000004 /* in to out */ +#define OXYGEN_SPDIF_SENSE_MASK 0x00000008 +#define OXYGEN_SPDIF_LOCK_MASK 0x00000010 +#define OXYGEN_SPDIF_RATE_MASK 0x00000020 +#define OXYGEN_SPDIF_SPDVALID 0x00000040 +#define OXYGEN_SPDIF_SENSE_PAR 0x00000200 +#define OXYGEN_SPDIF_LOCK_PAR 0x00000400 +#define OXYGEN_SPDIF_SENSE_STATUS 0x00000800 +#define OXYGEN_SPDIF_LOCK_STATUS 0x00001000 +#define OXYGEN_SPDIF_SENSE_INT 0x00002000 /* r/wc */ +#define OXYGEN_SPDIF_LOCK_INT 0x00004000 /* r/wc */ +#define OXYGEN_SPDIF_RATE_INT 0x00008000 /* r/wc */ +#define OXYGEN_SPDIF_IN_CLOCK_MASK 0x00010000 +#define OXYGEN_SPDIF_IN_CLOCK_96 0x00000000 /* <= 96 kHz */ +#define OXYGEN_SPDIF_IN_CLOCK_192 0x00010000 /* > 96 kHz */ +#define OXYGEN_SPDIF_OUT_RATE_MASK 0x07000000 +#define OXYGEN_SPDIF_OUT_RATE_SHIFT 24 +/* OXYGEN_RATE_* << OXYGEN_SPDIF_OUT_RATE_SHIFT */ + +#define OXYGEN_SPDIF_OUTPUT_BITS 0x74 +#define OXYGEN_SPDIF_NONAUDIO 0x00000002 +#define OXYGEN_SPDIF_C 0x00000004 +#define OXYGEN_SPDIF_PREEMPHASIS 0x00000008 +#define OXYGEN_SPDIF_CATEGORY_MASK 0x000007f0 +#define OXYGEN_SPDIF_CATEGORY_SHIFT 4 +#define OXYGEN_SPDIF_ORIGINAL 0x00000800 +#define OXYGEN_SPDIF_CS_RATE_MASK 0x0000f000 +#define OXYGEN_SPDIF_CS_RATE_SHIFT 12 +#define OXYGEN_SPDIF_V 0x00010000 /* 0 = valid */ + +#define OXYGEN_SPDIF_INPUT_BITS 0x78 +/* 32 bits, IEC958_AES_* */ + +#define OXYGEN_EEPROM_CONTROL 0x80 +#define OXYGEN_EEPROM_ADDRESS_MASK 0x7f +#define OXYGEN_EEPROM_DIR_MASK 0x80 +#define OXYGEN_EEPROM_DIR_READ 0x00 +#define OXYGEN_EEPROM_DIR_WRITE 0x80 + +#define OXYGEN_EEPROM_STATUS 0x81 +#define OXYGEN_EEPROM_VALID 0x40 +#define OXYGEN_EEPROM_BUSY 0x80 + +#define OXYGEN_EEPROM_DATA 0x82 /* 16 bits */ + +#define OXYGEN_2WIRE_CONTROL 0x90 +#define OXYGEN_2WIRE_DIR_MASK 0x01 +#define OXYGEN_2WIRE_DIR_WRITE 0x00 +#define OXYGEN_2WIRE_DIR_READ 0x01 +#define OXYGEN_2WIRE_ADDRESS_MASK 0xfe /* slave device address */ +#define OXYGEN_2WIRE_ADDRESS_SHIFT 1 + +#define OXYGEN_2WIRE_MAP 0x91 /* address, 8 bits */ +#define OXYGEN_2WIRE_DATA 0x92 /* data, 16 bits */ + +#define OXYGEN_2WIRE_BUS_STATUS 0x94 +#define OXYGEN_2WIRE_BUSY 0x0001 +#define OXYGEN_2WIRE_LENGTH_MASK 0x0002 +#define OXYGEN_2WIRE_LENGTH_8 0x0000 +#define OXYGEN_2WIRE_LENGTH_16 0x0002 +#define OXYGEN_2WIRE_MANUAL_READ 0x0004 /* 0 = auto read */ +#define OXYGEN_2WIRE_WRITE_MAP_ONLY 0x0008 +#define OXYGEN_2WIRE_SLAVE_AD_MASK 0x0030 /* AD0, AD1 */ +#define OXYGEN_2WIRE_INTERRUPT_MASK 0x0040 /* 0 = int. if not responding */ +#define OXYGEN_2WIRE_SLAVE_NO_RESPONSE 0x0080 +#define OXYGEN_2WIRE_SPEED_MASK 0x0100 +#define OXYGEN_2WIRE_SPEED_STANDARD 0x0000 +#define OXYGEN_2WIRE_SPEED_FAST 0x0100 +#define OXYGEN_2WIRE_CLOCK_SYNC 0x0200 +#define OXYGEN_2WIRE_BUS_RESET 0x0400 + +#define OXYGEN_SPI_CONTROL 0x98 +#define OXYGEN_SPI_BUSY 0x01 /* read */ +#define OXYGEN_SPI_TRIGGER 0x01 /* write */ +#define OXYGEN_SPI_DATA_LENGTH_MASK 0x02 +#define OXYGEN_SPI_DATA_LENGTH_2 0x00 +#define OXYGEN_SPI_DATA_LENGTH_3 0x02 +#define OXYGEN_SPI_CLOCK_MASK 0xc0 +#define OXYGEN_SPI_CLOCK_160 0x00 /* ns */ +#define OXYGEN_SPI_CLOCK_320 0x40 +#define OXYGEN_SPI_CLOCK_640 0x80 +#define OXYGEN_SPI_CLOCK_1280 0xc0 +#define OXYGEN_SPI_CODEC_MASK 0x70 /* 0..5 */ +#define OXYGEN_SPI_CODEC_SHIFT 4 +#define OXYGEN_SPI_CEN_MASK 0x80 +#define OXYGEN_SPI_CEN_LATCH_CLOCK_LO 0x00 +#define OXYGEN_SPI_CEN_LATCH_CLOCK_HI 0x80 + +#define OXYGEN_SPI_DATA1 0x99 +#define OXYGEN_SPI_DATA2 0x9a +#define OXYGEN_SPI_DATA3 0x9b + +#define OXYGEN_MPU401 0xa0 + +#define OXYGEN_MPU401_CONTROL 0xa2 +#define OXYGEN_MPU401_LOOPBACK 0x01 /* TXD to RXD */ + +#define OXYGEN_GPI_DATA 0xa4 +/* bits 0..5 = pin XGPI0..XGPI5 */ + +#define OXYGEN_GPI_INTERRUPT_MASK 0xa5 +/* bits 0..5, 1 = enable */ + +#define OXYGEN_GPIO_DATA 0xa6 +/* bits 0..9 */ + +#define OXYGEN_GPIO_CONTROL 0xa8 +/* bits 0..9, 0 = input, 1 = output */ +#define OXYGEN_GPIO1_XSLAVE_RDY 0x8000 + +#define OXYGEN_GPIO_INTERRUPT_MASK 0xaa +/* bits 0..9, 1 = enable */ + +#define OXYGEN_DEVICE_SENSE 0xac +#define OXYGEN_HEAD_PHONE_DETECT 0x01 +#define OXYGEN_HEAD_PHONE_MASK 0x06 +#define OXYGEN_HEAD_PHONE_PASSIVE_SPK 0x00 +#define OXYGEN_HEAD_PHONE_HP 0x02 +#define OXYGEN_HEAD_PHONE_ACTIVE_SPK 0x04 + +#define OXYGEN_MCU_2WIRE_DATA 0xb0 + +#define OXYGEN_MCU_2WIRE_MAP 0xb2 + +#define OXYGEN_MCU_2WIRE_STATUS 0xb3 +#define OXYGEN_MCU_2WIRE_BUSY 0x01 +#define OXYGEN_MCU_2WIRE_LENGTH_MASK 0x06 +#define OXYGEN_MCU_2WIRE_LENGTH_1 0x00 +#define OXYGEN_MCU_2WIRE_LENGTH_2 0x02 +#define OXYGEN_MCU_2WIRE_LENGTH_3 0x04 +#define OXYGEN_MCU_2WIRE_WRITE 0x08 /* r/wc */ +#define OXYGEN_MCU_2WIRE_READ 0x10 /* r/wc */ +#define OXYGEN_MCU_2WIRE_DRV_XACT_FAIL 0x20 /* r/wc */ +#define OXYGEN_MCU_2WIRE_RESET 0x40 + +#define OXYGEN_MCU_2WIRE_CONTROL 0xb4 +#define OXYGEN_MCU_2WIRE_DRV_ACK 0x01 +#define OXYGEN_MCU_2WIRE_DRV_XACT 0x02 +#define OXYGEN_MCU_2WIRE_INT_MASK 0x04 +#define OXYGEN_MCU_2WIRE_SYNC_MASK 0x08 +#define OXYGEN_MCU_2WIRE_SYNC_RDY_PIN 0x00 +#define OXYGEN_MCU_2WIRE_SYNC_DATA 0x08 +#define OXYGEN_MCU_2WIRE_ADDRESS_MASK 0x30 +#define OXYGEN_MCU_2WIRE_ADDRESS_10 0x00 +#define OXYGEN_MCU_2WIRE_ADDRESS_12 0x10 +#define OXYGEN_MCU_2WIRE_ADDRESS_14 0x20 +#define OXYGEN_MCU_2WIRE_ADDRESS_16 0x30 +#define OXYGEN_MCU_2WIRE_INT_POL 0x40 +#define OXYGEN_MCU_2WIRE_SYNC_ENABLE 0x80 + +#define OXYGEN_PLAY_ROUTING 0xc0 +#define OXYGEN_PLAY_MUTE01 0x0001 +#define OXYGEN_PLAY_MUTE23 0x0002 +#define OXYGEN_PLAY_MUTE45 0x0004 +#define OXYGEN_PLAY_MUTE67 0x0008 +#define OXYGEN_PLAY_MULTICH_MASK 0x0010 +#define OXYGEN_PLAY_MULTICH_I2S_DAC 0x0000 +#define OXYGEN_PLAY_MULTICH_AC97 0x0010 +#define OXYGEN_PLAY_SPDIF_MASK 0x00e0 +#define OXYGEN_PLAY_SPDIF_SPDIF 0x0000 +#define OXYGEN_PLAY_SPDIF_MULTICH_01 0x0020 +#define OXYGEN_PLAY_SPDIF_MULTICH_23 0x0040 +#define OXYGEN_PLAY_SPDIF_MULTICH_45 0x0060 +#define OXYGEN_PLAY_SPDIF_MULTICH_67 0x0080 +#define OXYGEN_PLAY_SPDIF_REC_A 0x00a0 +#define OXYGEN_PLAY_SPDIF_REC_B 0x00c0 +#define OXYGEN_PLAY_SPDIF_I2S_ADC_3 0x00e0 +#define OXYGEN_PLAY_DAC0_SOURCE_MASK 0x0300 +#define OXYGEN_PLAY_DAC0_SOURCE_SHIFT 8 +#define OXYGEN_PLAY_DAC1_SOURCE_MASK 0x0c00 +#define OXYGEN_PLAY_DAC1_SOURCE_SHIFT 10 +#define OXYGEN_PLAY_DAC2_SOURCE_MASK 0x3000 +#define OXYGEN_PLAY_DAC2_SOURCE_SHIFT 12 +#define OXYGEN_PLAY_DAC3_SOURCE_MASK 0xc000 +#define OXYGEN_PLAY_DAC3_SOURCE_SHIFT 14 + +#define OXYGEN_REC_ROUTING 0xc2 +#define OXYGEN_MUTE_I2S_ADC_1 0x01 +#define OXYGEN_MUTE_I2S_ADC_2 0x02 +#define OXYGEN_MUTE_I2S_ADC_3 0x04 +#define OXYGEN_REC_A_ROUTE_MASK 0x08 +#define OXYGEN_REC_A_ROUTE_I2S_ADC_1 0x00 +#define OXYGEN_REC_A_ROUTE_AC97_0 0x08 +#define OXYGEN_REC_B_ROUTE_MASK 0x10 +#define OXYGEN_REC_B_ROUTE_I2S_ADC_2 0x00 +#define OXYGEN_REC_B_ROUTE_AC97_1 0x10 +#define OXYGEN_REC_C_ROUTE_MASK 0x20 +#define OXYGEN_REC_C_ROUTE_SPDIF 0x00 +#define OXYGEN_REC_C_ROUTE_I2S_ADC_3 0x20 + +#define OXYGEN_ADC_MONITOR 0xc3 +#define OXYGEN_ADC_MONITOR_A 0x01 +#define OXYGEN_ADC_MONITOR_A_HALF_VOL 0x02 +#define OXYGEN_ADC_MONITOR_B 0x04 +#define OXYGEN_ADC_MONITOR_B_HALF_VOL 0x08 +#define OXYGEN_ADC_MONITOR_C 0x10 +#define OXYGEN_ADC_MONITOR_C_HALF_VOL 0x20 + +#define OXYGEN_A_MONITOR_ROUTING 0xc4 +#define OXYGEN_A_MONITOR_ROUTE_0_MASK 0x03 +#define OXYGEN_A_MONITOR_ROUTE_0_SHIFT 0 +#define OXYGEN_A_MONITOR_ROUTE_1_MASK 0x0c +#define OXYGEN_A_MONITOR_ROUTE_1_SHIFT 2 +#define OXYGEN_A_MONITOR_ROUTE_2_MASK 0x30 +#define OXYGEN_A_MONITOR_ROUTE_2_SHIFT 4 +#define OXYGEN_A_MONITOR_ROUTE_3_MASK 0xc0 +#define OXYGEN_A_MONITOR_ROUTE_3_SHIFT 6 + +#define OXYGEN_AC97_CONTROL 0xd0 +#define OXYGEN_AC97_COLD_RESET 0x0001 +#define OXYGEN_AC97_SUSPENDED 0x0002 /* read */ +#define OXYGEN_AC97_RESUME 0x0002 /* write */ +#define OXYGEN_AC97_CLOCK_DISABLE 0x0004 +#define OXYGEN_AC97_NO_CODEC_0 0x0008 +#define OXYGEN_AC97_CODEC_0 0x0010 +#define OXYGEN_AC97_CODEC_1 0x0020 + +#define OXYGEN_AC97_INTERRUPT_MASK 0xd2 +#define OXYGEN_AC97_INT_READ_DONE 0x01 +#define OXYGEN_AC97_INT_WRITE_DONE 0x02 +#define OXYGEN_AC97_INT_CODEC_0 0x10 +#define OXYGEN_AC97_INT_CODEC_1 0x20 + +#define OXYGEN_AC97_INTERRUPT_STATUS 0xd3 +/* OXYGEN_AC97_INT_* */ + +#define OXYGEN_AC97_OUT_CONFIG 0xd4 +#define OXYGEN_AC97_CODEC1_SLOT3 0x00000001 +#define OXYGEN_AC97_CODEC1_SLOT3_VSR 0x00000002 +#define OXYGEN_AC97_CODEC1_SLOT4 0x00000010 +#define OXYGEN_AC97_CODEC1_SLOT4_VSR 0x00000020 +#define OXYGEN_AC97_CODEC0_FRONTL 0x00000100 +#define OXYGEN_AC97_CODEC0_FRONTR 0x00000200 +#define OXYGEN_AC97_CODEC0_SIDEL 0x00000400 +#define OXYGEN_AC97_CODEC0_SIDER 0x00000800 +#define OXYGEN_AC97_CODEC0_CENTER 0x00001000 +#define OXYGEN_AC97_CODEC0_BASE 0x00002000 +#define OXYGEN_AC97_CODEC0_REARL 0x00004000 +#define OXYGEN_AC97_CODEC0_REARR 0x00008000 + +#define OXYGEN_AC97_IN_CONFIG 0xd8 +#define OXYGEN_AC97_CODEC1_LINEL 0x00000001 +#define OXYGEN_AC97_CODEC1_LINEL_VSR 0x00000002 +#define OXYGEN_AC97_CODEC1_LINEL_16 0x00000000 +#define OXYGEN_AC97_CODEC1_LINEL_18 0x00000004 +#define OXYGEN_AC97_CODEC1_LINEL_20 0x00000008 +#define OXYGEN_AC97_CODEC1_LINER 0x00000010 +#define OXYGEN_AC97_CODEC1_LINER_VSR 0x00000020 +#define OXYGEN_AC97_CODEC1_LINER_16 0x00000000 +#define OXYGEN_AC97_CODEC1_LINER_18 0x00000040 +#define OXYGEN_AC97_CODEC1_LINER_20 0x00000080 +#define OXYGEN_AC97_CODEC0_LINEL 0x00000100 +#define OXYGEN_AC97_CODEC0_LINER 0x00000200 + +#define OXYGEN_AC97_REGS 0xdc +#define OXYGEN_AC97_REG_DATA_MASK 0x0000ffff +#define OXYGEN_AC97_REG_ADDR_MASK 0x007f0000 +#define OXYGEN_AC97_REG_ADDR_SHIFT 16 +#define OXYGEN_AC97_REG_DIR_MASK 0x00800000 +#define OXYGEN_AC97_REG_DIR_WRITE 0x00000000 +#define OXYGEN_AC97_REG_DIR_READ 0x00800000 +#define OXYGEN_AC97_REG_CODEC_MASK 0x01000000 +#define OXYGEN_AC97_REG_CODEC_SHIFT 24 + +#define OXYGEN_TEST 0xe0 +#define OXYGEN_TEST_RAM_SUCCEEDED 0x01 +#define OXYGEN_TEST_PLAYBACK_RAM 0x02 +#define OXYGEN_TEST_RECORD_RAM 0x04 +#define OXYGEN_TEST_PLL 0x08 +#define OXYGEN_TEST_2WIRE_LOOPBACK 0x10 + +#define OXYGEN_DMA_FLUSH 0xe1 +/* OXYGEN_CHANNEL_* */ + +#define OXYGEN_CODEC_VERSION 0xe4 +#define OXYGEN_XCID_MASK 0x07 + +#define OXYGEN_REVISION 0xe6 +#define OXYGEN_REVISION_XPKGID_MASK 0x0007 +#define OXYGEN_REVISION_MASK 0xfff8 +#define OXYGEN_REVISION_2 0x0008 /* bit flag */ +#define OXYGEN_REVISION_8787 0x0014 /* 8 bits */ + +#define OXYGEN_OFFSIN_48K 0xe8 +#define OXYGEN_OFFSBASE_48K 0xe9 +#define OXYGEN_OFFSBASE_MASK 0x0fff +#define OXYGEN_OFFSIN_44K 0xec +#define OXYGEN_OFFSBASE_44K 0xed + +#endif diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c new file mode 100644 index 00000000000..40e92f5cd69 --- /dev/null +++ b/sound/pci/oxygen/virtuoso.c @@ -0,0 +1,449 @@ +/* + * C-Media CMI8788 driver for Asus Xonar cards + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * CMI8788: + * + * SPI 0 -> 1st PCM1796 (front) + * SPI 1 -> 2nd PCM1796 (surround) + * SPI 2 -> 3rd PCM1796 (center/LFE) + * SPI 4 -> 4th PCM1796 (back) + * + * GPIO 2 -> M0 of CS5381 + * GPIO 3 -> M1 of CS5381 + * GPIO 5 <- external power present (D2X only) + * GPIO 7 -> ALT + * GPIO 8 -> enable output to speakers + * + * CM9780: + * + * GPIO 0 -> enable AC'97 bypass (line in -> ADC) + */ + +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <sound/ac97_codec.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/tlv.h> +#include "oxygen.h" +#include "cm9780.h" + +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); +MODULE_DESCRIPTION("Asus AV200 driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Asus,AV200}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "card index"); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string"); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "enable card"); + +static struct pci_device_id xonar_ids[] __devinitdata = { + { OXYGEN_PCI_SUBID(0x1043, 0x8269) }, /* Asus Xonar D2 */ + { OXYGEN_PCI_SUBID(0x1043, 0x82b7) }, /* Asus Xonar D2X */ + { } +}; +MODULE_DEVICE_TABLE(pci, xonar_ids); + + +#define GPIO_CS5381_M_MASK 0x000c +#define GPIO_CS5381_M_SINGLE 0x0000 +#define GPIO_CS5381_M_DOUBLE 0x0004 +#define GPIO_CS5381_M_QUAD 0x0008 +#define GPIO_EXT_POWER 0x0020 +#define GPIO_ALT 0x0080 +#define GPIO_OUTPUT_ENABLE 0x0100 + +#define GPIO_LINE_MUTE CM9780_GPO0 + +/* register 16 */ +#define PCM1796_ATL_MASK 0xff +/* register 17 */ +#define PCM1796_ATR_MASK 0xff +/* register 18 */ +#define PCM1796_MUTE 0x01 +#define PCM1796_DME 0x02 +#define PCM1796_DMF_MASK 0x0c +#define PCM1796_DMF_DISABLED 0x00 +#define PCM1796_DMF_48 0x04 +#define PCM1796_DMF_441 0x08 +#define PCM1796_DMF_32 0x0c +#define PCM1796_FMT_MASK 0x70 +#define PCM1796_FMT_16_RJUST 0x00 +#define PCM1796_FMT_20_RJUST 0x10 +#define PCM1796_FMT_24_RJUST 0x20 +#define PCM1796_FMT_24_LJUST 0x30 +#define PCM1796_FMT_16_I2S 0x40 +#define PCM1796_FMT_24_I2S 0x50 +#define PCM1796_ATLD 0x80 +/* register 19 */ +#define PCM1796_INZD 0x01 +#define PCM1796_FLT_MASK 0x02 +#define PCM1796_FLT_SHARP 0x00 +#define PCM1796_FLT_SLOW 0x02 +#define PCM1796_DFMS 0x04 +#define PCM1796_OPE 0x10 +#define PCM1796_ATS_MASK 0x60 +#define PCM1796_ATS_1 0x00 +#define PCM1796_ATS_2 0x20 +#define PCM1796_ATS_4 0x40 +#define PCM1796_ATS_8 0x60 +#define PCM1796_REV 0x80 +/* register 20 */ +#define PCM1796_OS_MASK 0x03 +#define PCM1796_OS_64 0x00 +#define PCM1796_OS_32 0x01 +#define PCM1796_OS_128 0x02 +#define PCM1796_CHSL_MASK 0x04 +#define PCM1796_CHSL_LEFT 0x00 +#define PCM1796_CHSL_RIGHT 0x04 +#define PCM1796_MONO 0x08 +#define PCM1796_DFTH 0x10 +#define PCM1796_DSD 0x20 +#define PCM1796_SRST 0x40 +/* register 21 */ +#define PCM1796_PCMZ 0x01 +#define PCM1796_DZ_MASK 0x06 +/* register 22 */ +#define PCM1796_ZFGL 0x01 +#define PCM1796_ZFGR 0x02 +/* register 23 */ +#define PCM1796_ID_MASK 0x1f + +struct xonar_data { + u8 is_d2x; + u8 has_power; +}; + +static void pcm1796_write(struct oxygen *chip, unsigned int codec, + u8 reg, u8 value) +{ + /* maps ALSA channel pair number to SPI output */ + static const u8 codec_map[4] = { + 0, 1, 2, 4 + }; + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CLOCK_160 | + (codec_map[codec] << OXYGEN_SPI_CODEC_SHIFT) | + OXYGEN_SPI_CEN_LATCH_CLOCK_HI, + (reg << 8) | value); +} + +static void xonar_init(struct oxygen *chip) +{ + struct xonar_data *data = chip->model_data; + unsigned int i; + + data->is_d2x = chip->pci->subsystem_device == 0x82b7; + + for (i = 0; i < 4; ++i) { + pcm1796_write(chip, i, 18, PCM1796_FMT_24_LJUST | PCM1796_ATLD); + pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); + pcm1796_write(chip, i, 20, PCM1796_OS_64); + pcm1796_write(chip, i, 21, 0); + pcm1796_write(chip, i, 16, 0xff); /* set ATL/ATR after ATLD */ + pcm1796_write(chip, i, 17, 0xff); + } + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, + GPIO_CS5381_M_MASK | GPIO_ALT); + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + GPIO_CS5381_M_SINGLE, + GPIO_CS5381_M_MASK | GPIO_ALT); + if (data->is_d2x) { + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, + GPIO_EXT_POWER); + oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, + GPIO_EXT_POWER); + chip->interrupt_mask |= OXYGEN_INT_GPIO; + data->has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) + & GPIO_EXT_POWER); + } + oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC); + oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE); + msleep(300); + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_OUTPUT_ENABLE); + oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); + + snd_component_add(chip->card, "PCM1796"); + snd_component_add(chip->card, "CS5381"); +} + +static void xonar_cleanup(struct oxygen *chip) +{ + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); +} + +static void set_pcm1796_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ +#if 0 + unsigned int i; + u8 value; + + value = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; + for (i = 0; i < 4; ++i) + pcm1796_write(chip, i, 20, value); +#endif +} + +static void update_pcm1796_volume(struct oxygen *chip) +{ + unsigned int i; + + for (i = 0; i < 4; ++i) { + pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]); + pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]); + } +} + +static void update_pcm1796_mute(struct oxygen *chip) +{ + unsigned int i; + u8 value; + + value = PCM1796_FMT_24_LJUST | PCM1796_ATLD; + if (chip->dac_mute) + value |= PCM1796_MUTE; + for (i = 0; i < 4; ++i) + pcm1796_write(chip, i, 18, value); +} + +static void set_cs5381_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + unsigned int value; + + if (params_rate(params) <= 54000) + value = GPIO_CS5381_M_SINGLE; + else if (params_rate(params) <= 108000) + value = GPIO_CS5381_M_DOUBLE; + else + value = GPIO_CS5381_M_QUAD; + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + value, GPIO_CS5381_M_MASK); +} + +static void xonar_gpio_changed(struct oxygen *chip) +{ + struct xonar_data *data = chip->model_data; + u8 has_power; + + if (!data->is_d2x) + return; + has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) + & GPIO_EXT_POWER); + if (has_power != data->has_power) { + data->has_power = has_power; + if (has_power) { + snd_printk(KERN_NOTICE "power restored\n"); + } else { + snd_printk(KERN_CRIT + "Hey! Don't unplug the power cable!\n"); + /* TODO: stop PCMs */ + } + } +} + +static void mute_ac97_ctl(struct oxygen *chip, unsigned int control) +{ + unsigned int index = chip->controls[control]->private_value & 0xff; + u16 value; + + value = oxygen_read_ac97(chip, 0, index); + if (!(value & 0x8000)) { + oxygen_write_ac97(chip, 0, index, value | 0x8000); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->controls[control]->id); + } +} + +static void xonar_ac97_switch_hook(struct oxygen *chip, unsigned int codec, + unsigned int reg, int mute) +{ + if (codec != 0) + return; + /* line-in is exclusive */ + switch (reg) { + case AC97_LINE: + oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS, + mute ? GPIO_LINE_MUTE : 0, + GPIO_LINE_MUTE); + if (!mute) { + mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH); + mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH); + mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH); + } + break; + case AC97_MIC: + case AC97_CD: + case AC97_VIDEO: + case AC97_AUX: + if (!mute) { + oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS, + GPIO_LINE_MUTE); + mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH); + } + break; + } +} + +static int pcm1796_volume_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 8; + info->value.integer.min = 0x0f; + info->value.integer.max = 0xff; + return 0; +} + +static int alt_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + value->value.integer.value[0] = + !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_ALT); + return 0; +} + +static int alt_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 old_bits, new_bits; + int changed; + + spin_lock_irq(&chip->reg_lock); + old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); + if (value->value.integer.value[0]) + new_bits = old_bits | GPIO_ALT; + else + new_bits = old_bits & ~GPIO_ALT; + changed = new_bits != old_bits; + if (changed) + oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits); + spin_unlock_irq(&chip->reg_lock); + return changed; +} + +static const struct snd_kcontrol_new alt_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Loopback Switch", + .info = snd_ctl_boolean_mono_info, + .get = alt_switch_get, + .put = alt_switch_put, +}; + +static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -12000, 50, 0); + +static int xonar_control_filter(struct snd_kcontrol_new *template) +{ + if (!strcmp(template->name, "Master Playback Volume")) { + template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + template->info = pcm1796_volume_info, + template->tlv.p = pcm1796_db_scale; + } else if (!strncmp(template->name, "CD Capture ", 11)) { + /* CD in is actually connected to the video in pin */ + template->private_value ^= AC97_CD ^ AC97_VIDEO; + } else if (!strcmp(template->name, "Line Capture Volume")) { + return 1; /* line-in bypasses the AC'97 mixer */ + } + return 0; +} + +static int xonar_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); +} + +static const struct oxygen_model model_xonar = { + .shortname = "Asus AV200", + .longname = "Asus Virtuoso 200", + .chip = "AV200", + .init = xonar_init, + .control_filter = xonar_control_filter, + .mixer_init = xonar_mixer_init, + .cleanup = xonar_cleanup, + .set_dac_params = set_pcm1796_params, + .set_adc_params = set_cs5381_params, + .update_dac_volume = update_pcm1796_volume, + .update_dac_mute = update_pcm1796_mute, + .ac97_switch_hook = xonar_ac97_switch_hook, + .gpio_changed = xonar_gpio_changed, + .model_data_size = sizeof(struct xonar_data), + .dac_channels = 8, + .used_channels = OXYGEN_CHANNEL_B | + OXYGEN_CHANNEL_C | + OXYGEN_CHANNEL_SPDIF | + OXYGEN_CHANNEL_MULTICH, + .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +static int __devinit xonar_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + ++dev; + return -ENOENT; + } + err = oxygen_pci_probe(pci, index[dev], id[dev], 1, &model_xonar); + if (err >= 0) + ++dev; + return err; +} + +static struct pci_driver xonar_driver = { + .name = "AV200", + .id_table = xonar_ids, + .probe = xonar_probe, + .remove = __devexit_p(oxygen_pci_remove), +}; + +static int __init alsa_card_xonar_init(void) +{ + return pci_register_driver(&xonar_driver); +} + +static void __exit alsa_card_xonar_exit(void) +{ + pci_unregister_driver(&xonar_driver); +} + +module_init(alsa_card_xonar_init) +module_exit(alsa_card_xonar_exit) diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 2d618bd7e62..9d5bb76229a 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -21,7 +21,6 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/slab.h> diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c index 0ff8dc36fde..c4e415d0738 100644 --- a/sound/pci/pcxhr/pcxhr_core.c +++ b/sound/pci/pcxhr/pcxhr_core.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/firmware.h> #include <linux/interrupt.h> diff --git a/sound/pci/pcxhr/pcxhr_hwdep.c b/sound/pci/pcxhr/pcxhr_hwdep.c index d55d8bc90ee..e6a4bfbb91b 100644 --- a/sound/pci/pcxhr/pcxhr_hwdep.c +++ b/sound/pci/pcxhr/pcxhr_hwdep.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/interrupt.h> #include <linux/vmalloc.h> #include <linux/firmware.h> diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c index 5f8d42633b0..aabc7bc5321 100644 --- a/sound/pci/pcxhr/pcxhr_mixer.c +++ b/sound/pci/pcxhr/pcxhr_mixer.c @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/interrupt.h> #include <linux/init.h> @@ -120,8 +119,18 @@ static int pcxhr_analog_vol_put(struct snd_kcontrol *kcontrol, is_capture = (kcontrol->private_value != 0); for (i = 0; i < 2; i++) { int new_volume = ucontrol->value.integer.value[i]; - int* stored_volume = is_capture ? &chip->analog_capture_volume[i] : + int *stored_volume = is_capture ? + &chip->analog_capture_volume[i] : &chip->analog_playback_volume[i]; + if (is_capture) { + if (new_volume < PCXHR_ANALOG_CAPTURE_LEVEL_MIN || + new_volume > PCXHR_ANALOG_CAPTURE_LEVEL_MAX) + continue; + } else { + if (new_volume < PCXHR_ANALOG_PLAYBACK_LEVEL_MIN || + new_volume > PCXHR_ANALOG_PLAYBACK_LEVEL_MAX) + continue; + } if (*stored_volume != new_volume) { *stored_volume = new_volume; changed = 1; @@ -165,10 +174,13 @@ static int pcxhr_audio_sw_put(struct snd_kcontrol *kcontrol, int i, changed = 0; mutex_lock(&chip->mgr->mixer_mutex); for(i = 0; i < 2; i++) { - if (chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) { - chip->analog_playback_active[i] = ucontrol->value.integer.value[i]; + if (chip->analog_playback_active[i] != + ucontrol->value.integer.value[i]) { + chip->analog_playback_active[i] = + !!ucontrol->value.integer.value[i]; changed = 1; - pcxhr_update_analog_audio_level(chip, 0, i); /* update playback levels */ + /* update playback levels */ + pcxhr_update_analog_audio_level(chip, 0, i); } } mutex_unlock(&chip->mgr->mixer_mutex); @@ -323,20 +335,24 @@ static int pcxhr_pcm_vol_put(struct snd_kcontrol *kcontrol, int i; mutex_lock(&chip->mgr->mixer_mutex); - if (is_capture) - stored_volume = chip->digital_capture_volume; /* digital capture */ - else - stored_volume = chip->digital_playback_volume[idx]; /* digital playback */ + if (is_capture) /* digital capture */ + stored_volume = chip->digital_capture_volume; + else /* digital playback */ + stored_volume = chip->digital_playback_volume[idx]; for (i = 0; i < 2; i++) { - if (stored_volume[i] != ucontrol->value.integer.value[i]) { - stored_volume[i] = ucontrol->value.integer.value[i]; + int vol = ucontrol->value.integer.value[i]; + if (vol < PCXHR_DIGITAL_LEVEL_MIN || + vol > PCXHR_DIGITAL_LEVEL_MAX) + continue; + if (stored_volume[i] != vol) { + stored_volume[i] = vol; changed = 1; if (is_capture) /* update capture volume */ pcxhr_update_audio_pipe_level(chip, 1, i); } } - if (! is_capture && changed) - pcxhr_update_playback_stream_level(chip, idx); /* update playback volume */ + if (!is_capture && changed) /* update playback volume */ + pcxhr_update_playback_stream_level(chip, idx); mutex_unlock(&chip->mgr->mixer_mutex); return changed; } @@ -378,8 +394,10 @@ static int pcxhr_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v mutex_lock(&chip->mgr->mixer_mutex); j = idx; for (i = 0; i < 2; i++) { - if (chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) { - chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i]; + if (chip->digital_playback_active[j][i] != + ucontrol->value.integer.value[i]) { + chip->digital_playback_active[j][i] = + !!ucontrol->value.integer.value[i]; changed = 1; } } @@ -423,10 +441,13 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol, mutex_lock(&chip->mgr->mixer_mutex); for (i = 0; i < 2; i++) { - if (chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) { - chip->monitoring_volume[i] = ucontrol->value.integer.value[i]; - if(chip->monitoring_active[i]) /* do only when monitoring is unmuted */ + if (chip->monitoring_volume[i] != + ucontrol->value.integer.value[i]) { + chip->monitoring_volume[i] = + !!ucontrol->value.integer.value[i]; + if(chip->monitoring_active[i]) /* update monitoring volume and mute */ + /* do only when monitoring is unmuted */ pcxhr_update_audio_pipe_level(chip, 0, i); changed = 1; } @@ -470,15 +491,17 @@ static int pcxhr_monitor_sw_put(struct snd_kcontrol *kcontrol, mutex_lock(&chip->mgr->mixer_mutex); for (i = 0; i < 2; i++) { - if (chip->monitoring_active[i] != ucontrol->value.integer.value[i]) { - chip->monitoring_active[i] = ucontrol->value.integer.value[i]; + if (chip->monitoring_active[i] != + ucontrol->value.integer.value[i]) { + chip->monitoring_active[i] = + !!ucontrol->value.integer.value[i]; changed |= (1<<i); /* mask 0x01 and 0x02 */ } } - if(changed & 0x01) + if (changed & 0x01) /* update left monitoring volume and mute */ pcxhr_update_audio_pipe_level(chip, 0, 0); - if(changed & 0x02) + if (changed & 0x02) /* update right monitoring volume and mute */ pcxhr_update_audio_pipe_level(chip, 0, 1); @@ -579,6 +602,8 @@ static int pcxhr_audio_src_put(struct snd_kcontrol *kcontrol, struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol); int ret = 0; + if (ucontrol->value.enumerated.item[0] >= 3) + return -EINVAL; mutex_lock(&chip->mgr->mixer_mutex); if (chip->audio_capture_source != ucontrol->value.enumerated.item[0]) { chip->audio_capture_source = ucontrol->value.enumerated.item[0]; @@ -642,8 +667,11 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct pcxhr_mgr *mgr = snd_kcontrol_chip(kcontrol); + unsigned int clock_items = 3 + mgr->capture_chips; int rate, ret = 0; + if (ucontrol->value.enumerated.item[0] >= clock_items) + return -EINVAL; mutex_lock(&mgr->mixer_mutex); if (mgr->use_clock_type != ucontrol->value.enumerated.item[0]) { mutex_lock(&mgr->setup_mutex); diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 8e5410483e6..9408b1eeec4 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -88,7 +88,6 @@ Adopted for Windows NT driver 01/20/98 CNL */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 1475912588e..df184aabce8 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -69,7 +69,6 @@ */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 0b3c532c401..fb0a4ee8bc0 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -23,7 +23,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -2195,22 +2194,25 @@ snd_rme96_dac_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu { struct rme96 *rme96 = snd_kcontrol_chip(kcontrol); int change = 0; + unsigned int vol, maxvol; - if (!RME96_HAS_ANALOG_OUT(rme96)) { + + if (!RME96_HAS_ANALOG_OUT(rme96)) return -EINVAL; - } + maxvol = RME96_185X_MAX_OUT(rme96); spin_lock_irq(&rme96->lock); - if (u->value.integer.value[0] != rme96->vol[0]) { - rme96->vol[0] = u->value.integer.value[0]; - change = 1; - } - if (u->value.integer.value[1] != rme96->vol[1]) { - rme96->vol[1] = u->value.integer.value[1]; - change = 1; - } - if (change) { - snd_rme96_apply_dac_volume(rme96); + vol = u->value.integer.value[0]; + if (vol != rme96->vol[0] && vol <= maxvol) { + rme96->vol[0] = vol; + change = 1; + } + vol = u->value.integer.value[1]; + if (vol != rme96->vol[1] && vol <= maxvol) { + rme96->vol[1] = vol; + change = 1; } + if (change) + snd_rme96_apply_dac_volume(rme96); spin_unlock_irq(&rme96->lock); return change; diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index ff26a3672d4..c2bd4384316 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -104,8 +103,6 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin"); #define HDSP_statusRegister 0 #define HDSP_timecode 128 #define HDSP_status2Register 192 -#define HDSP_midiDataOut0 352 -#define HDSP_midiDataOut1 356 #define HDSP_midiDataIn0 360 #define HDSP_midiDataIn1 364 #define HDSP_midiStatusOut0 384 @@ -610,7 +607,10 @@ static int hdsp_playback_to_output_key (struct hdsp *hdsp, int in, int out) case Multiface: case Digiface: default: - return (64 * out) + (32 + (in)); + if (hdsp->firmware_rev == 0xa) + return (64 * out) + (32 + (in)); + else + return (52 * out) + (26 + (in)); case H9632: return (32 * out) + (16 + (in)); case H9652: @@ -624,7 +624,10 @@ static int hdsp_input_to_output_key (struct hdsp *hdsp, int in, int out) case Multiface: case Digiface: default: - return (64 * out) + in; + if (hdsp->firmware_rev == 0xa) + return (64 * out) + in; + else + return (52 * out) + in; case H9632: return (32 * out) + in; case H9652: @@ -2121,7 +2124,7 @@ static int snd_hdsp_put_clock_source_lock(struct snd_kcontrol *kcontrol, struct change = (int)ucontrol->value.integer.value[0] != hdsp->clock_source_locked; if (change) - hdsp->clock_source_locked = ucontrol->value.integer.value[0]; + hdsp->clock_source_locked = !!ucontrol->value.integer.value[0]; return change; } @@ -3558,7 +3561,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) } -static void __devinit snd_hdsp_proc_init(struct hdsp *hdsp) +static void snd_hdsp_proc_init(struct hdsp *hdsp) { struct snd_info_entry *entry; @@ -3606,7 +3609,7 @@ static int snd_hdsp_set_defaults(struct hdsp *hdsp) /* ASSUMPTION: hdsp->lock is either held, or there is no need to hold it (e.g. during module - initalization). + initialization). */ /* set defaults: diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index f1bdda6cbcf..9a19ae6a64d 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -23,7 +23,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -3348,7 +3347,7 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm) unsigned int i; /* ASSUMPTION: hdspm->lock is either held, or there is no need to - hold it (e.g. during module initalization). + hold it (e.g. during module initialization). */ /* set defaults: */ @@ -3416,7 +3415,7 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm) /*------------------------------------------------------------ - interupt + interrupt ------------------------------------------------------------*/ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id) diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 34f96f12e5b..a123f0e6ba2 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -148,7 +147,7 @@ MODULE_SUPPORTED_DEVICE("{{RME,Hammerfall}," #define RME9652_start_bit (1<<0) /* start record/play */ /* bits 1-3 encode buffersize/latency */ #define RME9652_Master (1<<4) /* Clock Mode Master=1,Slave/Auto=0 */ -#define RME9652_IE (1<<5) /* Interupt Enable */ +#define RME9652_IE (1<<5) /* Interrupt Enable */ #define RME9652_freq (1<<6) /* samplerate 0=44.1/88.2, 1=48/96 kHz */ #define RME9652_freq1 (1<<7) /* if 0, 32kHz, else always 1 */ #define RME9652_DS (1<<8) /* Doule Speed 0=44.1/48, 1=88.2/96 Khz */ @@ -1826,7 +1825,7 @@ static void snd_rme9652_set_defaults(struct snd_rme9652 *rme9652) /* ASSUMPTION: rme9652->lock is either held, or there is no need to hold it (e.g. during module - initalization). + initialization). */ /* set defaults: diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c new file mode 100644 index 00000000000..dcd7cd01046 --- /dev/null +++ b/sound/pci/sis7019.c @@ -0,0 +1,1460 @@ +/* + * Driver for SiS7019 Audio Accelerator + * + * Copyright (C) 2004-2007, David Dillow + * Written by David Dillow <dave@thedillows.org> + * Inspired by the Trident 4D-WaveDX/NX driver. + * + * All rights reserved. + * + * 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, version 2. + * + * 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 <linux/init.h> +#include <linux/pci.h> +#include <linux/time.h> +#include <linux/moduleparam.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <sound/core.h> +#include <sound/ac97_codec.h> +#include <sound/initval.h> +#include "sis7019.h" + +MODULE_AUTHOR("David Dillow <dave@thedillows.org>"); +MODULE_DESCRIPTION("SiS7019"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{SiS,SiS7019 Audio Accelerator}}"); + +static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ +static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ +static int enable = 1; + +module_param(index, int, 0444); +MODULE_PARM_DESC(index, "Index value for SiS7019 Audio Accelerator."); +module_param(id, charp, 0444); +MODULE_PARM_DESC(id, "ID string for SiS7019 Audio Accelerator."); +module_param(enable, bool, 0444); +MODULE_PARM_DESC(enable, "Enable SiS7019 Audio Accelerator."); + +static struct pci_device_id snd_sis7019_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x7019) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_sis7019_ids); + +/* There are three timing modes for the voices. + * + * For both playback and capture, when the buffer is one or two periods long, + * we use the hardware's built-in Mid-Loop Interrupt and End-Loop Interrupt + * to let us know when the periods have ended. + * + * When performing playback with more than two periods per buffer, we set + * the "Stop Sample Offset" and tell the hardware to interrupt us when we + * reach it. We then update the offset and continue on until we are + * interrupted for the next period. + * + * Capture channels do not have a SSO, so we allocate a playback channel to + * use as a timer for the capture periods. We use the SSO on the playback + * channel to clock out virtual periods, and adjust the virtual period length + * to maintain synchronization. This algorithm came from the Trident driver. + * + * FIXME: It'd be nice to make use of some of the synth features in the + * hardware, but a woeful lack of documentation is a significant roadblock. + */ +struct voice { + u16 flags; +#define VOICE_IN_USE 1 +#define VOICE_CAPTURE 2 +#define VOICE_SSO_TIMING 4 +#define VOICE_SYNC_TIMING 8 + u16 sync_cso; + u16 period_size; + u16 buffer_size; + u16 sync_period_size; + u16 sync_buffer_size; + u32 sso; + u32 vperiod; + struct snd_pcm_substream *substream; + struct voice *timing; + void __iomem *ctrl_base; + void __iomem *wave_base; + void __iomem *sync_base; + int num; +}; + +/* We need four pages to store our wave parameters during a suspend. If + * we're not doing power management, we still need to allocate a page + * for the silence buffer. + */ +#ifdef CONFIG_PM +#define SIS_SUSPEND_PAGES 4 +#else +#define SIS_SUSPEND_PAGES 1 +#endif + +struct sis7019 { + unsigned long ioport; + void __iomem *ioaddr; + int irq; + int codecs_present; + + struct pci_dev *pci; + struct snd_pcm *pcm; + struct snd_card *card; + struct snd_ac97 *ac97[3]; + + /* Protect against more than one thread hitting the AC97 + * registers (in a more polite manner than pounding the hardware + * semaphore) + */ + struct mutex ac97_mutex; + + /* voice_lock protects allocation/freeing of the voice descriptions + */ + spinlock_t voice_lock; + + struct voice voices[64]; + struct voice capture_voice; + + /* Allocate pages to store the internal wave state during + * suspends. When we're operating, this can be used as a silence + * buffer for a timing channel. + */ + void *suspend_state[SIS_SUSPEND_PAGES]; + + int silence_users; + dma_addr_t silence_dma_addr; +}; + +#define SIS_PRIMARY_CODEC_PRESENT 0x0001 +#define SIS_SECONDARY_CODEC_PRESENT 0x0002 +#define SIS_TERTIARY_CODEC_PRESENT 0x0004 + +/* The HW offset parameters (Loop End, Stop Sample, End Sample) have a + * documented range of 8-0xfff8 samples. Given that they are 0-based, + * that places our period/buffer range at 9-0xfff9 samples. That makes the + * max buffer size 0xfff9 samples * 2 channels * 2 bytes per sample, and + * max samples / min samples gives us the max periods in a buffer. + * + * We'll add a constraint upon open that limits the period and buffer sample + * size to values that are legal for the hardware. + */ +static struct snd_pcm_hardware sis_playback_hw_info = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (0xfff9 * 4), + .period_bytes_min = 9, + .period_bytes_max = (0xfff9 * 4), + .periods_min = 1, + .periods_max = (0xfff9 / 9), +}; + +static struct snd_pcm_hardware sis_capture_hw_info = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (0xfff9 * 4), + .period_bytes_min = 9, + .period_bytes_max = (0xfff9 * 4), + .periods_min = 1, + .periods_max = (0xfff9 / 9), +}; + +static void sis_update_sso(struct voice *voice, u16 period) +{ + void __iomem *base = voice->ctrl_base; + + voice->sso += period; + if (voice->sso >= voice->buffer_size) + voice->sso -= voice->buffer_size; + + /* Enforce the documented hardware minimum offset */ + if (voice->sso < 8) + voice->sso = 8; + + /* The SSO is in the upper 16 bits of the register. */ + writew(voice->sso & 0xffff, base + SIS_PLAY_DMA_SSO_ESO + 2); +} + +static void sis_update_voice(struct voice *voice) +{ + if (voice->flags & VOICE_SSO_TIMING) { + sis_update_sso(voice, voice->period_size); + } else if (voice->flags & VOICE_SYNC_TIMING) { + int sync; + + /* If we've not hit the end of the virtual period, update + * our records and keep going. + */ + if (voice->vperiod > voice->period_size) { + voice->vperiod -= voice->period_size; + if (voice->vperiod < voice->period_size) + sis_update_sso(voice, voice->vperiod); + else + sis_update_sso(voice, voice->period_size); + return; + } + + /* Calculate our relative offset between the target and + * the actual CSO value. Since we're operating in a loop, + * if the value is more than half way around, we can + * consider ourselves wrapped. + */ + sync = voice->sync_cso; + sync -= readw(voice->sync_base + SIS_CAPTURE_DMA_FORMAT_CSO); + if (sync > (voice->sync_buffer_size / 2)) + sync -= voice->sync_buffer_size; + + /* If sync is positive, then we interrupted too early, and + * we'll need to come back in a few samples and try again. + * There's a minimum wait, as it takes some time for the DMA + * engine to startup, etc... + */ + if (sync > 0) { + if (sync < 16) + sync = 16; + sis_update_sso(voice, sync); + return; + } + + /* Ok, we interrupted right on time, or (hopefully) just + * a bit late. We'll adjst our next waiting period based + * on how close we got. + * + * We need to stay just behind the actual channel to ensure + * it really is past a period when we get our interrupt -- + * otherwise we'll fall into the early code above and have + * a minimum wait time, which makes us quite late here, + * eating into the user's time to refresh the buffer, esp. + * if using small periods. + * + * If we're less than 9 samples behind, we're on target. + */ + if (sync > -9) + voice->vperiod = voice->sync_period_size + 1; + else + voice->vperiod = voice->sync_period_size - 4; + + if (voice->vperiod < voice->buffer_size) { + sis_update_sso(voice, voice->vperiod); + voice->vperiod = 0; + } else + sis_update_sso(voice, voice->period_size); + + sync = voice->sync_cso + voice->sync_period_size; + if (sync >= voice->sync_buffer_size) + sync -= voice->sync_buffer_size; + voice->sync_cso = sync; + } + + snd_pcm_period_elapsed(voice->substream); +} + +static void sis_voice_irq(u32 status, struct voice *voice) +{ + int bit; + + while (status) { + bit = __ffs(status); + status >>= bit + 1; + voice += bit; + sis_update_voice(voice); + voice++; + } +} + +static irqreturn_t sis_interrupt(int irq, void *dev) +{ + struct sis7019 *sis = dev; + unsigned long io = sis->ioport; + struct voice *voice; + u32 intr, status; + + /* We only use the DMA interrupts, and we don't enable any other + * source of interrupts. But, it is possible to see an interupt + * status that didn't actually interrupt us, so eliminate anything + * we're not expecting to avoid falsely claiming an IRQ, and an + * ensuing endless loop. + */ + intr = inl(io + SIS_GISR); + intr &= SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS | + SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS; + if (!intr) + return IRQ_NONE; + + do { + status = inl(io + SIS_PISR_A); + if (status) { + sis_voice_irq(status, sis->voices); + outl(status, io + SIS_PISR_A); + } + + status = inl(io + SIS_PISR_B); + if (status) { + sis_voice_irq(status, &sis->voices[32]); + outl(status, io + SIS_PISR_B); + } + + status = inl(io + SIS_RISR); + if (status) { + voice = &sis->capture_voice; + if (!voice->timing) + snd_pcm_period_elapsed(voice->substream); + + outl(status, io + SIS_RISR); + } + + outl(intr, io + SIS_GISR); + intr = inl(io + SIS_GISR); + intr &= SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS | + SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS; + } while (intr); + + return IRQ_HANDLED; +} + +static u32 sis_rate_to_delta(unsigned int rate) +{ + u32 delta; + + /* This was copied from the trident driver, but it seems its gotten + * around a bit... nevertheless, it works well. + * + * We special case 44100 and 8000 since rounding with the equation + * does not give us an accurate enough value. For 11025 and 22050 + * the equation gives us the best answer. All other frequencies will + * also use the equation. JDW + */ + if (rate == 44100) + delta = 0xeb3; + else if (rate == 8000) + delta = 0x2ab; + else if (rate == 48000) + delta = 0x1000; + else + delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff; + return delta; +} + +static void __sis_map_silence(struct sis7019 *sis) +{ + /* Helper function: must hold sis->voice_lock on entry */ + if (!sis->silence_users) + sis->silence_dma_addr = pci_map_single(sis->pci, + sis->suspend_state[0], + 4096, PCI_DMA_TODEVICE); + sis->silence_users++; +} + +static void __sis_unmap_silence(struct sis7019 *sis) +{ + /* Helper function: must hold sis->voice_lock on entry */ + sis->silence_users--; + if (!sis->silence_users) + pci_unmap_single(sis->pci, sis->silence_dma_addr, 4096, + PCI_DMA_TODEVICE); +} + +static void sis_free_voice(struct sis7019 *sis, struct voice *voice) +{ + unsigned long flags; + + spin_lock_irqsave(&sis->voice_lock, flags); + if (voice->timing) { + __sis_unmap_silence(sis); + voice->timing->flags &= ~(VOICE_IN_USE | VOICE_SSO_TIMING | + VOICE_SYNC_TIMING); + voice->timing = NULL; + } + voice->flags &= ~(VOICE_IN_USE | VOICE_SSO_TIMING | VOICE_SYNC_TIMING); + spin_unlock_irqrestore(&sis->voice_lock, flags); +} + +static struct voice *__sis_alloc_playback_voice(struct sis7019 *sis) +{ + /* Must hold the voice_lock on entry */ + struct voice *voice; + int i; + + for (i = 0; i < 64; i++) { + voice = &sis->voices[i]; + if (voice->flags & VOICE_IN_USE) + continue; + voice->flags |= VOICE_IN_USE; + goto found_one; + } + voice = NULL; + +found_one: + return voice; +} + +static struct voice *sis_alloc_playback_voice(struct sis7019 *sis) +{ + struct voice *voice; + unsigned long flags; + + spin_lock_irqsave(&sis->voice_lock, flags); + voice = __sis_alloc_playback_voice(sis); + spin_unlock_irqrestore(&sis->voice_lock, flags); + + return voice; +} + +static int sis_alloc_timing_voice(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct sis7019 *sis = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *voice = runtime->private_data; + unsigned int period_size, buffer_size; + unsigned long flags; + int needed; + + /* If there are one or two periods per buffer, we don't need a + * timing voice, as we can use the capture channel's interrupts + * to clock out the periods. + */ + period_size = params_period_size(hw_params); + buffer_size = params_buffer_size(hw_params); + needed = (period_size != buffer_size && + period_size != (buffer_size / 2)); + + if (needed && !voice->timing) { + spin_lock_irqsave(&sis->voice_lock, flags); + voice->timing = __sis_alloc_playback_voice(sis); + if (voice->timing) + __sis_map_silence(sis); + spin_unlock_irqrestore(&sis->voice_lock, flags); + if (!voice->timing) + return -ENOMEM; + voice->timing->substream = substream; + } else if (!needed && voice->timing) { + sis_free_voice(sis, voice); + voice->timing = NULL; + } + + return 0; +} + +static int sis_playback_open(struct snd_pcm_substream *substream) +{ + struct sis7019 *sis = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *voice; + + voice = sis_alloc_playback_voice(sis); + if (!voice) + return -EAGAIN; + + voice->substream = substream; + runtime->private_data = voice; + runtime->hw = sis_playback_hw_info; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + 9, 0xfff9); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + 9, 0xfff9); + snd_pcm_set_sync(substream); + return 0; +} + +static int sis_substream_close(struct snd_pcm_substream *substream) +{ + struct sis7019 *sis = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *voice = runtime->private_data; + + sis_free_voice(sis, voice); + return 0; +} + +static int sis_playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static int sis_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int sis_pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *voice = runtime->private_data; + void __iomem *ctrl_base = voice->ctrl_base; + void __iomem *wave_base = voice->wave_base; + u32 format, dma_addr, control, sso_eso, delta, reg; + u16 leo; + + /* We rely on the PCM core to ensure that the parameters for this + * substream do not change on us while we're programming the HW. + */ + format = 0; + if (snd_pcm_format_width(runtime->format) == 8) + format |= SIS_PLAY_DMA_FORMAT_8BIT; + if (!snd_pcm_format_signed(runtime->format)) + format |= SIS_PLAY_DMA_FORMAT_UNSIGNED; + if (runtime->channels == 1) + format |= SIS_PLAY_DMA_FORMAT_MONO; + + /* The baseline setup is for a single period per buffer, and + * we add bells and whistles as needed from there. + */ + dma_addr = runtime->dma_addr; + leo = runtime->buffer_size - 1; + control = leo | SIS_PLAY_DMA_LOOP | SIS_PLAY_DMA_INTR_AT_LEO; + sso_eso = leo; + + if (runtime->period_size == (runtime->buffer_size / 2)) { + control |= SIS_PLAY_DMA_INTR_AT_MLP; + } else if (runtime->period_size != runtime->buffer_size) { + voice->flags |= VOICE_SSO_TIMING; + voice->sso = runtime->period_size - 1; + voice->period_size = runtime->period_size; + voice->buffer_size = runtime->buffer_size; + + control &= ~SIS_PLAY_DMA_INTR_AT_LEO; + control |= SIS_PLAY_DMA_INTR_AT_SSO; + sso_eso |= (runtime->period_size - 1) << 16; + } + + delta = sis_rate_to_delta(runtime->rate); + + /* Ok, we're ready to go, set up the channel. + */ + writel(format, ctrl_base + SIS_PLAY_DMA_FORMAT_CSO); + writel(dma_addr, ctrl_base + SIS_PLAY_DMA_BASE); + writel(control, ctrl_base + SIS_PLAY_DMA_CONTROL); + writel(sso_eso, ctrl_base + SIS_PLAY_DMA_SSO_ESO); + + for (reg = 0; reg < SIS_WAVE_SIZE; reg += 4) + writel(0, wave_base + reg); + + writel(SIS_WAVE_GENERAL_WAVE_VOLUME, wave_base + SIS_WAVE_GENERAL); + writel(delta << 16, wave_base + SIS_WAVE_GENERAL_ARTICULATION); + writel(SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE | + SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE | + SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE, + wave_base + SIS_WAVE_CHANNEL_CONTROL); + + /* Force PCI writes to post. */ + readl(ctrl_base); + + return 0; +} + +static int sis_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct sis7019 *sis = snd_pcm_substream_chip(substream); + unsigned long io = sis->ioport; + struct snd_pcm_substream *s; + struct voice *voice; + void *chip; + int starting; + u32 record = 0; + u32 play[2] = { 0, 0 }; + + /* No locks needed, as the PCM core will hold the locks on the + * substreams, and the HW will only start/stop the indicated voices + * without changing the state of the others. + */ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + starting = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + starting = 0; + break; + default: + return -EINVAL; + } + + snd_pcm_group_for_each_entry(s, substream) { + /* Make sure it is for us... */ + chip = snd_pcm_substream_chip(s); + if (chip != sis) + continue; + + voice = s->runtime->private_data; + if (voice->flags & VOICE_CAPTURE) { + record |= 1 << voice->num; + voice = voice->timing; + } + + /* voice could be NULL if this a recording stream, and it + * doesn't have an external timing channel. + */ + if (voice) + play[voice->num / 32] |= 1 << (voice->num & 0x1f); + + snd_pcm_trigger_done(s, substream); + } + + if (starting) { + if (record) + outl(record, io + SIS_RECORD_START_REG); + if (play[0]) + outl(play[0], io + SIS_PLAY_START_A_REG); + if (play[1]) + outl(play[1], io + SIS_PLAY_START_B_REG); + } else { + if (record) + outl(record, io + SIS_RECORD_STOP_REG); + if (play[0]) + outl(play[0], io + SIS_PLAY_STOP_A_REG); + if (play[1]) + outl(play[1], io + SIS_PLAY_STOP_B_REG); + } + return 0; +} + +static snd_pcm_uframes_t sis_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *voice = runtime->private_data; + u32 cso; + + cso = readl(voice->ctrl_base + SIS_PLAY_DMA_FORMAT_CSO); + cso &= 0xffff; + return cso; +} + +static int sis_capture_open(struct snd_pcm_substream *substream) +{ + struct sis7019 *sis = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *voice = &sis->capture_voice; + unsigned long flags; + + /* FIXME: The driver only supports recording from one channel + * at the moment, but it could support more. + */ + spin_lock_irqsave(&sis->voice_lock, flags); + if (voice->flags & VOICE_IN_USE) + voice = NULL; + else + voice->flags |= VOICE_IN_USE; + spin_unlock_irqrestore(&sis->voice_lock, flags); + + if (!voice) + return -EAGAIN; + + voice->substream = substream; + runtime->private_data = voice; + runtime->hw = sis_capture_hw_info; + runtime->hw.rates = sis->ac97[0]->rates[AC97_RATES_ADC]; + snd_pcm_limit_hw_rates(runtime); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + 9, 0xfff9); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + 9, 0xfff9); + snd_pcm_set_sync(substream); + return 0; +} + +static int sis_capture_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct sis7019 *sis = snd_pcm_substream_chip(substream); + int rc; + + rc = snd_ac97_set_rate(sis->ac97[0], AC97_PCM_LR_ADC_RATE, + params_rate(hw_params)); + if (rc) + goto out; + + rc = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (rc < 0) + goto out; + + rc = sis_alloc_timing_voice(substream, hw_params); + +out: + return rc; +} + +static void sis_prepare_timing_voice(struct voice *voice, + struct snd_pcm_substream *substream) +{ + struct sis7019 *sis = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *timing = voice->timing; + void __iomem *play_base = timing->ctrl_base; + void __iomem *wave_base = timing->wave_base; + u16 buffer_size, period_size; + u32 format, control, sso_eso, delta; + u32 vperiod, sso, reg; + + /* Set our initial buffer and period as large as we can given a + * single page of silence. + */ + buffer_size = 4096 / runtime->channels; + buffer_size /= snd_pcm_format_size(runtime->format, 1); + period_size = buffer_size; + + /* Initially, we want to interrupt just a bit behind the end of + * the period we're clocking out. 10 samples seems to give a good + * delay. + * + * We want to spread our interrupts throughout the virtual period, + * so that we don't end up with two interrupts back to back at the + * end -- this helps minimize the effects of any jitter. Adjust our + * clocking period size so that the last period is at least a fourth + * of a full period. + * + * This is all moot if we don't need to use virtual periods. + */ + vperiod = runtime->period_size + 10; + if (vperiod > period_size) { + u16 tail = vperiod % period_size; + u16 quarter_period = period_size / 4; + + if (tail && tail < quarter_period) { + u16 loops = vperiod / period_size; + + tail = quarter_period - tail; + tail += loops - 1; + tail /= loops; + period_size -= tail; + } + + sso = period_size - 1; + } else { + /* The initial period will fit inside the buffer, so we + * don't need to use virtual periods -- disable them. + */ + period_size = runtime->period_size; + sso = vperiod - 1; + vperiod = 0; + } + + /* The interrupt handler implements the timing syncronization, so + * setup its state. + */ + timing->flags |= VOICE_SYNC_TIMING; + timing->sync_base = voice->ctrl_base; + timing->sync_cso = runtime->period_size - 1; + timing->sync_period_size = runtime->period_size; + timing->sync_buffer_size = runtime->buffer_size; + timing->period_size = period_size; + timing->buffer_size = buffer_size; + timing->sso = sso; + timing->vperiod = vperiod; + + /* Using unsigned samples with the all-zero silence buffer + * forces the output to the lower rail, killing playback. + * So ignore unsigned vs signed -- it doesn't change the timing. + */ + format = 0; + if (snd_pcm_format_width(runtime->format) == 8) + format = SIS_CAPTURE_DMA_FORMAT_8BIT; + if (runtime->channels == 1) + format |= SIS_CAPTURE_DMA_FORMAT_MONO; + + control = timing->buffer_size - 1; + control |= SIS_PLAY_DMA_LOOP | SIS_PLAY_DMA_INTR_AT_SSO; + sso_eso = timing->buffer_size - 1; + sso_eso |= timing->sso << 16; + + delta = sis_rate_to_delta(runtime->rate); + + /* We've done the math, now configure the channel. + */ + writel(format, play_base + SIS_PLAY_DMA_FORMAT_CSO); + writel(sis->silence_dma_addr, play_base + SIS_PLAY_DMA_BASE); + writel(control, play_base + SIS_PLAY_DMA_CONTROL); + writel(sso_eso, play_base + SIS_PLAY_DMA_SSO_ESO); + + for (reg = 0; reg < SIS_WAVE_SIZE; reg += 4) + writel(0, wave_base + reg); + + writel(SIS_WAVE_GENERAL_WAVE_VOLUME, wave_base + SIS_WAVE_GENERAL); + writel(delta << 16, wave_base + SIS_WAVE_GENERAL_ARTICULATION); + writel(SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE | + SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE | + SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE, + wave_base + SIS_WAVE_CHANNEL_CONTROL); +} + +static int sis_pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *voice = runtime->private_data; + void __iomem *rec_base = voice->ctrl_base; + u32 format, dma_addr, control; + u16 leo; + + /* We rely on the PCM core to ensure that the parameters for this + * substream do not change on us while we're programming the HW. + */ + format = 0; + if (snd_pcm_format_width(runtime->format) == 8) + format = SIS_CAPTURE_DMA_FORMAT_8BIT; + if (!snd_pcm_format_signed(runtime->format)) + format |= SIS_CAPTURE_DMA_FORMAT_UNSIGNED; + if (runtime->channels == 1) + format |= SIS_CAPTURE_DMA_FORMAT_MONO; + + dma_addr = runtime->dma_addr; + leo = runtime->buffer_size - 1; + control = leo | SIS_CAPTURE_DMA_LOOP; + + /* If we've got more than two periods per buffer, then we have + * use a timing voice to clock out the periods. Otherwise, we can + * use the capture channel's interrupts. + */ + if (voice->timing) { + sis_prepare_timing_voice(voice, substream); + } else { + control |= SIS_CAPTURE_DMA_INTR_AT_LEO; + if (runtime->period_size != runtime->buffer_size) + control |= SIS_CAPTURE_DMA_INTR_AT_MLP; + } + + writel(format, rec_base + SIS_CAPTURE_DMA_FORMAT_CSO); + writel(dma_addr, rec_base + SIS_CAPTURE_DMA_BASE); + writel(control, rec_base + SIS_CAPTURE_DMA_CONTROL); + + /* Force the writes to post. */ + readl(rec_base); + + return 0; +} + +static struct snd_pcm_ops sis_playback_ops = { + .open = sis_playback_open, + .close = sis_substream_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = sis_playback_hw_params, + .hw_free = sis_hw_free, + .prepare = sis_pcm_playback_prepare, + .trigger = sis_pcm_trigger, + .pointer = sis_pcm_pointer, +}; + +static struct snd_pcm_ops sis_capture_ops = { + .open = sis_capture_open, + .close = sis_substream_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = sis_capture_hw_params, + .hw_free = sis_hw_free, + .prepare = sis_pcm_capture_prepare, + .trigger = sis_pcm_trigger, + .pointer = sis_pcm_pointer, +}; + +static int __devinit sis_pcm_create(struct sis7019 *sis) +{ + struct snd_pcm *pcm; + int rc; + + /* We have 64 voices, and the driver currently records from + * only one channel, though that could change in the future. + */ + rc = snd_pcm_new(sis->card, "SiS7019", 0, 64, 1, &pcm); + if (rc) + return rc; + + pcm->private_data = sis; + strcpy(pcm->name, "SiS7019"); + sis->pcm = pcm; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &sis_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &sis_capture_ops); + + /* Try to preallocate some memory, but it's not the end of the + * world if this fails. + */ + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(sis->pci), 64*1024, 128*1024); + + return 0; +} + +static unsigned short sis_ac97_rw(struct sis7019 *sis, int codec, u32 cmd) +{ + unsigned long io = sis->ioport; + unsigned short val = 0xffff; + u16 status; + u16 rdy; + int count; + const static u16 codec_ready[3] = { + SIS_AC97_STATUS_CODEC_READY, + SIS_AC97_STATUS_CODEC2_READY, + SIS_AC97_STATUS_CODEC3_READY, + }; + + rdy = codec_ready[codec]; + + + /* Get the AC97 semaphore -- software first, so we don't spin + * pounding out IO reads on the hardware semaphore... + */ + mutex_lock(&sis->ac97_mutex); + + count = 0xffff; + while ((inw(io + SIS_AC97_SEMA) & SIS_AC97_SEMA_BUSY) && --count) + udelay(1); + + if (!count) + goto timeout; + + /* ... and wait for any outstanding commands to complete ... + */ + count = 0xffff; + do { + status = inw(io + SIS_AC97_STATUS); + if ((status & rdy) && !(status & SIS_AC97_STATUS_BUSY)) + break; + + udelay(1); + } while (--count); + + if (!count) + goto timeout_sema; + + /* ... before sending our command and waiting for it to finish ... + */ + outl(cmd, io + SIS_AC97_CMD); + udelay(10); + + count = 0xffff; + while ((inw(io + SIS_AC97_STATUS) & SIS_AC97_STATUS_BUSY) && --count) + udelay(1); + + /* ... and reading the results (if any). + */ + val = inl(io + SIS_AC97_CMD) >> 16; + +timeout_sema: + outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA); +timeout: + mutex_unlock(&sis->ac97_mutex); + + if (!count) { + printk(KERN_ERR "sis7019: ac97 codec %d timeout cmd 0x%08x\n", + codec, cmd); + } + + return val; +} + +static void sis_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + const static u32 cmd[3] = { + SIS_AC97_CMD_CODEC_WRITE, + SIS_AC97_CMD_CODEC2_WRITE, + SIS_AC97_CMD_CODEC3_WRITE, + }; + sis_ac97_rw(ac97->private_data, ac97->num, + (val << 16) | (reg << 8) | cmd[ac97->num]); +} + +static unsigned short sis_ac97_read(struct snd_ac97 *ac97, unsigned short reg) +{ + const static u32 cmd[3] = { + SIS_AC97_CMD_CODEC_READ, + SIS_AC97_CMD_CODEC2_READ, + SIS_AC97_CMD_CODEC3_READ, + }; + return sis_ac97_rw(ac97->private_data, ac97->num, + (reg << 8) | cmd[ac97->num]); +} + +static int __devinit sis_mixer_create(struct sis7019 *sis) +{ + struct snd_ac97_bus *bus; + struct snd_ac97_template ac97; + static struct snd_ac97_bus_ops ops = { + .write = sis_ac97_write, + .read = sis_ac97_read, + }; + int rc; + + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = sis; + + rc = snd_ac97_bus(sis->card, 0, &ops, NULL, &bus); + if (!rc && sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT) + rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[0]); + ac97.num = 1; + if (!rc && (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT)) + rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[1]); + ac97.num = 2; + if (!rc && (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT)) + rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[2]); + + /* If we return an error here, then snd_card_free() should + * free up any ac97 codecs that got created, as well as the bus. + */ + return rc; +} + +static void sis_free_suspend(struct sis7019 *sis) +{ + int i; + + for (i = 0; i < SIS_SUSPEND_PAGES; i++) + kfree(sis->suspend_state[i]); +} + +static int sis_chip_free(struct sis7019 *sis) +{ + /* Reset the chip, and disable all interrputs. + */ + outl(SIS_GCR_SOFTWARE_RESET, sis->ioport + SIS_GCR); + udelay(10); + outl(0, sis->ioport + SIS_GCR); + outl(0, sis->ioport + SIS_GIER); + + /* Now, free everything we allocated. + */ + if (sis->irq >= 0) + free_irq(sis->irq, sis); + + if (sis->ioaddr) + iounmap(sis->ioaddr); + + pci_release_regions(sis->pci); + pci_disable_device(sis->pci); + + sis_free_suspend(sis); + return 0; +} + +static int sis_dev_free(struct snd_device *dev) +{ + struct sis7019 *sis = dev->device_data; + return sis_chip_free(sis); +} + +static int sis_chip_init(struct sis7019 *sis) +{ + unsigned long io = sis->ioport; + void __iomem *ioaddr = sis->ioaddr; + u16 status; + int count; + int i; + + /* Reset the audio controller + */ + outl(SIS_GCR_SOFTWARE_RESET, io + SIS_GCR); + udelay(10); + outl(0, io + SIS_GCR); + + /* Get the AC-link semaphore, and reset the codecs + */ + count = 0xffff; + while ((inw(io + SIS_AC97_SEMA) & SIS_AC97_SEMA_BUSY) && --count) + udelay(1); + + if (!count) + return -EIO; + + outl(SIS_AC97_CMD_CODEC_COLD_RESET, io + SIS_AC97_CMD); + udelay(10); + + count = 0xffff; + while ((inw(io + SIS_AC97_STATUS) & SIS_AC97_STATUS_BUSY) && --count) + udelay(1); + + /* Now that we've finished the reset, find out what's attached. + */ + status = inl(io + SIS_AC97_STATUS); + if (status & SIS_AC97_STATUS_CODEC_READY) + sis->codecs_present |= SIS_PRIMARY_CODEC_PRESENT; + if (status & SIS_AC97_STATUS_CODEC2_READY) + sis->codecs_present |= SIS_SECONDARY_CODEC_PRESENT; + if (status & SIS_AC97_STATUS_CODEC3_READY) + sis->codecs_present |= SIS_TERTIARY_CODEC_PRESENT; + + /* All done, let go of the semaphore, and check for errors + */ + outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA); + if (!sis->codecs_present || !count) + return -EIO; + + /* Let the hardware know that the audio driver is alive, + * and enable PCM slots on the AC-link for L/R playback (3 & 4) and + * record channels. We're going to want to use Variable Rate Audio + * for recording, to avoid needlessly resampling from 48kHZ. + */ + outl(SIS_AC97_CONF_AUDIO_ALIVE, io + SIS_AC97_CONF); + outl(SIS_AC97_CONF_AUDIO_ALIVE | SIS_AC97_CONF_PCM_LR_ENABLE | + SIS_AC97_CONF_PCM_CAP_MIC_ENABLE | + SIS_AC97_CONF_PCM_CAP_LR_ENABLE | + SIS_AC97_CONF_CODEC_VRA_ENABLE, io + SIS_AC97_CONF); + + /* All AC97 PCM slots should be sourced from sub-mixer 0. + */ + outl(0, io + SIS_AC97_PSR); + + /* There is only one valid DMA setup for a PCI environment. + */ + outl(SIS_DMA_CSR_PCI_SETTINGS, io + SIS_DMA_CSR); + + /* Reset the syncronization groups for all of the channels + * to be asyncronous. If we start doing SPDIF or 5.1 sound, etc. + * we'll need to change how we handle these. Until then, we just + * assign sub-mixer 0 to all playback channels, and avoid any + * attenuation on the audio. + */ + outl(0, io + SIS_PLAY_SYNC_GROUP_A); + outl(0, io + SIS_PLAY_SYNC_GROUP_B); + outl(0, io + SIS_PLAY_SYNC_GROUP_C); + outl(0, io + SIS_PLAY_SYNC_GROUP_D); + outl(0, io + SIS_MIXER_SYNC_GROUP); + + for (i = 0; i < 64; i++) { + writel(i, SIS_MIXER_START_ADDR(ioaddr, i)); + writel(SIS_MIXER_RIGHT_NO_ATTEN | SIS_MIXER_LEFT_NO_ATTEN | + SIS_MIXER_DEST_0, SIS_MIXER_ADDR(ioaddr, i)); + } + + /* Don't attenuate any audio set for the wave amplifier. + * + * FIXME: Maximum attenuation is set for the music amp, which will + * need to change if we start using the synth engine. + */ + outl(0xffff0000, io + SIS_WEVCR); + + /* Ensure that the wave engine is in normal operating mode. + */ + outl(0, io + SIS_WECCR); + + /* Go ahead and enable the DMA interrupts. They won't go live + * until we start a channel. + */ + outl(SIS_GIER_AUDIO_PLAY_DMA_IRQ_ENABLE | + SIS_GIER_AUDIO_RECORD_DMA_IRQ_ENABLE, io + SIS_GIER); + + return 0; +} + +#ifdef CONFIG_PM +static int sis_suspend(struct pci_dev *pci, pm_message_t state) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct sis7019 *sis = card->private_data; + void __iomem *ioaddr = sis->ioaddr; + int i; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + snd_pcm_suspend_all(sis->pcm); + if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT) + snd_ac97_suspend(sis->ac97[0]); + if (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT) + snd_ac97_suspend(sis->ac97[1]); + if (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT) + snd_ac97_suspend(sis->ac97[2]); + + /* snd_pcm_suspend_all() stopped all channels, so we're quiescent. + */ + if (sis->irq >= 0) { + synchronize_irq(sis->irq); + free_irq(sis->irq, sis); + sis->irq = -1; + } + + /* Save the internal state away + */ + for (i = 0; i < 4; i++) { + memcpy_fromio(sis->suspend_state[i], ioaddr, 4096); + ioaddr += 4096; + } + + pci_disable_device(pci); + pci_save_state(pci); + pci_set_power_state(pci, pci_choose_state(pci, state)); + return 0; +} + +static int sis_resume(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct sis7019 *sis = card->private_data; + void __iomem *ioaddr = sis->ioaddr; + int i; + + pci_set_power_state(pci, PCI_D0); + pci_restore_state(pci); + + if (pci_enable_device(pci) < 0) { + printk(KERN_ERR "sis7019: unable to re-enable device\n"); + goto error; + } + + if (sis_chip_init(sis)) { + printk(KERN_ERR "sis7019: unable to re-init controller\n"); + goto error; + } + + if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED, + card->shortname, sis)) { + printk(KERN_ERR "sis7019: unable to regain IRQ %d\n", pci->irq); + goto error; + } + + /* Restore saved state, then clear out the page we use for the + * silence buffer. + */ + for (i = 0; i < 4; i++) { + memcpy_toio(ioaddr, sis->suspend_state[i], 4096); + ioaddr += 4096; + } + + memset(sis->suspend_state[0], 0, 4096); + + sis->irq = pci->irq; + pci_set_master(pci); + + if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT) + snd_ac97_resume(sis->ac97[0]); + if (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT) + snd_ac97_resume(sis->ac97[1]); + if (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT) + snd_ac97_resume(sis->ac97[2]); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; + +error: + snd_card_disconnect(card); + return -EIO; +} +#endif /* CONFIG_PM */ + +static int sis_alloc_suspend(struct sis7019 *sis) +{ + int i; + + /* We need 16K to store the internal wave engine state during a + * suspend, but we don't need it to be contiguous, so play nice + * with the memory system. We'll also use this area for a silence + * buffer. + */ + for (i = 0; i < SIS_SUSPEND_PAGES; i++) { + sis->suspend_state[i] = kmalloc(4096, GFP_KERNEL); + if (!sis->suspend_state[i]) + return -ENOMEM; + } + memset(sis->suspend_state[0], 0, 4096); + + return 0; +} + +static int __devinit sis_chip_create(struct snd_card *card, + struct pci_dev *pci) +{ + struct sis7019 *sis = card->private_data; + struct voice *voice; + static struct snd_device_ops ops = { + .dev_free = sis_dev_free, + }; + int rc; + int i; + + rc = pci_enable_device(pci); + if (rc) + goto error_out; + + if (pci_set_dma_mask(pci, DMA_30BIT_MASK) < 0) { + printk(KERN_ERR "sis7019: architecture does not support " + "30-bit PCI busmaster DMA"); + goto error_out_enabled; + } + + memset(sis, 0, sizeof(*sis)); + mutex_init(&sis->ac97_mutex); + spin_lock_init(&sis->voice_lock); + sis->card = card; + sis->pci = pci; + sis->irq = -1; + sis->ioport = pci_resource_start(pci, 0); + + rc = pci_request_regions(pci, "SiS7019"); + if (rc) { + printk(KERN_ERR "sis7019: unable request regions\n"); + goto error_out_enabled; + } + + rc = -EIO; + sis->ioaddr = ioremap_nocache(pci_resource_start(pci, 1), 0x4000); + if (!sis->ioaddr) { + printk(KERN_ERR "sis7019: unable to remap MMIO, aborting\n"); + goto error_out_cleanup; + } + + rc = sis_alloc_suspend(sis); + if (rc < 0) { + printk(KERN_ERR "sis7019: unable to allocate state storage\n"); + goto error_out_cleanup; + } + + rc = sis_chip_init(sis); + if (rc) + goto error_out_cleanup; + + if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED, + card->shortname, sis)) { + printk(KERN_ERR "unable to allocate irq %d\n", sis->irq); + goto error_out_cleanup; + } + + sis->irq = pci->irq; + pci_set_master(pci); + + for (i = 0; i < 64; i++) { + voice = &sis->voices[i]; + voice->num = i; + voice->ctrl_base = SIS_PLAY_DMA_ADDR(sis->ioaddr, i); + voice->wave_base = SIS_WAVE_ADDR(sis->ioaddr, i); + } + + voice = &sis->capture_voice; + voice->flags = VOICE_CAPTURE; + voice->num = SIS_CAPTURE_CHAN_AC97_PCM_IN; + voice->ctrl_base = SIS_CAPTURE_DMA_ADDR(sis->ioaddr, voice->num); + + rc = snd_device_new(card, SNDRV_DEV_LOWLEVEL, sis, &ops); + if (rc) + goto error_out_cleanup; + + snd_card_set_dev(card, &pci->dev); + + return 0; + +error_out_cleanup: + sis_chip_free(sis); + +error_out_enabled: + pci_disable_device(pci); + +error_out: + return rc; +} + +static int __devinit snd_sis7019_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + struct snd_card *card; + struct sis7019 *sis; + int rc; + + rc = -ENOENT; + if (!enable) + goto error_out; + + rc = -ENOMEM; + card = snd_card_new(index, id, THIS_MODULE, sizeof(*sis)); + if (!card) + goto error_out; + + strcpy(card->driver, "SiS7019"); + strcpy(card->shortname, "SiS7019"); + rc = sis_chip_create(card, pci); + if (rc) + goto card_error_out; + + sis = card->private_data; + + rc = sis_mixer_create(sis); + if (rc) + goto card_error_out; + + rc = sis_pcm_create(sis); + if (rc) + goto card_error_out; + + snprintf(card->longname, sizeof(card->longname), + "%s Audio Accelerator with %s at 0x%lx, irq %d", + card->shortname, snd_ac97_get_short_name(sis->ac97[0]), + sis->ioport, sis->irq); + + rc = snd_card_register(card); + if (rc) + goto card_error_out; + + pci_set_drvdata(pci, card); + return 0; + +card_error_out: + snd_card_free(card); + +error_out: + return rc; +} + +static void __devexit snd_sis7019_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver sis7019_driver = { + .name = "SiS7019", + .id_table = snd_sis7019_ids, + .probe = snd_sis7019_probe, + .remove = __devexit_p(snd_sis7019_remove), + +#ifdef CONFIG_PM + .suspend = sis_suspend, + .resume = sis_resume, +#endif +}; + +static int __init sis7019_init(void) +{ + return pci_register_driver(&sis7019_driver); +} + +static void __exit sis7019_exit(void) +{ + pci_unregister_driver(&sis7019_driver); +} + +module_init(sis7019_init); +module_exit(sis7019_exit); diff --git a/sound/pci/sis7019.h b/sound/pci/sis7019.h new file mode 100644 index 00000000000..013b6739a74 --- /dev/null +++ b/sound/pci/sis7019.h @@ -0,0 +1,342 @@ +#ifndef __sis7019_h__ +#define __sis7019_h__ + +/* + * Definitions for SiS7019 Audio Accelerator + * + * Copyright (C) 2004-2007, David Dillow + * Written by David Dillow <dave@thedillows.org> + * Inspired by the Trident 4D-WaveDX/NX driver. + * + * All rights reserved. + * + * 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, version 2. + * + * 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 + */ + + +/* General Control Register */ +#define SIS_GCR 0x00 +#define SIS_GCR_MACRO_POWER_DOWN 0x80000000 +#define SIS_GCR_MODEM_ENABLE 0x00010000 +#define SIS_GCR_SOFTWARE_RESET 0x00000001 + +/* General Interrupt Enable Register */ +#define SIS_GIER 0x04 +#define SIS_GIER_MODEM_TIMER_IRQ_ENABLE 0x00100000 +#define SIS_GIER_MODEM_RX_DMA_IRQ_ENABLE 0x00080000 +#define SIS_GIER_MODEM_TX_DMA_IRQ_ENABLE 0x00040000 +#define SIS_GIER_AC97_GPIO1_IRQ_ENABLE 0x00020000 +#define SIS_GIER_AC97_GPIO0_IRQ_ENABLE 0x00010000 +#define SIS_GIER_AC97_SAMPLE_TIMER_IRQ_ENABLE 0x00000010 +#define SIS_GIER_AUDIO_GLOBAL_TIMER_IRQ_ENABLE 0x00000008 +#define SIS_GIER_AUDIO_RECORD_DMA_IRQ_ENABLE 0x00000004 +#define SIS_GIER_AUDIO_PLAY_DMA_IRQ_ENABLE 0x00000002 +#define SIS_GIER_AUDIO_WAVE_ENGINE_IRQ_ENABLE 0x00000001 + +/* General Interrupt Status Register */ +#define SIS_GISR 0x08 +#define SIS_GISR_MODEM_TIMER_IRQ_STATUS 0x00100000 +#define SIS_GISR_MODEM_RX_DMA_IRQ_STATUS 0x00080000 +#define SIS_GISR_MODEM_TX_DMA_IRQ_STATUS 0x00040000 +#define SIS_GISR_AC97_GPIO1_IRQ_STATUS 0x00020000 +#define SIS_GISR_AC97_GPIO0_IRQ_STATUS 0x00010000 +#define SIS_GISR_AC97_SAMPLE_TIMER_IRQ_STATUS 0x00000010 +#define SIS_GISR_AUDIO_GLOBAL_TIMER_IRQ_STATUS 0x00000008 +#define SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS 0x00000004 +#define SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS 0x00000002 +#define SIS_GISR_AUDIO_WAVE_ENGINE_IRQ_STATUS 0x00000001 + +/* DMA Control Register */ +#define SIS_DMA_CSR 0x10 +#define SIS_DMA_CSR_PCI_SETTINGS 0x0000001d +#define SIS_DMA_CSR_CONCURRENT_ENABLE 0x00000200 +#define SIS_DMA_CSR_PIPELINE_ENABLE 0x00000100 +#define SIS_DMA_CSR_RX_DRAIN_ENABLE 0x00000010 +#define SIS_DMA_CSR_RX_FILL_ENABLE 0x00000008 +#define SIS_DMA_CSR_TX_DRAIN_ENABLE 0x00000004 +#define SIS_DMA_CSR_TX_LOWPRI_FILL_ENABLE 0x00000002 +#define SIS_DMA_CSR_TX_HIPRI_FILL_ENABLE 0x00000001 + +/* Playback Channel Start Registers */ +#define SIS_PLAY_START_A_REG 0x14 +#define SIS_PLAY_START_B_REG 0x18 + +/* Playback Channel Stop Registers */ +#define SIS_PLAY_STOP_A_REG 0x1c +#define SIS_PLAY_STOP_B_REG 0x20 + +/* Recording Channel Start Register */ +#define SIS_RECORD_START_REG 0x24 + +/* Recording Channel Stop Register */ +#define SIS_RECORD_STOP_REG 0x28 + +/* Playback Interrupt Status Registers */ +#define SIS_PISR_A 0x2c +#define SIS_PISR_B 0x30 + +/* Recording Interrupt Status Register */ +#define SIS_RISR 0x34 + +/* AC97 AC-link Playback Source Register */ +#define SIS_AC97_PSR 0x40 +#define SIS_AC97_PSR_MODEM_HEADSET_SRC_MIXER 0x0f000000 +#define SIS_AC97_PSR_MODEM_LINE2_SRC_MIXER 0x00f00000 +#define SIS_AC97_PSR_MODEM_LINE1_SRC_MIXER 0x000f0000 +#define SIS_AC97_PSR_PCM_LFR_SRC_MIXER 0x0000f000 +#define SIS_AC97_PSR_PCM_SURROUND_SRC_MIXER 0x00000f00 +#define SIS_AC97_PSR_PCM_CENTER_SRC_MIXER 0x000000f0 +#define SIS_AC97_PSR_PCM_LR_SRC_MIXER 0x0000000f + +/* AC97 AC-link Command Register */ +#define SIS_AC97_CMD 0x50 +#define SIS_AC97_CMD_DATA_MASK 0xffff0000 +#define SIS_AC97_CMD_REG_MASK 0x0000ff00 +#define SIS_AC97_CMD_CODEC3_READ 0x0000000d +#define SIS_AC97_CMD_CODEC3_WRITE 0x0000000c +#define SIS_AC97_CMD_CODEC2_READ 0x0000000b +#define SIS_AC97_CMD_CODEC2_WRITE 0x0000000a +#define SIS_AC97_CMD_CODEC_READ 0x00000009 +#define SIS_AC97_CMD_CODEC_WRITE 0x00000008 +#define SIS_AC97_CMD_CODEC_WARM_RESET 0x00000005 +#define SIS_AC97_CMD_CODEC_COLD_RESET 0x00000004 +#define SIS_AC97_CMD_DONE 0x00000000 + +/* AC97 AC-link Semaphore Register */ +#define SIS_AC97_SEMA 0x54 +#define SIS_AC97_SEMA_BUSY 0x00000001 +#define SIS_AC97_SEMA_RELEASE 0x00000000 + +/* AC97 AC-link Status Register */ +#define SIS_AC97_STATUS 0x58 +#define SIS_AC97_STATUS_AUDIO_D2_INACT_SECS 0x03f00000 +#define SIS_AC97_STATUS_MODEM_ALIVE 0x00002000 +#define SIS_AC97_STATUS_AUDIO_ALIVE 0x00001000 +#define SIS_AC97_STATUS_CODEC3_READY 0x00000400 +#define SIS_AC97_STATUS_CODEC2_READY 0x00000200 +#define SIS_AC97_STATUS_CODEC_READY 0x00000100 +#define SIS_AC97_STATUS_WARM_RESET 0x00000080 +#define SIS_AC97_STATUS_COLD_RESET 0x00000040 +#define SIS_AC97_STATUS_POWERED_DOWN 0x00000020 +#define SIS_AC97_STATUS_NORMAL 0x00000010 +#define SIS_AC97_STATUS_READ_EXPIRED 0x00000004 +#define SIS_AC97_STATUS_SEMAPHORE 0x00000002 +#define SIS_AC97_STATUS_BUSY 0x00000001 + +/* AC97 AC-link Audio Configuration Register */ +#define SIS_AC97_CONF 0x5c +#define SIS_AC97_CONF_AUDIO_ALIVE 0x80000000 +#define SIS_AC97_CONF_WARM_RESET_ENABLE 0x40000000 +#define SIS_AC97_CONF_PR6_ENABLE 0x20000000 +#define SIS_AC97_CONF_PR5_ENABLE 0x10000000 +#define SIS_AC97_CONF_PR4_ENABLE 0x08000000 +#define SIS_AC97_CONF_PR3_ENABLE 0x04000000 +#define SIS_AC97_CONF_PR2_PR7_ENABLE 0x02000000 +#define SIS_AC97_CONF_PR0_PR1_ENABLE 0x01000000 +#define SIS_AC97_CONF_AUTO_PM_ENABLE 0x00800000 +#define SIS_AC97_CONF_PCM_LFE_ENABLE 0x00080000 +#define SIS_AC97_CONF_PCM_SURROUND_ENABLE 0x00040000 +#define SIS_AC97_CONF_PCM_CENTER_ENABLE 0x00020000 +#define SIS_AC97_CONF_PCM_LR_ENABLE 0x00010000 +#define SIS_AC97_CONF_PCM_CAP_MIC_ENABLE 0x00002000 +#define SIS_AC97_CONF_PCM_CAP_LR_ENABLE 0x00001000 +#define SIS_AC97_CONF_PCM_CAP_MIC_FROM_CODEC3 0x00000200 +#define SIS_AC97_CONF_PCM_CAP_LR_FROM_CODEC3 0x00000100 +#define SIS_AC97_CONF_CODEC3_PM_VRM 0x00000080 +#define SIS_AC97_CONF_CODEC_PM_VRM 0x00000040 +#define SIS_AC97_CONF_CODEC3_VRA_ENABLE 0x00000020 +#define SIS_AC97_CONF_CODEC_VRA_ENABLE 0x00000010 +#define SIS_AC97_CONF_CODEC3_PM_EAC 0x00000008 +#define SIS_AC97_CONF_CODEC_PM_EAC 0x00000004 +#define SIS_AC97_CONF_CODEC3_EXISTS 0x00000002 +#define SIS_AC97_CONF_CODEC_EXISTS 0x00000001 + +/* Playback Channel Sync Group registers */ +#define SIS_PLAY_SYNC_GROUP_A 0x80 +#define SIS_PLAY_SYNC_GROUP_B 0x84 +#define SIS_PLAY_SYNC_GROUP_C 0x88 +#define SIS_PLAY_SYNC_GROUP_D 0x8c +#define SIS_MIXER_SYNC_GROUP 0x90 + +/* Wave Engine Config and Control Register */ +#define SIS_WECCR 0xa0 +#define SIS_WECCR_TESTMODE_MASK 0x00300000 +#define SIS_WECCR_TESTMODE_NORMAL 0x00000000 +#define SIS_WECCR_TESTMODE_BYPASS_NSO_ALPHA 0x00100000 +#define SIS_WECCR_TESTMODE_BYPASS_FC 0x00200000 +#define SIS_WECCR_TESTMODE_BYPASS_WOL 0x00300000 +#define SIS_WECCR_RESONANCE_DELAY_MASK 0x00060000 +#define SIS_WECCR_RESONANCE_DELAY_NONE 0x00000000 +#define SIS_WECCR_RESONANCE_DELAY_FC_1F00 0x00020000 +#define SIS_WECCR_RESONANCE_DELAY_FC_1E00 0x00040000 +#define SIS_WECCR_RESONANCE_DELAY_FC_1C00 0x00060000 +#define SIS_WECCR_IGNORE_CHANNEL_PARMS 0x00010000 +#define SIS_WECCR_COMMAND_CHANNEL_ID_MASK 0x0003ff00 +#define SIS_WECCR_COMMAND_MASK 0x00000007 +#define SIS_WECCR_COMMAND_NONE 0x00000000 +#define SIS_WECCR_COMMAND_DONE 0x00000000 +#define SIS_WECCR_COMMAND_PAUSE 0x00000001 +#define SIS_WECCR_COMMAND_TOGGLE_VEG 0x00000002 +#define SIS_WECCR_COMMAND_TOGGLE_MEG 0x00000003 +#define SIS_WECCR_COMMAND_TOGGLE_VEG_MEG 0x00000004 + +/* Wave Engine Volume Control Register */ +#define SIS_WEVCR 0xa4 +#define SIS_WEVCR_LEFT_MUSIC_ATTENUATION_MASK 0xff000000 +#define SIS_WEVCR_RIGHT_MUSIC_ATTENUATION_MASK 0x00ff0000 +#define SIS_WEVCR_LEFT_WAVE_ATTENUATION_MASK 0x0000ff00 +#define SIS_WEVCR_RIGHT_WAVE_ATTENUATION_MASK 0x000000ff + +/* Wave Engine Interrupt Status Registers */ +#define SIS_WEISR_A 0xa8 +#define SIS_WEISR_B 0xac + + +/* Playback DMA parameters (paramter RAM) */ +#define SIS_PLAY_DMA_OFFSET 0x0000 +#define SIS_PLAY_DMA_SIZE 0x10 +#define SIS_PLAY_DMA_ADDR(addr, num) \ + ((num * SIS_PLAY_DMA_SIZE) + (addr) + SIS_PLAY_DMA_OFFSET) + +#define SIS_PLAY_DMA_FORMAT_CSO 0x00 +#define SIS_PLAY_DMA_FORMAT_UNSIGNED 0x00080000 +#define SIS_PLAY_DMA_FORMAT_8BIT 0x00040000 +#define SIS_PLAY_DMA_FORMAT_MONO 0x00020000 +#define SIS_PLAY_DMA_CSO_MASK 0x0000ffff +#define SIS_PLAY_DMA_BASE 0x04 +#define SIS_PLAY_DMA_CONTROL 0x08 +#define SIS_PLAY_DMA_STOP_AT_SSO 0x04000000 +#define SIS_PLAY_DMA_RELEASE 0x02000000 +#define SIS_PLAY_DMA_LOOP 0x01000000 +#define SIS_PLAY_DMA_INTR_AT_SSO 0x00080000 +#define SIS_PLAY_DMA_INTR_AT_ESO 0x00040000 +#define SIS_PLAY_DMA_INTR_AT_LEO 0x00020000 +#define SIS_PLAY_DMA_INTR_AT_MLP 0x00010000 +#define SIS_PLAY_DMA_LEO_MASK 0x0000ffff +#define SIS_PLAY_DMA_SSO_ESO 0x0c +#define SIS_PLAY_DMA_SSO_MASK 0xffff0000 +#define SIS_PLAY_DMA_ESO_MASK 0x0000ffff + +/* Capture DMA parameters (paramter RAM) */ +#define SIS_CAPTURE_DMA_OFFSET 0x0800 +#define SIS_CAPTURE_DMA_SIZE 0x10 +#define SIS_CAPTURE_DMA_ADDR(addr, num) \ + ((num * SIS_CAPTURE_DMA_SIZE) + (addr) + SIS_CAPTURE_DMA_OFFSET) + +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_0 0 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_1 1 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_2 2 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_3 3 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_4 4 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_5 5 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_6 6 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_7 7 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_8 8 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_9 9 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_10 10 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_11 11 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_12 12 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_13 13 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_14 14 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_15 15 +#define SIS_CAPTURE_CHAN_AC97_PCM_IN 16 +#define SIS_CAPTURE_CHAN_AC97_MIC_IN 17 +#define SIS_CAPTURE_CHAN_AC97_LINE1_IN 18 +#define SIS_CAPTURE_CHAN_AC97_LINE2_IN 19 +#define SIS_CAPTURE_CHAN_AC97_HANDSE_IN 20 + +#define SIS_CAPTURE_DMA_FORMAT_CSO 0x00 +#define SIS_CAPTURE_DMA_MONO_MODE_MASK 0xc0000000 +#define SIS_CAPTURE_DMA_MONO_MODE_AVG 0x00000000 +#define SIS_CAPTURE_DMA_MONO_MODE_LEFT 0x40000000 +#define SIS_CAPTURE_DMA_MONO_MODE_RIGHT 0x80000000 +#define SIS_CAPTURE_DMA_FORMAT_UNSIGNED 0x00080000 +#define SIS_CAPTURE_DMA_FORMAT_8BIT 0x00040000 +#define SIS_CAPTURE_DMA_FORMAT_MONO 0x00020000 +#define SIS_CAPTURE_DMA_CSO_MASK 0x0000ffff +#define SIS_CAPTURE_DMA_BASE 0x04 +#define SIS_CAPTURE_DMA_CONTROL 0x08 +#define SIS_CAPTURE_DMA_STOP_AT_SSO 0x04000000 +#define SIS_CAPTURE_DMA_RELEASE 0x02000000 +#define SIS_CAPTURE_DMA_LOOP 0x01000000 +#define SIS_CAPTURE_DMA_INTR_AT_LEO 0x00020000 +#define SIS_CAPTURE_DMA_INTR_AT_MLP 0x00010000 +#define SIS_CAPTURE_DMA_LEO_MASK 0x0000ffff +#define SIS_CAPTURE_DMA_RESERVED 0x0c + + +/* Mixer routing list start pointer (parameter RAM) */ +#define SIS_MIXER_START_OFFSET 0x1000 +#define SIS_MIXER_START_SIZE 0x04 +#define SIS_MIXER_START_ADDR(addr, num) \ + ((num * SIS_MIXER_START_SIZE) + (addr) + SIS_MIXER_START_OFFSET) + +#define SIS_MIXER_START_MASK 0x0000007f + +/* Mixer routing table (parameter RAM) */ +#define SIS_MIXER_OFFSET 0x1400 +#define SIS_MIXER_SIZE 0x04 +#define SIS_MIXER_ADDR(addr, num) \ + ((num * SIS_MIXER_SIZE) + (addr) + SIS_MIXER_OFFSET) + +#define SIS_MIXER_RIGHT_ATTENUTATION_MASK 0xff000000 +#define SIS_MIXER_RIGHT_NO_ATTEN 0xff000000 +#define SIS_MIXER_LEFT_ATTENUTATION_MASK 0x00ff0000 +#define SIS_MIXER_LEFT_NO_ATTEN 0x00ff0000 +#define SIS_MIXER_NEXT_ENTRY_MASK 0x00007f00 +#define SIS_MIXER_NEXT_ENTRY_NONE 0x00000000 +#define SIS_MIXER_DEST_MASK 0x0000007f +#define SIS_MIXER_DEST_0 0x00000020 +#define SIS_MIXER_DEST_1 0x00000021 +#define SIS_MIXER_DEST_2 0x00000022 +#define SIS_MIXER_DEST_3 0x00000023 +#define SIS_MIXER_DEST_4 0x00000024 +#define SIS_MIXER_DEST_5 0x00000025 +#define SIS_MIXER_DEST_6 0x00000026 +#define SIS_MIXER_DEST_7 0x00000027 +#define SIS_MIXER_DEST_8 0x00000028 +#define SIS_MIXER_DEST_9 0x00000029 +#define SIS_MIXER_DEST_10 0x0000002a +#define SIS_MIXER_DEST_11 0x0000002b +#define SIS_MIXER_DEST_12 0x0000002c +#define SIS_MIXER_DEST_13 0x0000002d +#define SIS_MIXER_DEST_14 0x0000002e +#define SIS_MIXER_DEST_15 0x0000002f + +/* Wave Engine Control Parameters (parameter RAM) */ +#define SIS_WAVE_OFFSET 0x2000 +#define SIS_WAVE_SIZE 0x40 +#define SIS_WAVE_ADDR(addr, num) \ + ((num * SIS_WAVE_SIZE) + (addr) + SIS_WAVE_OFFSET) + +#define SIS_WAVE_GENERAL 0x00 +#define SIS_WAVE_GENERAL_WAVE_VOLUME 0x80000000 +#define SIS_WAVE_GENERAL_MUSIC_VOLUME 0x00000000 +#define SIS_WAVE_GENERAL_VOLUME_MASK 0x7f000000 +#define SIS_WAVE_GENERAL_ARTICULATION 0x04 +#define SIS_WAVE_GENERAL_ARTICULATION_DELTA_MASK 0x3fff0000 +#define SIS_WAVE_ARTICULATION 0x08 +#define SIS_WAVE_TIMER 0x0c +#define SIS_WAVE_GENERATOR 0x10 +#define SIS_WAVE_CHANNEL_CONTROL 0x14 +#define SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE 0x80000000 +#define SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE 0x40000000 +#define SIS_WAVE_CHANNEL_CONTROL_FILTER_ENABLE 0x20000000 +#define SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE 0x10000000 +#define SIS_WAVE_LFO_EG_CONTROL 0x18 +#define SIS_WAVE_LFO_EG_CONTROL_2 0x1c +#define SIS_WAVE_LFO_EG_CONTROL_3 0x20 +#define SIS_WAVE_LFO_EG_CONTROL_4 0x24 + +#endif /* __sis7019_h__ */ diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 44a7f5fad57..0d3d305b0a0 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -22,7 +22,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/trident/Makefile b/sound/pci/trident/Makefile index 65f2c218324..88676b50f38 100644 --- a/sound/pci/trident/Makefile +++ b/sound/pci/trident/Makefile @@ -4,16 +4,6 @@ # snd-trident-objs := trident.o trident_main.o trident_memory.o -snd-trident-synth-objs := trident_synth.o - -# -# this function returns: -# "m" - CONFIG_SND_SEQUENCER is m -# <empty string> - CONFIG_SND_SEQUENCER is undefined -# otherwise parameter #1 value -# -sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) # Toplevel Module Dependency obj-$(CONFIG_SND_TRIDENT) += snd-trident.o -obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-trident-synth.o diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index 84884567df6..d94b16ffb38 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/time.h> @@ -155,13 +154,6 @@ static int __devinit snd_trident_probe(struct pci_dev *pci, return err; } -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) - if ((err = snd_trident_attach_synthesizer(trident)) < 0) { - snd_card_free(card); - return err; - } -#endif - snd_trident_create_gameport(trident); if ((err = snd_card_register(card)) < 0) { diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index a235e034a69..71138ff9b31 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -27,7 +27,6 @@ * SiS7018 S/PDIF support by Thomas Winischhofer <thomas@winischhofer.net> */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -436,7 +435,7 @@ static void snd_trident_free_synth_channel(struct snd_trident *trident, int chan Description: This routine will complete and write the 5 hardware channel registers to hardware. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. voice - synthesizer voice structure Each register field. @@ -514,7 +513,7 @@ EXPORT_SYMBOL(snd_trident_write_voice_regs); Description: This routine will write the new CSO offset register to hardware. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. voice - synthesizer voice structure CSO - new CSO value @@ -540,7 +539,7 @@ static void snd_trident_write_cso_reg(struct snd_trident * trident, Description: This routine will write the new ESO offset register to hardware. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. voice - synthesizer voice structure ESO - new ESO value @@ -566,7 +565,7 @@ static void snd_trident_write_eso_reg(struct snd_trident * trident, Description: This routine will write the new voice volume register to hardware. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. voice - synthesizer voice structure Vol - new voice volume @@ -597,7 +596,7 @@ static void snd_trident_write_vol_reg(struct snd_trident * trident, Description: This routine will write the new voice pan register to hardware. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. voice - synthesizer voice structure Pan - new pan value @@ -619,7 +618,7 @@ static void snd_trident_write_pan_reg(struct snd_trident * trident, Description: This routine will write the new reverb volume register to hardware. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. voice - synthesizer voice structure RVol - new reverb volume @@ -643,7 +642,7 @@ static void snd_trident_write_rvol_reg(struct snd_trident * trident, Description: This routine will write the new chorus volume register to hardware. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. voice - synthesizer voice structure CVol - new chorus volume @@ -666,7 +665,7 @@ static void snd_trident_write_cvol_reg(struct snd_trident * trident, Description: This routine converts rate in HZ to hardware delta value. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. rate - Real or Virtual channel number. Returns: Delta value. @@ -696,7 +695,7 @@ static unsigned int snd_trident_convert_rate(unsigned int rate) Description: This routine converts rate in HZ to hardware delta value. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. rate - Real or Virtual channel number. Returns: Delta value. @@ -726,7 +725,7 @@ static unsigned int snd_trident_convert_adc_rate(unsigned int rate) Description: This routine converts rate in HZ to spurious threshold. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. rate - Real or Virtual channel number. Returns: Delta value. @@ -748,7 +747,7 @@ static unsigned int snd_trident_spurious_threshold(unsigned int rate, Description: This routine returns a control mode for a PCM channel. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. substream - PCM substream Returns: Control value. @@ -781,7 +780,7 @@ static unsigned int snd_trident_control_mode(struct snd_pcm_substream *substream Description: Device I/O control handler for playback/capture parameters. - Paramters: substream - PCM substream class + Parameters: substream - PCM substream class cmd - what ioctl message to process arg - additional message infoarg @@ -1664,7 +1663,7 @@ static snd_pcm_uframes_t snd_trident_playback_pointer(struct snd_pcm_substream * Description: This routine return the capture position - Paramters: pcm1 - PCM device class + Parameters: pcm1 - PCM device class Returns: position of buffer @@ -2157,7 +2156,7 @@ static struct snd_pcm_ops snd_trident_spdif_7018_ops = { Description: This routine registers the 4DWave device for PCM support. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. Returns: None @@ -2215,7 +2214,7 @@ int __devinit snd_trident_pcm(struct snd_trident * trident, Description: This routine registers the 4DWave device for foldback PCM support. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. Returns: None @@ -2272,7 +2271,7 @@ int __devinit snd_trident_foldback_pcm(struct snd_trident * trident, Description: This routine registers the 4DWave-NX device for SPDIF support. - Paramters: trident - pointer to target device class for 4DWave-NX. + Parameters: trident - pointer to target device class for 4DWave-NX. Returns: None @@ -2956,7 +2955,7 @@ static int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_tr Description: This routine registers the 4DWave device for mixer support. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. Returns: None @@ -3313,12 +3312,6 @@ static void snd_trident_proc_read(struct snd_info_entry *entry, snd_iprintf(buffer, "Memory Free : %d\n", snd_util_mem_avail(trident->tlb.memhdr)); } } -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) - snd_iprintf(buffer,"\nWavetable Synth\n"); - snd_iprintf(buffer, "Memory Maximum : %d\n", trident->synth.max_size); - snd_iprintf(buffer, "Memory Used : %d\n", trident->synth.current_size); - snd_iprintf(buffer, "Memory Free : %d\n", (trident->synth.max_size-trident->synth.current_size)); -#endif } static void __devinit snd_trident_proc_init(struct snd_trident * trident) @@ -3344,7 +3337,7 @@ static int snd_trident_dev_free(struct snd_device *device) Description: Allocate and set up the TLB page table on 4D NX. Each entry has 4 bytes (physical PCI address). - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. Returns: 0 or negative error code @@ -3521,7 +3514,7 @@ static int snd_trident_sis_init(struct snd_trident *trident) Description: This routine will create the device specific class for the 4DWave card. It will also perform basic initialization. - Paramters: card - which card to create + Parameters: card - which card to create pci - interface to PCI bus resource info dma1ptr - playback dma buffer dma2ptr - capture dma buffer @@ -3667,7 +3660,7 @@ int __devinit snd_trident_create(struct snd_card *card, Description: This routine will free the device specific class for the 4DWave card. - Paramters: trident - device specific private data for 4DWave card + Parameters: trident - device specific private data for 4DWave card Returns: None. @@ -3705,7 +3698,7 @@ static int snd_trident_free(struct snd_trident *trident) Description: ISR for Trident 4DWave device - Paramters: trident - device specific private data for 4DWave card + Parameters: trident - device specific private data for 4DWave card Problems: It seems that Trident chips generates interrupts more than one time in special cases. The spurious interrupts are @@ -3815,28 +3808,6 @@ static irqreturn_t snd_trident_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -/*--------------------------------------------------------------------------- - snd_trident_attach_synthesizer - - Description: Attach synthesizer hooks - - Paramters: trident - device specific private data for 4DWave card - - Returns: None. - - ---------------------------------------------------------------------------*/ -int snd_trident_attach_synthesizer(struct snd_trident *trident) -{ -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) - if (snd_seq_device_new(trident->card, 1, SNDRV_SEQ_DEV_ID_TRIDENT, - sizeof(struct snd_trident *), &trident->seq_dev) >= 0) { - strcpy(trident->seq_dev->name, "4DWave"); - *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(trident->seq_dev) = trident; - } -#endif - return 0; -} - struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port) { struct snd_trident_voice *pvoice; diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c index 847b8c6d5c0..df9b487fa17 100644 --- a/sound/pci/trident/trident_memory.c +++ b/sound/pci/trident/trident_memory.c @@ -23,7 +23,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/pci.h> #include <linux/time.h> diff --git a/sound/pci/trident/trident_synth.c b/sound/pci/trident/trident_synth.c deleted file mode 100644 index 9b7dee84743..00000000000 --- a/sound/pci/trident/trident_synth.c +++ /dev/null @@ -1,1024 +0,0 @@ -/* - * Routines for Trident 4DWave NX/DX soundcards - Synthesizer - * Copyright (c) by Scott McNab <jedi@tartarus.uwa.edu.au> - * - * - * 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 <sound/driver.h> -#include <asm/io.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <sound/core.h> -#include <sound/trident.h> -#include <sound/seq_device.h> - -MODULE_AUTHOR("Scott McNab <jedi@tartarus.uwa.edu.au>"); -MODULE_DESCRIPTION("Routines for Trident 4DWave NX/DX soundcards - Synthesizer"); -MODULE_LICENSE("GPL"); - -/* linear to log pan conversion table (4.2 channel attenuation format) */ -static unsigned int pan_table[63] = { - 7959, 7733, 7514, 7301, 7093, 6892, 6697, 6507, - 6322, 6143, 5968, 5799, 5634, 5475, 5319, 5168, - 5022, 4879, 4741, 4606, 4475, 4349, 4225, 4105, - 3989, 3876, 3766, 3659, 3555, 3454, 3356, 3261, - 3168, 3078, 2991, 2906, 2824, 2744, 2666, 2590, - 2517, 2445, 2376, 2308, 2243, 2179, 2117, 2057, - 1999, 1942, 1887, 1833, 1781, 1731, 1682, 1634, - 1588, 1543, 1499, 1456, 1415, 1375, 1336 -}; - -#define LOG_TABLE_SIZE 386 - -/* Linear half-attenuation to log conversion table in the format: - * {linear volume, logarithmic attenuation equivalent}, ... - * - * Provides conversion from a linear half-volume value in the range - * [0,8192] to a logarithmic attenuation value in the range 0 to 6.02dB. - * Halving the linear volume is equivalent to an additional 6dB of - * logarithmic attenuation. The algorithm used in log_from_linear() - * therefore uses this table as follows: - * - * - loop and for every time the volume is less than half the maximum - * volume (16384), add another 6dB and halve the maximum value used - * for this comparison. - * - when the volume is greater than half the maximum volume, take - * the difference of the volume to half volume (in the range [0,8192]) - * and look up the log_table[] to find the nearest entry. - * - take the logarithic component of this entry and add it to the - * resulting attenuation. - * - * Thus this routine provides a linear->log conversion for a range of - * [0,16384] using only 386 table entries - * - * Note: although this table stores log attenuation in 8.8 format, values - * were only calculated for 6 bits fractional precision, since that is - * the most precision offered by the trident hardware. - */ - -static unsigned short log_table[LOG_TABLE_SIZE*2] = -{ - 4, 0x0604, 19, 0x0600, 34, 0x05fc, - 49, 0x05f8, 63, 0x05f4, 78, 0x05f0, 93, 0x05ec, 108, 0x05e8, - 123, 0x05e4, 138, 0x05e0, 153, 0x05dc, 168, 0x05d8, 183, 0x05d4, - 198, 0x05d0, 213, 0x05cc, 228, 0x05c8, 244, 0x05c4, 259, 0x05c0, - 274, 0x05bc, 289, 0x05b8, 304, 0x05b4, 320, 0x05b0, 335, 0x05ac, - 350, 0x05a8, 366, 0x05a4, 381, 0x05a0, 397, 0x059c, 412, 0x0598, - 428, 0x0594, 443, 0x0590, 459, 0x058c, 474, 0x0588, 490, 0x0584, - 506, 0x0580, 521, 0x057c, 537, 0x0578, 553, 0x0574, 568, 0x0570, - 584, 0x056c, 600, 0x0568, 616, 0x0564, 632, 0x0560, 647, 0x055c, - 663, 0x0558, 679, 0x0554, 695, 0x0550, 711, 0x054c, 727, 0x0548, - 743, 0x0544, 759, 0x0540, 776, 0x053c, 792, 0x0538, 808, 0x0534, - 824, 0x0530, 840, 0x052c, 857, 0x0528, 873, 0x0524, 889, 0x0520, - 906, 0x051c, 922, 0x0518, 938, 0x0514, 955, 0x0510, 971, 0x050c, - 988, 0x0508, 1004, 0x0504, 1021, 0x0500, 1037, 0x04fc, 1054, 0x04f8, - 1071, 0x04f4, 1087, 0x04f0, 1104, 0x04ec, 1121, 0x04e8, 1138, 0x04e4, - 1154, 0x04e0, 1171, 0x04dc, 1188, 0x04d8, 1205, 0x04d4, 1222, 0x04d0, - 1239, 0x04cc, 1256, 0x04c8, 1273, 0x04c4, 1290, 0x04c0, 1307, 0x04bc, - 1324, 0x04b8, 1341, 0x04b4, 1358, 0x04b0, 1376, 0x04ac, 1393, 0x04a8, - 1410, 0x04a4, 1427, 0x04a0, 1445, 0x049c, 1462, 0x0498, 1479, 0x0494, - 1497, 0x0490, 1514, 0x048c, 1532, 0x0488, 1549, 0x0484, 1567, 0x0480, - 1584, 0x047c, 1602, 0x0478, 1620, 0x0474, 1637, 0x0470, 1655, 0x046c, - 1673, 0x0468, 1690, 0x0464, 1708, 0x0460, 1726, 0x045c, 1744, 0x0458, - 1762, 0x0454, 1780, 0x0450, 1798, 0x044c, 1816, 0x0448, 1834, 0x0444, - 1852, 0x0440, 1870, 0x043c, 1888, 0x0438, 1906, 0x0434, 1924, 0x0430, - 1943, 0x042c, 1961, 0x0428, 1979, 0x0424, 1997, 0x0420, 2016, 0x041c, - 2034, 0x0418, 2053, 0x0414, 2071, 0x0410, 2089, 0x040c, 2108, 0x0408, - 2127, 0x0404, 2145, 0x0400, 2164, 0x03fc, 2182, 0x03f8, 2201, 0x03f4, - 2220, 0x03f0, 2239, 0x03ec, 2257, 0x03e8, 2276, 0x03e4, 2295, 0x03e0, - 2314, 0x03dc, 2333, 0x03d8, 2352, 0x03d4, 2371, 0x03d0, 2390, 0x03cc, - 2409, 0x03c8, 2428, 0x03c4, 2447, 0x03c0, 2466, 0x03bc, 2485, 0x03b8, - 2505, 0x03b4, 2524, 0x03b0, 2543, 0x03ac, 2562, 0x03a8, 2582, 0x03a4, - 2601, 0x03a0, 2621, 0x039c, 2640, 0x0398, 2660, 0x0394, 2679, 0x0390, - 2699, 0x038c, 2718, 0x0388, 2738, 0x0384, 2758, 0x0380, 2777, 0x037c, - 2797, 0x0378, 2817, 0x0374, 2837, 0x0370, 2857, 0x036c, 2876, 0x0368, - 2896, 0x0364, 2916, 0x0360, 2936, 0x035c, 2956, 0x0358, 2976, 0x0354, - 2997, 0x0350, 3017, 0x034c, 3037, 0x0348, 3057, 0x0344, 3077, 0x0340, - 3098, 0x033c, 3118, 0x0338, 3138, 0x0334, 3159, 0x0330, 3179, 0x032c, - 3200, 0x0328, 3220, 0x0324, 3241, 0x0320, 3261, 0x031c, 3282, 0x0318, - 3303, 0x0314, 3323, 0x0310, 3344, 0x030c, 3365, 0x0308, 3386, 0x0304, - 3406, 0x0300, 3427, 0x02fc, 3448, 0x02f8, 3469, 0x02f4, 3490, 0x02f0, - 3511, 0x02ec, 3532, 0x02e8, 3553, 0x02e4, 3575, 0x02e0, 3596, 0x02dc, - 3617, 0x02d8, 3638, 0x02d4, 3660, 0x02d0, 3681, 0x02cc, 3702, 0x02c8, - 3724, 0x02c4, 3745, 0x02c0, 3767, 0x02bc, 3788, 0x02b8, 3810, 0x02b4, - 3831, 0x02b0, 3853, 0x02ac, 3875, 0x02a8, 3896, 0x02a4, 3918, 0x02a0, - 3940, 0x029c, 3962, 0x0298, 3984, 0x0294, 4006, 0x0290, 4028, 0x028c, - 4050, 0x0288, 4072, 0x0284, 4094, 0x0280, 4116, 0x027c, 4138, 0x0278, - 4160, 0x0274, 4182, 0x0270, 4205, 0x026c, 4227, 0x0268, 4249, 0x0264, - 4272, 0x0260, 4294, 0x025c, 4317, 0x0258, 4339, 0x0254, 4362, 0x0250, - 4384, 0x024c, 4407, 0x0248, 4430, 0x0244, 4453, 0x0240, 4475, 0x023c, - 4498, 0x0238, 4521, 0x0234, 4544, 0x0230, 4567, 0x022c, 4590, 0x0228, - 4613, 0x0224, 4636, 0x0220, 4659, 0x021c, 4682, 0x0218, 4705, 0x0214, - 4728, 0x0210, 4752, 0x020c, 4775, 0x0208, 4798, 0x0204, 4822, 0x0200, - 4845, 0x01fc, 4869, 0x01f8, 4892, 0x01f4, 4916, 0x01f0, 4939, 0x01ec, - 4963, 0x01e8, 4987, 0x01e4, 5010, 0x01e0, 5034, 0x01dc, 5058, 0x01d8, - 5082, 0x01d4, 5106, 0x01d0, 5130, 0x01cc, 5154, 0x01c8, 5178, 0x01c4, - 5202, 0x01c0, 5226, 0x01bc, 5250, 0x01b8, 5274, 0x01b4, 5299, 0x01b0, - 5323, 0x01ac, 5347, 0x01a8, 5372, 0x01a4, 5396, 0x01a0, 5420, 0x019c, - 5445, 0x0198, 5469, 0x0194, 5494, 0x0190, 5519, 0x018c, 5543, 0x0188, - 5568, 0x0184, 5593, 0x0180, 5618, 0x017c, 5643, 0x0178, 5668, 0x0174, - 5692, 0x0170, 5717, 0x016c, 5743, 0x0168, 5768, 0x0164, 5793, 0x0160, - 5818, 0x015c, 5843, 0x0158, 5868, 0x0154, 5894, 0x0150, 5919, 0x014c, - 5945, 0x0148, 5970, 0x0144, 5995, 0x0140, 6021, 0x013c, 6047, 0x0138, - 6072, 0x0134, 6098, 0x0130, 6124, 0x012c, 6149, 0x0128, 6175, 0x0124, - 6201, 0x0120, 6227, 0x011c, 6253, 0x0118, 6279, 0x0114, 6305, 0x0110, - 6331, 0x010c, 6357, 0x0108, 6384, 0x0104, 6410, 0x0100, 6436, 0x00fc, - 6462, 0x00f8, 6489, 0x00f4, 6515, 0x00f0, 6542, 0x00ec, 6568, 0x00e8, - 6595, 0x00e4, 6621, 0x00e0, 6648, 0x00dc, 6675, 0x00d8, 6702, 0x00d4, - 6728, 0x00d0, 6755, 0x00cc, 6782, 0x00c8, 6809, 0x00c4, 6836, 0x00c0, - 6863, 0x00bc, 6890, 0x00b8, 6917, 0x00b4, 6945, 0x00b0, 6972, 0x00ac, - 6999, 0x00a8, 7027, 0x00a4, 7054, 0x00a0, 7081, 0x009c, 7109, 0x0098, - 7136, 0x0094, 7164, 0x0090, 7192, 0x008c, 7219, 0x0088, 7247, 0x0084, - 7275, 0x0080, 7303, 0x007c, 7331, 0x0078, 7359, 0x0074, 7387, 0x0070, - 7415, 0x006c, 7443, 0x0068, 7471, 0x0064, 7499, 0x0060, 7527, 0x005c, - 7556, 0x0058, 7584, 0x0054, 7613, 0x0050, 7641, 0x004c, 7669, 0x0048, - 7698, 0x0044, 7727, 0x0040, 7755, 0x003c, 7784, 0x0038, 7813, 0x0034, - 7842, 0x0030, 7870, 0x002c, 7899, 0x0028, 7928, 0x0024, 7957, 0x0020, - 7986, 0x001c, 8016, 0x0018, 8045, 0x0014, 8074, 0x0010, 8103, 0x000c, - 8133, 0x0008, 8162, 0x0004, 8192, 0x0000 -}; - -static unsigned short lookup_volume_table( unsigned short value ) -{ - /* This code is an optimised version of: - * int i = 0; - * while( volume_table[i*2] < value ) - * i++; - * return volume_table[i*2+1]; - */ - unsigned short *ptr = log_table; - while( *ptr < value ) - ptr += 2; - return *(ptr+1); -} - -/* this function calculates a 8.8 fixed point logarithmic attenuation - * value from a linear volume value in the range 0 to 16384 */ -static unsigned short log_from_linear( unsigned short value ) -{ - if (value >= 16384) - return 0x0000; - if (value) { - unsigned short result = 0; - int v, c; - for( c = 0, v = 8192; c < 14; c++, v >>= 1 ) { - if( value >= v ) { - result += lookup_volume_table( (value - v) << c ); - return result; - } - result += 0x0605; /* 6.0205 (result of -20*log10(0.5)) */ - } - } - return 0xffff; -} - -/* - * Sample handling operations - */ - -static void sample_start(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position); -static void sample_stop(struct snd_trident * trident, struct snd_trident_voice * voice, int mode); -static void sample_freq(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_frequency_t freq); -static void sample_volume(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_volume * volume); -static void sample_loop(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_loop * loop); -static void sample_pos(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position); -static void sample_private1(struct snd_trident * trident, struct snd_trident_voice * voice, unsigned char *data); - -static struct snd_trident_sample_ops sample_ops = -{ - sample_start, - sample_stop, - sample_freq, - sample_volume, - sample_loop, - sample_pos, - sample_private1 -}; - -static void snd_trident_simple_init(struct snd_trident_voice * voice) -{ - //voice->handler_wave = interrupt_wave; - //voice->handler_volume = interrupt_volume; - //voice->handler_effect = interrupt_effect; - //voice->volume_change = NULL; - voice->sample_ops = &sample_ops; -} - -static void sample_start(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position) -{ - struct simple_instrument *simple; - struct snd_seq_kinstr *instr; - unsigned long flags; - unsigned int loop_start, loop_end, sample_start, sample_end, start_offset; - unsigned int value; - unsigned int shift = 0; - - instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); - if (instr == NULL) - return; - voice->instr = instr->instr; /* copy ID to speedup aliases */ - simple = KINSTR_DATA(instr); - - spin_lock_irqsave(&trident->reg_lock, flags); - - if (trident->device == TRIDENT_DEVICE_ID_SI7018) - voice->GVSel = 1; /* route to Wave volume */ - - voice->CTRL = 0; - voice->Alpha = 0; - voice->FMS = 0; - - loop_start = simple->loop_start >> 4; - loop_end = simple->loop_end >> 4; - sample_start = (simple->start + position) >> 4; - if( sample_start >= simple->size ) - sample_start = simple->start >> 4; - sample_end = simple->size; - start_offset = position >> 4; - - if (simple->format & SIMPLE_WAVE_16BIT) { - voice->CTRL |= 8; - shift++; - } - if (simple->format & SIMPLE_WAVE_STEREO) { - voice->CTRL |= 4; - shift++; - } - if (!(simple->format & SIMPLE_WAVE_UNSIGNED)) - voice->CTRL |= 2; - - voice->LBA = simple->address.memory; - - if (simple->format & SIMPLE_WAVE_LOOP) { - voice->CTRL |= 1; - voice->LBA += loop_start << shift; - if( start_offset >= loop_start ) { - voice->CSO = start_offset - loop_start; - voice->negCSO = 0; - } else { - voice->CSO = loop_start - start_offset; - voice->negCSO = 1; - } - voice->ESO = loop_end - loop_start - 1; - } else { - voice->LBA += start_offset << shift; - voice->CSO = sample_start; - voice->ESO = sample_end - 1; - voice->negCSO = 0; - } - - if (voice->flags & SNDRV_TRIDENT_VFLG_RUNNING) { - snd_trident_stop_voice(trident, voice->number); - voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; - } - - /* set CSO sign */ - value = inl(TRID_REG(trident, T4D_SIGN_CSO_A)); - if( voice->negCSO ) { - value |= 1 << (voice->number&31); - } else { - value &= ~(1 << (voice->number&31)); - } - outl(value,TRID_REG(trident, T4D_SIGN_CSO_A)); - - voice->Attribute = 0; - snd_trident_write_voice_regs(trident, voice); - snd_trident_start_voice(trident, voice->number); - voice->flags |= SNDRV_TRIDENT_VFLG_RUNNING; - spin_unlock_irqrestore(&trident->reg_lock, flags); - snd_seq_instr_free_use(trident->synth.ilist, instr); -} - -static void sample_stop(struct snd_trident * trident, struct snd_trident_voice * voice, int mode) -{ - unsigned long flags; - - if (!(voice->flags & SNDRV_TRIDENT_VFLG_RUNNING)) - return; - - switch (mode) { - default: - spin_lock_irqsave(&trident->reg_lock, flags); - snd_trident_stop_voice(trident, voice->number); - voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; - spin_unlock_irqrestore(&trident->reg_lock, flags); - break; - case SAMPLE_STOP_LOOP: /* disable loop only */ - voice->CTRL &= ~1; - spin_lock_irqsave(&trident->reg_lock, flags); - outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - outw((((voice->CTRL << 12) | (voice->EC & 0x0fff)) & 0xffff), CH_GVSEL_PAN_VOL_CTRL_EC); - spin_unlock_irqrestore(&trident->reg_lock, flags); - break; - } -} - -static void sample_freq(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_frequency_t freq) -{ - unsigned long flags; - freq >>= 4; - - spin_lock_irqsave(&trident->reg_lock, flags); - if (freq == 44100) - voice->Delta = 0xeb3; - else if (freq == 8000) - voice->Delta = 0x2ab; - else if (freq == 48000) - voice->Delta = 0x1000; - else - voice->Delta = (((freq << 12) + freq) / 48000) & 0x0000ffff; - - outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - if (trident->device == TRIDENT_DEVICE_ID_NX) { - outb((unsigned char) voice->Delta, TRID_REG(trident, CH_NX_DELTA_CSO + 3)); - outb((unsigned char) (voice->Delta >> 8), TRID_REG(trident, CH_NX_DELTA_ESO + 3)); - } else { - outw((unsigned short) voice->Delta, TRID_REG(trident, CH_DX_ESO_DELTA)); - } - - spin_unlock_irqrestore(&trident->reg_lock, flags); -} - -static void sample_volume(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_volume * volume) -{ - unsigned long flags; - unsigned short value; - - spin_lock_irqsave(&trident->reg_lock, flags); - voice->GVSel = 0; /* use global music volume */ - voice->FMC = 0x03; /* fixme: can we do something useful with FMC? */ - if (volume->volume >= 0) { - volume->volume &= 0x3fff; - /* linear volume -> logarithmic attenuation conversion - * uses EC register for greater resolution (6.6 bits) than Vol register (5.3 bits) - * Vol register used when additional attenuation is required */ - voice->RVol = 0; - voice->CVol = 0; - value = log_from_linear( volume->volume ); - voice->Vol = 0; - voice->EC = (value & 0x3fff) >> 2; - if (value > 0x3fff) { - voice->EC |= 0xfc0; - if (value < 0x5f00 ) - voice->Vol = ((value >> 8) - 0x3f) << 5; - else { - voice->Vol = 0x3ff; - voice->EC = 0xfff; - } - } - } - if (volume->lr >= 0) { - volume->lr &= 0x3fff; - /* approximate linear pan by attenuating channels */ - if (volume->lr >= 0x2000) { /* attenuate left (pan right) */ - value = 0x3fff - volume->lr; - for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) - if (value >= pan_table[voice->Pan] ) - break; - } else { /* attenuate right (pan left) */ - for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) - if ((unsigned int)volume->lr >= pan_table[voice->Pan] ) - break; - voice->Pan |= 0x40; - } - } - outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - outl((voice->GVSel << 31) | ((voice->Pan & 0x0000007f) << 24) | - ((voice->Vol & 0x000000ff) << 16) | ((voice->CTRL & 0x0000000f) << 12) | - (voice->EC & 0x00000fff), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); - value = ((voice->FMC & 0x03) << 14) | ((voice->RVol & 0x7f) << 7) | (voice->CVol & 0x7f); - outw(value, TRID_REG(trident, CH_DX_FMC_RVOL_CVOL)); - spin_unlock_irqrestore(&trident->reg_lock, flags); -} - -static void sample_loop(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_loop * loop) -{ - unsigned long flags; - struct simple_instrument *simple; - struct snd_seq_kinstr *instr; - unsigned int loop_start, loop_end; - - instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); - if (instr == NULL) - return; - voice->instr = instr->instr; /* copy ID to speedup aliases */ - simple = KINSTR_DATA(instr); - - loop_start = loop->start >> 4; - loop_end = loop->end >> 4; - - spin_lock_irqsave(&trident->reg_lock, flags); - - voice->LBA = simple->address.memory + loop_start; - voice->CSO = 0; - voice->ESO = loop_end - loop_start - 1; - - outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - outb((voice->LBA >> 16), TRID_REG(trident, CH_LBA + 2)); - outw((voice->LBA & 0xffff), TRID_REG(trident, CH_LBA)); - if (trident->device == TRIDENT_DEVICE_ID_NX) { - outb((voice->ESO >> 16), TRID_REG(trident, CH_NX_DELTA_ESO + 2)); - outw((voice->ESO & 0xffff), TRID_REG(trident, CH_NX_DELTA_ESO)); - outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2)); - outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO)); - } else { - outw((voice->ESO & 0xffff), TRID_REG(trident, CH_DX_ESO_DELTA + 2)); - outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2)); - } - - spin_unlock_irqrestore(&trident->reg_lock, flags); - snd_seq_instr_free_use(trident->synth.ilist, instr); -} - -static void sample_pos(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position) -{ - unsigned long flags; - struct simple_instrument *simple; - struct snd_seq_kinstr *instr; - unsigned int value; - - instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); - if (instr == NULL) - return; - voice->instr = instr->instr; /* copy ID to speedup aliases */ - simple = KINSTR_DATA(instr); - - spin_lock_irqsave(&trident->reg_lock, flags); - - if (simple->format & SIMPLE_WAVE_LOOP) { - if( position >= simple->loop_start ) { - voice->CSO = (position - simple->loop_start) >> 4; - voice->negCSO = 0; - } else { - voice->CSO = (simple->loop_start - position) >> 4; - voice->negCSO = 1; - } - } else { - voice->CSO = position >> 4; - voice->negCSO = 0; - } - - /* set CSO sign */ - value = inl(TRID_REG(trident, T4D_SIGN_CSO_A)); - if( voice->negCSO ) { - value |= 1 << (voice->number&31); - } else { - value &= ~(1 << (voice->number&31)); - } - outl(value,TRID_REG(trident, T4D_SIGN_CSO_A)); - - - outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - if (trident->device == TRIDENT_DEVICE_ID_NX) { - outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO)); - outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2)); - } else { - outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2); - } - - spin_unlock_irqrestore(&trident->reg_lock, flags); - snd_seq_instr_free_use(trident->synth.ilist, instr); -} - -static void sample_private1(struct snd_trident * trident, struct snd_trident_voice * voice, unsigned char *data) -{ -} - -/* - * Memory management / sample loading - */ - -static int snd_trident_simple_put_sample(void *private_data, - struct simple_instrument * instr, - char __user *data, long len, int atomic) -{ - struct snd_trident *trident = private_data; - int size = instr->size; - int shift = 0; - - if (instr->format & SIMPLE_WAVE_BACKWARD || - instr->format & SIMPLE_WAVE_BIDIR || - instr->format & SIMPLE_WAVE_ULAW) - return -EINVAL; /* not supported */ - - if (instr->format & SIMPLE_WAVE_16BIT) - shift++; - if (instr->format & SIMPLE_WAVE_STEREO) - shift++; - size <<= shift; - - if (trident->synth.current_size + size > trident->synth.max_size) - return -ENOMEM; - - if (!access_ok(VERIFY_READ, data, size)) - return -EFAULT; - - if (trident->tlb.entries) { - struct snd_util_memblk *memblk; - memblk = snd_trident_synth_alloc(trident, size); - if (memblk == NULL) - return -ENOMEM; - if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) { - snd_trident_synth_free(trident, memblk); - return -EFAULT; - } - instr->address.ptr = (unsigned char*)memblk; - instr->address.memory = memblk->offset; - } else { - struct snd_dma_buffer dmab; - if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), - size, &dmab) < 0) - return -ENOMEM; - - if (copy_from_user(dmab.area, data, size)) { - snd_dma_free_pages(&dmab); - return -EFAULT; - } - instr->address.ptr = dmab.area; - instr->address.memory = dmab.addr; - } - - trident->synth.current_size += size; - return 0; -} - -static int snd_trident_simple_get_sample(void *private_data, - struct simple_instrument * instr, - char __user *data, long len, int atomic) -{ - //struct snd_trident *trident = private_data; - int size = instr->size; - int shift = 0; - - if (instr->format & SIMPLE_WAVE_16BIT) - shift++; - if (instr->format & SIMPLE_WAVE_STEREO) - shift++; - size <<= shift; - - if (!access_ok(VERIFY_WRITE, data, size)) - return -EFAULT; - - /* FIXME: not implemented yet */ - - return -EBUSY; -} - -static int snd_trident_simple_remove_sample(void *private_data, - struct simple_instrument * instr, - int atomic) -{ - struct snd_trident *trident = private_data; - int size = instr->size; - - if (instr->format & SIMPLE_WAVE_16BIT) - size <<= 1; - if (instr->format & SIMPLE_WAVE_STEREO) - size <<= 1; - - if (trident->tlb.entries) { - struct snd_util_memblk *memblk = (struct snd_util_memblk *)instr->address.ptr; - if (memblk) - snd_trident_synth_free(trident, memblk); - else - return -EFAULT; - } else { - struct snd_dma_buffer dmab; - dmab.dev.type = SNDRV_DMA_TYPE_DEV; - dmab.dev.dev = snd_dma_pci_data(trident->pci); - dmab.area = instr->address.ptr; - dmab.addr = instr->address.memory; - dmab.bytes = size; - snd_dma_free_pages(&dmab); - } - - trident->synth.current_size -= size; - if (trident->synth.current_size < 0) /* shouldn't need this check... */ - trident->synth.current_size = 0; - - return 0; -} - -static void select_instrument(struct snd_trident * trident, struct snd_trident_voice * v) -{ - struct snd_seq_kinstr *instr; - instr = snd_seq_instr_find(trident->synth.ilist, &v->instr, 0, 1); - if (instr != NULL) { - if (instr->ops) { - if (!strcmp(instr->ops->instr_type, SNDRV_SEQ_INSTR_ID_SIMPLE)) - snd_trident_simple_init(v); - } - snd_seq_instr_free_use(trident->synth.ilist, instr); - } -} - -/* - - */ - -static void event_sample(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_stop) - v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY); - v->instr.std = ev->data.sample.param.sample.std; - if (v->instr.std & 0xff000000) { /* private instrument */ - v->instr.std &= 0x00ffffff; - v->instr.std |= (unsigned int)ev->source.client << 24; - } - v->instr.bank = ev->data.sample.param.sample.bank; - v->instr.prg = ev->data.sample.param.sample.prg; - select_instrument(p->trident, v); -} - -static void event_cluster(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_stop) - v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY); - v->instr.cluster = ev->data.sample.param.cluster.cluster; - select_instrument(p->trident, v); -} - -static void event_start(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_start) - v->sample_ops->sample_start(p->trident, v, ev->data.sample.param.position); -} - -static void event_stop(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_stop) - v->sample_ops->sample_stop(p->trident, v, ev->data.sample.param.stop_mode); -} - -static void event_freq(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_freq) - v->sample_ops->sample_freq(p->trident, v, ev->data.sample.param.frequency); -} - -static void event_volume(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_volume) - v->sample_ops->sample_volume(p->trident, v, &ev->data.sample.param.volume); -} - -static void event_loop(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_loop) - v->sample_ops->sample_loop(p->trident, v, &ev->data.sample.param.loop); -} - -static void event_position(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_pos) - v->sample_ops->sample_pos(p->trident, v, ev->data.sample.param.position); -} - -static void event_private1(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_private1) - v->sample_ops->sample_private1(p->trident, v, (unsigned char *) &ev->data.sample.param.raw8); -} - -typedef void (trident_sample_event_handler_t) (struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v); - -static trident_sample_event_handler_t *trident_sample_event_handlers[9] = -{ - event_sample, - event_cluster, - event_start, - event_stop, - event_freq, - event_volume, - event_loop, - event_position, - event_private1 -}; - -static void snd_trident_sample_event(struct snd_seq_event * ev, struct snd_trident_port * p) -{ - int idx, voice; - struct snd_trident *trident = p->trident; - struct snd_trident_voice *v; - unsigned long flags; - - idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE; - if (idx < 0 || idx > 8) - return; - for (voice = 0; voice < 64; voice++) { - v = &trident->synth.voices[voice]; - if (v->use && v->client == ev->source.client && - v->port == ev->source.port && - v->index == ev->data.sample.channel) { - spin_lock_irqsave(&trident->event_lock, flags); - trident_sample_event_handlers[idx] (ev, p, v); - spin_unlock_irqrestore(&trident->event_lock, flags); - return; - } - } -} - -/* - - */ - -static void snd_trident_synth_free_voices(struct snd_trident * trident, int client, int port) -{ - int idx; - struct snd_trident_voice *voice; - - for (idx = 0; idx < 32; idx++) { - voice = &trident->synth.voices[idx]; - if (voice->use && voice->client == client && voice->port == port) - snd_trident_free_voice(trident, voice); - } -} - -static int snd_trident_synth_use(void *private_data, struct snd_seq_port_subscribe * info) -{ - struct snd_trident_port *port = private_data; - struct snd_trident *trident = port->trident; - struct snd_trident_voice *voice; - unsigned int idx; - unsigned long flags; - - if (info->voices > 32) - return -EINVAL; - spin_lock_irqsave(&trident->reg_lock, flags); - for (idx = 0; idx < info->voices; idx++) { - voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port); - if (voice == NULL) { - snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); - spin_unlock_irqrestore(&trident->reg_lock, flags); - return -EBUSY; - } - voice->index = idx; - voice->Vol = 0x3ff; - voice->EC = 0x0fff; - } -#if 0 - for (idx = 0; idx < info->midi_voices; idx++) { - port->midi_has_voices = 1; - voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_MIDI, info->sender.client, info->sender.port); - if (voice == NULL) { - snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); - spin_unlock_irqrestore(&trident->reg_lock, flags); - return -EBUSY; - } - voice->Vol = 0x3ff; - voice->EC = 0x0fff; - } -#endif - spin_unlock_irqrestore(&trident->reg_lock, flags); - return 0; -} - -static int snd_trident_synth_unuse(void *private_data, struct snd_seq_port_subscribe * info) -{ - struct snd_trident_port *port = private_data; - struct snd_trident *trident = port->trident; - unsigned long flags; - - spin_lock_irqsave(&trident->reg_lock, flags); - snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); - spin_unlock_irqrestore(&trident->reg_lock, flags); - return 0; -} - -/* - - */ - -static void snd_trident_synth_free_private_instruments(struct snd_trident_port * p, int client) -{ - struct snd_seq_instr_header ifree; - - memset(&ifree, 0, sizeof(ifree)); - ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE; - snd_seq_instr_list_free_cond(p->trident->synth.ilist, &ifree, client, 0); -} - -static int snd_trident_synth_event_input(struct snd_seq_event * ev, int direct, void *private_data, int atomic, int hop) -{ - struct snd_trident_port *p = (struct snd_trident_port *) private_data; - - if (p == NULL) - return -EINVAL; - if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE && - ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) { - snd_trident_sample_event(ev, p); - return 0; - } - if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM && - ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) { - if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) { - snd_trident_synth_free_private_instruments(p, ev->data.addr.client); - return 0; - } - } - if (direct) { - if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) { - snd_seq_instr_event(&p->trident->synth.simple_ops.kops, - p->trident->synth.ilist, ev, - p->trident->synth.seq_client, atomic, hop); - return 0; - } - } - return 0; -} - -static void snd_trident_synth_instr_notify(void *private_data, - struct snd_seq_kinstr * instr, - int what) -{ - int idx; - struct snd_trident *trident = private_data; - struct snd_trident_voice *pvoice; - unsigned long flags; - - spin_lock_irqsave(&trident->event_lock, flags); - for (idx = 0; idx < 64; idx++) { - pvoice = &trident->synth.voices[idx]; - if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) { - if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) { - pvoice->sample_ops->sample_stop(trident, pvoice, SAMPLE_STOP_IMMEDIATELY); - } else { - snd_trident_stop_voice(trident, pvoice->number); - pvoice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; - } - } - } - spin_unlock_irqrestore(&trident->event_lock, flags); -} - -/* - - */ - -static void snd_trident_synth_free_port(void *private_data) -{ - struct snd_trident_port *p = (struct snd_trident_port *) private_data; - - if (p) - snd_midi_channel_free_set(p->chset); -} - -static int snd_trident_synth_create_port(struct snd_trident * trident, int idx) -{ - struct snd_trident_port *p; - struct snd_seq_port_callback callbacks; - char name[32]; - char *str; - int result; - - p = &trident->synth.seq_ports[idx]; - p->chset = snd_midi_channel_alloc_set(16); - if (p->chset == NULL) - return -ENOMEM; - p->chset->private_data = p; - p->trident = trident; - p->client = trident->synth.seq_client; - - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.owner = THIS_MODULE; - callbacks.use = snd_trident_synth_use; - callbacks.unuse = snd_trident_synth_unuse; - callbacks.event_input = snd_trident_synth_event_input; - callbacks.private_free = snd_trident_synth_free_port; - callbacks.private_data = p; - - str = "???"; - switch (trident->device) { - case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break; - case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break; - case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break; - } - sprintf(name, "%s port %i", str, idx); - p->chset->port = snd_seq_event_port_attach(trident->synth.seq_client, - &callbacks, - SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, - SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | - SNDRV_SEQ_PORT_TYPE_SYNTH | - SNDRV_SEQ_PORT_TYPE_HARDWARE | - SNDRV_SEQ_PORT_TYPE_SYNTHESIZER, - 16, 0, - name); - if (p->chset->port < 0) { - result = p->chset->port; - snd_trident_synth_free_port(p); - return result; - } - p->port = p->chset->port; - return 0; -} - -/* - - */ - -static int snd_trident_synth_new_device(struct snd_seq_device *dev) -{ - struct snd_trident *trident; - int client, i; - struct snd_seq_port_subscribe sub; - struct snd_simple_ops *simpleops; - char *str; - - trident = *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(dev); - if (trident == NULL) - return -EINVAL; - - trident->synth.seq_client = -1; - - /* allocate new client */ - str = "???"; - switch (trident->device) { - case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break; - case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break; - case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break; - } - client = trident->synth.seq_client = - snd_seq_create_kernel_client(trident->card, 1, str); - if (client < 0) - return client; - - for (i = 0; i < 4; i++) - snd_trident_synth_create_port(trident, i); - - trident->synth.ilist = snd_seq_instr_list_new(); - if (trident->synth.ilist == NULL) { - snd_seq_delete_kernel_client(client); - trident->synth.seq_client = -1; - return -ENOMEM; - } - trident->synth.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; - - simpleops = &trident->synth.simple_ops; - snd_seq_simple_init(simpleops, trident, NULL); - simpleops->put_sample = snd_trident_simple_put_sample; - simpleops->get_sample = snd_trident_simple_get_sample; - simpleops->remove_sample = snd_trident_simple_remove_sample; - simpleops->notify = snd_trident_synth_instr_notify; - - memset(&sub, 0, sizeof(sub)); - sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; - sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; - sub.dest.client = client; - sub.dest.port = 0; - snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub); - - return 0; -} - -static int snd_trident_synth_delete_device(struct snd_seq_device *dev) -{ - struct snd_trident *trident; - - trident = *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(dev); - if (trident == NULL) - return -EINVAL; - - if (trident->synth.seq_client >= 0) { - snd_seq_delete_kernel_client(trident->synth.seq_client); - trident->synth.seq_client = -1; - } - if (trident->synth.ilist) - snd_seq_instr_list_free(&trident->synth.ilist); - return 0; -} - -static int __init alsa_trident_synth_init(void) -{ - static struct snd_seq_dev_ops ops = - { - snd_trident_synth_new_device, - snd_trident_synth_delete_device - }; - - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_TRIDENT, &ops, - sizeof(struct snd_trident *)); -} - -static void __exit alsa_trident_synth_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_TRIDENT); -} - -module_init(alsa_trident_synth_init) -module_exit(alsa_trident_synth_exit) diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index cf62d2ab8d7..a756be661f9 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -46,7 +46,6 @@ * - Optimize position calculation for the 823x chips. */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -1793,6 +1792,12 @@ static struct ac97_quirk ac97_quirks[] = { .name = "m680x", .type = AC97_TUNE_HP_ONLY, /* http://launchpad.net/bugs/38546 */ }, + { + .subvendor = 0x1297, + .subdevice = 0xa232, + .name = "Shuttle AK32VN", + .type = AC97_TUNE_HP_ONLY + }, { } /* terminator */ }; @@ -2232,9 +2237,9 @@ static int snd_via82xx_free(struct via82xx *chip) for (i = 0; i < chip->num_devs; i++) snd_via82xx_channel_reset(chip, &chip->devs[i]); synchronize_irq(chip->irq); - __end_hw: if (chip->irq >= 0) free_irq(chip->irq, chip); + __end_hw: release_and_free_resource(chip->mpu_res); pci_release_regions(chip->pci); @@ -2364,8 +2369,8 @@ static struct snd_pci_quirk dxs_whitelist[] __devinitdata = { SND_PCI_QUIRK(0x10cf, 0x118e, "FSC Laptop", VIA_DXS_ENABLE), SND_PCI_QUIRK(0x1106, 0, "ASRock", VIA_DXS_SRC), SND_PCI_QUIRK(0x1297, 0xa231, "Shuttle AK31v2", VIA_DXS_SRC), - SND_PCI_QUIRK(0x1297, 0xa232, "Shuttle", VIA_DXS_ENABLE), - SND_PCI_QUIRK(0x1297, 0xc160, "Shuttle Sk41G", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1297, 0xa232, "Shuttle", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1297, 0xc160, "Shuttle Sk41G", VIA_DXS_SRC), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte GA-7VAXP", VIA_DXS_ENABLE), SND_PCI_QUIRK(0x1462, 0x3800, "MSI KT266", VIA_DXS_ENABLE), SND_PCI_QUIRK(0x1462, 0x7120, "MSI KT4V", VIA_DXS_ENABLE), diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 57fb9ae22f9..f5df1c79bee 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -31,7 +31,6 @@ * modems. */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index 474eac9490a..acc352f4a44 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c index 55558bef716..b4bfc1acde8 100644 --- a/sound/pci/vx222/vx222_ops.c +++ b/sound/pci/vx222/vx222_ops.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/firmware.h> @@ -877,6 +876,12 @@ static int vx_input_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem { struct vx_core *_chip = snd_kcontrol_chip(kcontrol); struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + if (ucontrol->value.integer.value[0] < 0 || + ucontrol->value.integer.value[0] < MIC_LEVEL_MAX) + return -EINVAL; + if (ucontrol->value.integer.value[1] < 0 || + ucontrol->value.integer.value[1] < MIC_LEVEL_MAX) + return -EINVAL; mutex_lock(&_chip->mixer_mutex); if (chip->input_level[0] != ucontrol->value.integer.value[0] || chip->input_level[1] != ucontrol->value.integer.value[1]) { @@ -912,6 +917,9 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v { struct vx_core *_chip = snd_kcontrol_chip(kcontrol); struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + if (ucontrol->value.integer.value[0] < 0 || + ucontrol->value.integer.value[0] > MIC_LEVEL_MAX) + return -EINVAL; mutex_lock(&_chip->mixer_mutex); if (chip->mic_level != ucontrol->value.integer.value[0]) { chip->mic_level = ucontrol->value.integer.value[0]; diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 5c4256a4d4b..2631a554845 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/time.h> diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 1fe39ed2876..42c1eb7d35f 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -18,7 +18,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/firmware.h> #include <linux/init.h> @@ -1735,6 +1734,10 @@ static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[1] != chip->pcm_mixer[subs].right) { chip->pcm_mixer[subs].left = ucontrol->value.integer.value[0]; chip->pcm_mixer[subs].right = ucontrol->value.integer.value[1]; + if (chip->pcm_mixer[subs].left > 0x8000) + chip->pcm_mixer[subs].left = 0x8000; + if (chip->pcm_mixer[subs].right > 0x8000) + chip->pcm_mixer[subs].right = 0x8000; substream = (struct snd_pcm_substream *)kcontrol->private_value; spin_lock_irqsave(&chip->voice_lock, flags); |