diff options
Diffstat (limited to 'sound/pci')
52 files changed, 6387 insertions, 3946 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 748f6b7d90b..fb5ee3cc396 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -135,11 +135,11 @@ config SND_AW2 config SND_AZT3328 - tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)" - depends on EXPERIMENTAL + tristate "Aztech AZF3328 / PCI168" select SND_OPL3_LIB select SND_MPU401_UART select SND_PCM + select SND_RAWMIDI help Say Y here to include support for Aztech AZF3328 (PCI168) soundcards. diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 76d76c08339..b458d208720 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -478,45 +478,6 @@ static int snd_ali_reset_5451(struct snd_ali *codec) return 0; } -#ifdef CODEC_RESET - -static int snd_ali_reset_codec(struct snd_ali *codec) -{ - struct pci_dev *pci_dev; - unsigned char bVal; - unsigned int dwVal; - unsigned short wCount, wReg; - - pci_dev = codec->pci_m1533; - - pci_read_config_dword(pci_dev, 0x7c, &dwVal); - pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); - udelay(5000); - pci_read_config_dword(pci_dev, 0x7c, &dwVal); - pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); - udelay(5000); - - bVal = inb(ALI_REG(codec,ALI_SCTRL)); - bVal |= 0x02; - outb(ALI_REG(codec,ALI_SCTRL),bVal); - udelay(5000); - bVal = inb(ALI_REG(codec,ALI_SCTRL)); - bVal &= 0xfd; - outb(ALI_REG(codec,ALI_SCTRL),bVal); - udelay(15000); - - wCount = 200; - while (wCount--) { - wReg = snd_ali_codec_read(codec->ac97, AC97_POWERDOWN); - if ((wReg & 0x000f) == 0x000f) - return 0; - udelay(5000); - } - return -1; -} - -#endif - /* * ALI 5451 Controller */ @@ -561,22 +522,6 @@ static void snd_ali_disable_address_interrupt(struct snd_ali *codec) outl(gc, ALI_REG(codec, ALI_GC_CIR)); } -#if 0 /* not used */ -static void snd_ali_enable_voice_irq(struct snd_ali *codec, - unsigned int channel) -{ - unsigned int mask; - struct snd_ali_channel_control *pchregs = &(codec->chregs); - - snd_ali_printk("enable_voice_irq channel=%d\n",channel); - - mask = 1 << (channel & 0x1f); - pchregs->data.ainten = inl(ALI_REG(codec, pchregs->regs.ainten)); - pchregs->data.ainten |= mask; - outl(pchregs->data.ainten, ALI_REG(codec, pchregs->regs.ainten)); -} -#endif - static void snd_ali_disable_voice_irq(struct snd_ali *codec, unsigned int channel) { @@ -677,16 +622,6 @@ static void snd_ali_free_channel_pcm(struct snd_ali *codec, int channel) } } -#if 0 /* not used */ -static void snd_ali_start_voice(struct snd_ali *codec, unsigned int channel) -{ - unsigned int mask = 1 << (channel & 0x1f); - - snd_ali_printk("start_voice: channel=%d\n",channel); - outl(mask, ALI_REG(codec,codec->chregs.regs.start)); -} -#endif - static void snd_ali_stop_voice(struct snd_ali *codec, unsigned int channel) { unsigned int mask = 1 << (channel & 0x1f); diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index f290bc56178..8451a0169f3 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -1,6 +1,6 @@ /* * azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168). - * Copyright (C) 2002, 2005 - 2008 by Andreas Mohr <andi AT lisas.de> + * Copyright (C) 2002, 2005 - 2009 by Andreas Mohr <andi AT lisas.de> * * Framework borrowed from Bart Hartgers's als4000.c. * Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801), @@ -10,6 +10,13 @@ * PCI168 A/AP, sub ID 8000 * Please give me feedback in case you try my driver with one of these!! * + * Keywords: Windows XP Vista 168nt4-125.zip 168win95-125.zip PCI 168 download + * (XP/Vista do not support this card at all but every Linux distribution + * has very good support out of the box; + * just to make sure that the right people hit this and get to know that, + * despite the high level of Internet ignorance - as usual :-P - + * about very good support for this card - on Linux!) + * * GPL LICENSE * 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 @@ -71,10 +78,11 @@ * - built-in General DirectX timer having a 20 bits counter * with 1us resolution (see below!) * - I2S serial output port for external DAC + * [FIXME: 3.3V or 5V level? maximum rate is 66.2kHz right?] * - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI * - supports hardware volume control * - single chip low cost solution (128 pin QFP) - * - supports programmable Sub-vendor and Sub-system ID + * - supports programmable Sub-vendor and Sub-system ID [24C02 SEEPROM chip] * required for Microsoft's logo compliance (FIXME: where?) * At least the Trident 4D Wave DX has one bit somewhere * to enable writes to PCI subsystem VID registers, that should be it. @@ -82,6 +90,7 @@ * some custom data starting at 0x80. What kind of config settings * are located in our extended PCI space anyway?? * - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms + * [TDA1517P chip] * * Note that this driver now is actually *better* than the Windows driver, * since it additionally supports the card's 1MHz DirectX timer - just try @@ -146,10 +155,15 @@ * to read the Digital Enhanced Game Port. Not sure whether it is fixable. * * TODO + * - use PCI_VDEVICE + * - verify driver status on x86_64 + * - test multi-card driver operation + * - (ab)use 1MHz DirectX timer as kernel clocksource * - test MPU401 MIDI playback etc. * - add more power micro-management (disable various units of the card - * as long as they're unused). However this requires more I/O ports which I - * haven't figured out yet and which thus might not even exist... + * as long as they're unused, to improve audio quality and save power). + * However this requires more I/O ports which I haven't figured out yet + * and which thus might not even exist... * The standard suspend/resume functionality could probably make use of * some improvement, too... * - figure out what all unknown port bits are responsible for @@ -185,25 +199,46 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}"); #define SUPPORT_GAMEPORT 1 #endif +/* === Debug settings === + Further diagnostic functionality than the settings below + does not need to be provided, since one can easily write a bash script + to dump the card's I/O ports (those listed in lspci -v -v): + function dump() + { + local descr=$1; local addr=$2; local count=$3 + + echo "${descr}: ${count} @ ${addr}:" + dd if=/dev/port skip=$[${addr}] count=${count} bs=1 2>/dev/null| hexdump -C + } + and then use something like + "dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8", + "dump codec00 0xa800 32", "dump mixer 0xb800 64", "dump synth 0xbc00 8", + possibly within a "while true; do ... sleep 1; done" loop. + Tweaking ports could be done using + VALSTRING="`printf "%02x" $value`" + printf "\x""$VALSTRING"|dd of=/dev/port seek=$[${addr}] bs=1 2>/dev/null +*/ + #define DEBUG_MISC 0 #define DEBUG_CALLS 0 #define DEBUG_MIXER 0 -#define DEBUG_PLAY_REC 0 +#define DEBUG_CODEC 0 #define DEBUG_IO 0 #define DEBUG_TIMER 0 #define DEBUG_GAME 0 +#define DEBUG_PM 0 #define MIXER_TESTING 0 #if DEBUG_MISC -#define snd_azf3328_dbgmisc(format, args...) printk(KERN_ERR format, ##args) +#define snd_azf3328_dbgmisc(format, args...) printk(KERN_DEBUG format, ##args) #else #define snd_azf3328_dbgmisc(format, args...) #endif #if DEBUG_CALLS #define snd_azf3328_dbgcalls(format, args...) printk(format, ##args) -#define snd_azf3328_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__) -#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__) +#define snd_azf3328_dbgcallenter() printk(KERN_DEBUG "--> %s\n", __func__) +#define snd_azf3328_dbgcallleave() printk(KERN_DEBUG "<-- %s\n", __func__) #else #define snd_azf3328_dbgcalls(format, args...) #define snd_azf3328_dbgcallenter() @@ -216,10 +251,10 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}"); #define snd_azf3328_dbgmixer(format, args...) #endif -#if DEBUG_PLAY_REC -#define snd_azf3328_dbgplay(format, args...) printk(KERN_DEBUG format, ##args) +#if DEBUG_CODEC +#define snd_azf3328_dbgcodec(format, args...) printk(KERN_DEBUG format, ##args) #else -#define snd_azf3328_dbgplay(format, args...) +#define snd_azf3328_dbgcodec(format, args...) #endif #if DEBUG_MISC @@ -234,6 +269,12 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}"); #define snd_azf3328_dbggame(format, args...) #endif +#if DEBUG_PM +#define snd_azf3328_dbgpm(format, args...) printk(KERN_DEBUG format, ##args) +#else +#define snd_azf3328_dbgpm(format, args...) +#endif + static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard."); @@ -250,22 +291,23 @@ static int seqtimer_scaling = 128; module_param(seqtimer_scaling, int, 0444); MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128."); -struct snd_azf3328_audio_stream { +struct snd_azf3328_codec_data { + unsigned long io_base; struct snd_pcm_substream *substream; - int enabled; - int running; - unsigned long portbase; + bool running; + const char *name; }; -enum snd_azf3328_stream_index { - AZF_PLAYBACK = 0, - AZF_CAPTURE = 1, +enum snd_azf3328_codec_type { + AZF_CODEC_PLAYBACK = 0, + AZF_CODEC_CAPTURE = 1, + AZF_CODEC_I2S_OUT = 2, }; struct snd_azf3328 { /* often-used fields towards beginning, then grouped */ - unsigned long codec_io; /* usually 0xb000, size 128 */ + unsigned long ctrl_io; /* usually 0xb000, size 128 */ unsigned long game_io; /* usually 0xb400, size 8 */ unsigned long mpu_io; /* usually 0xb800, size 4 */ unsigned long opl3_io; /* usually 0xbc00, size 8 */ @@ -275,15 +317,17 @@ struct snd_azf3328 { struct snd_timer *timer; - struct snd_pcm *pcm; - struct snd_azf3328_audio_stream audio_stream[2]; + struct snd_pcm *pcm[3]; + + /* playback, recording and I2S out codecs */ + struct snd_azf3328_codec_data codecs[3]; struct snd_card *card; struct snd_rawmidi *rmidi; #ifdef SUPPORT_GAMEPORT struct gameport *gameport; - int axes[4]; + u16 axes[4]; #endif struct pci_dev *pci; @@ -293,16 +337,16 @@ struct snd_azf3328 { * If we need to add more registers here, then we might try to fold this * into some transparent combined shadow register handling with * CONFIG_PM register storage below, but that's slightly difficult. */ - u16 shadow_reg_codec_6AH; + u16 shadow_reg_ctrl_6AH; #ifdef CONFIG_PM /* register value containers for power management - * Note: not always full I/O range preserved (just like Win driver!) */ - u16 saved_regs_codec[AZF_IO_SIZE_CODEC_PM / 2]; - u16 saved_regs_game [AZF_IO_SIZE_GAME_PM / 2]; - u16 saved_regs_mpu [AZF_IO_SIZE_MPU_PM / 2]; - u16 saved_regs_opl3 [AZF_IO_SIZE_OPL3_PM / 2]; - u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2]; + * Note: not always full I/O range preserved (similar to Win driver!) */ + u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4]; + u32 saved_regs_game[AZF_ALIGN(AZF_IO_SIZE_GAME_PM) / 4]; + u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4]; + u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4]; + u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4]; #endif }; @@ -316,7 +360,7 @@ MODULE_DEVICE_TABLE(pci, snd_azf3328_ids); static int -snd_azf3328_io_reg_setb(unsigned reg, u8 mask, int do_set) +snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set) { u8 prev = inb(reg), new; @@ -331,39 +375,72 @@ snd_azf3328_io_reg_setb(unsigned reg, u8 mask, int do_set) } static inline void -snd_azf3328_codec_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value) +snd_azf3328_codec_outb(const struct snd_azf3328_codec_data *codec, + unsigned reg, + u8 value +) { - outb(value, chip->codec_io + reg); + outb(value, codec->io_base + reg); } static inline u8 -snd_azf3328_codec_inb(const struct snd_azf3328 *chip, unsigned reg) +snd_azf3328_codec_inb(const struct snd_azf3328_codec_data *codec, unsigned reg) { - return inb(chip->codec_io + reg); + return inb(codec->io_base + reg); } static inline void -snd_azf3328_codec_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value) +snd_azf3328_codec_outw(const struct snd_azf3328_codec_data *codec, + unsigned reg, + u16 value +) { - outw(value, chip->codec_io + reg); + outw(value, codec->io_base + reg); } static inline u16 -snd_azf3328_codec_inw(const struct snd_azf3328 *chip, unsigned reg) +snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg) { - return inw(chip->codec_io + reg); + return inw(codec->io_base + reg); } static inline void -snd_azf3328_codec_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value) +snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec, + unsigned reg, + u32 value +) { - outl(value, chip->codec_io + reg); + outl(value, codec->io_base + reg); } static inline u32 -snd_azf3328_codec_inl(const struct snd_azf3328 *chip, unsigned reg) +snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg) +{ + return inl(codec->io_base + reg); +} + +static inline void +snd_azf3328_ctrl_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value) +{ + outb(value, chip->ctrl_io + reg); +} + +static inline u8 +snd_azf3328_ctrl_inb(const struct snd_azf3328 *chip, unsigned reg) +{ + return inb(chip->ctrl_io + reg); +} + +static inline void +snd_azf3328_ctrl_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value) +{ + outw(value, chip->ctrl_io + reg); +} + +static inline void +snd_azf3328_ctrl_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value) { - return inl(chip->codec_io + reg); + outl(value, chip->ctrl_io + reg); } static inline void @@ -404,13 +481,13 @@ snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg) #define AZF_MUTE_BIT 0x80 -static int +static bool snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip, - unsigned reg, int do_mute + unsigned reg, bool do_mute ) { unsigned long portbase = chip->mixer_io + reg + 1; - int updated; + bool updated; /* the mute bit is on the *second* (i.e. right) register of a * left/right channel setting */ @@ -569,7 +646,7 @@ snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol, { struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol); struct azf3328_mixer_reg reg; - unsigned int oreg, val; + u16 oreg, val; snd_azf3328_dbgcallenter(); snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); @@ -600,7 +677,7 @@ snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol, { struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol); struct azf3328_mixer_reg reg; - unsigned int oreg, nreg, val; + u16 oreg, nreg, val; snd_azf3328_dbgcallenter(); snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); @@ -709,7 +786,7 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol, { struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol); struct azf3328_mixer_reg reg; - unsigned int oreg, nreg, val; + u16 oreg, nreg, val; snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); oreg = snd_azf3328_mixer_inw(chip, reg.reg); @@ -867,14 +944,15 @@ snd_azf3328_hw_free(struct snd_pcm_substream *substream) static void snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, - unsigned reg, + enum snd_azf3328_codec_type codec_type, enum azf_freq_t bitrate, unsigned int format_width, unsigned int channels ) { - u16 val = 0xff00; unsigned long flags; + const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type]; + u16 val = 0xff00; snd_azf3328_dbgcallenter(); switch (bitrate) { @@ -917,7 +995,7 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, spin_lock_irqsave(&chip->reg_lock, flags); /* set bitrate/format */ - snd_azf3328_codec_outw(chip, reg, val); + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val); /* changing the bitrate/format settings switches off the * audio output with an annoying click in case of 8/16bit format change @@ -926,11 +1004,11 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, * (FIXME: yes, it works, but what exactly am I doing here?? :) * FIXME: does this have some side effects for full-duplex * or other dramatic side effects? */ - if (reg == IDX_IO_PLAY_SOUNDFORMAT) /* only do it for playback */ - snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, - snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | - DMA_PLAY_SOMETHING1 | - DMA_PLAY_SOMETHING2 | + if (codec_type == AZF_CODEC_PLAYBACK) /* only do it for playback */ + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, + snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) | + DMA_RUN_SOMETHING1 | + DMA_RUN_SOMETHING2 | SOMETHING_ALMOST_ALWAYS_SET | DMA_EPILOGUE_SOMETHING | DMA_SOMETHING_ELSE @@ -942,112 +1020,134 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, static inline void snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip, - unsigned reg + enum snd_azf3328_codec_type codec_type ) { /* choose lowest frequency for low power consumption. * While this will cause louder noise due to rather coarse frequency, * it should never matter since output should always * get disabled properly when idle anyway. */ - snd_azf3328_codec_setfmt(chip, reg, AZF_FREQ_4000, 8, 1); + snd_azf3328_codec_setfmt(chip, codec_type, AZF_FREQ_4000, 8, 1); } static void -snd_azf3328_codec_reg_6AH_update(struct snd_azf3328 *chip, +snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip, unsigned bitmask, - int enable + bool enable ) { - if (enable) - chip->shadow_reg_codec_6AH &= ~bitmask; + bool do_mask = !enable; + if (do_mask) + chip->shadow_reg_ctrl_6AH |= bitmask; else - chip->shadow_reg_codec_6AH |= bitmask; - snd_azf3328_dbgplay("6AH_update mask 0x%04x enable %d: val 0x%04x\n", - bitmask, enable, chip->shadow_reg_codec_6AH); - snd_azf3328_codec_outw(chip, IDX_IO_6AH, chip->shadow_reg_codec_6AH); + chip->shadow_reg_ctrl_6AH &= ~bitmask; + snd_azf3328_dbgcodec("6AH_update mask 0x%04x do_mask %d: val 0x%04x\n", + bitmask, do_mask, chip->shadow_reg_ctrl_6AH); + snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH); } static inline void -snd_azf3328_codec_enable(struct snd_azf3328 *chip, int enable) +snd_azf3328_ctrl_enable_codecs(struct snd_azf3328 *chip, bool enable) { - snd_azf3328_dbgplay("codec_enable %d\n", enable); + snd_azf3328_dbgcodec("codec_enable %d\n", enable); /* no idea what exactly is being done here, but I strongly assume it's * PM related */ - snd_azf3328_codec_reg_6AH_update( + snd_azf3328_ctrl_reg_6AH_update( chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable ); } static void -snd_azf3328_codec_activity(struct snd_azf3328 *chip, - enum snd_azf3328_stream_index stream_type, - int enable +snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip, + enum snd_azf3328_codec_type codec_type, + bool enable ) { - int need_change = (chip->audio_stream[stream_type].running != enable); + struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type]; + bool need_change = (codec->running != enable); - snd_azf3328_dbgplay( - "codec_activity: type %d, enable %d, need_change %d\n", - stream_type, enable, need_change + snd_azf3328_dbgcodec( + "codec_activity: %s codec, enable %d, need_change %d\n", + codec->name, enable, need_change ); if (need_change) { - enum snd_azf3328_stream_index other = - (stream_type == AZF_PLAYBACK) ? - AZF_CAPTURE : AZF_PLAYBACK; - /* small check to prevent shutting down the other party - * in case it's active */ - if ((enable) || !(chip->audio_stream[other].running)) - snd_azf3328_codec_enable(chip, enable); + static const struct { + enum snd_azf3328_codec_type other1; + enum snd_azf3328_codec_type other2; + } peer_codecs[3] = + { { AZF_CODEC_CAPTURE, AZF_CODEC_I2S_OUT }, + { AZF_CODEC_PLAYBACK, AZF_CODEC_I2S_OUT }, + { AZF_CODEC_PLAYBACK, AZF_CODEC_CAPTURE } }; + bool call_function; + + if (enable) + /* if enable codec, call enable_codecs func + to enable codec supply... */ + call_function = 1; + else { + /* ...otherwise call enable_codecs func + (which globally shuts down operation of codecs) + only in case the other codecs are currently + not active either! */ + call_function = + ((!chip->codecs[peer_codecs[codec_type].other1] + .running) + && (!chip->codecs[peer_codecs[codec_type].other2] + .running)); + } + if (call_function) + snd_azf3328_ctrl_enable_codecs(chip, enable); /* ...and adjust clock, too * (reduce noise and power consumption) */ if (!enable) snd_azf3328_codec_setfmt_lowpower( chip, - chip->audio_stream[stream_type].portbase - + IDX_IO_PLAY_SOUNDFORMAT + codec_type ); + codec->running = enable; } - chip->audio_stream[stream_type].running = enable; } static void -snd_azf3328_setdmaa(struct snd_azf3328 *chip, - long unsigned int addr, - unsigned int count, - unsigned int size, - enum snd_azf3328_stream_index stream_type +snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip, + enum snd_azf3328_codec_type codec_type, + unsigned long addr, + unsigned int count, + unsigned int size ) { + const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type]; snd_azf3328_dbgcallenter(); - if (!chip->audio_stream[stream_type].running) { - /* AZF3328 uses a two buffer pointer DMA playback approach */ + if (!codec->running) { + /* AZF3328 uses a two buffer pointer DMA transfer approach */ - unsigned long flags, portbase, addr_area2; + unsigned long flags, addr_area2; /* width 32bit (prevent overflow): */ - unsigned long count_areas, count_tmp; + u32 count_areas, lengths; - portbase = chip->audio_stream[stream_type].portbase; count_areas = size/2; addr_area2 = addr+count_areas; count_areas--; /* max. index */ - snd_azf3328_dbgplay("set DMA: buf1 %08lx[%lu], buf2 %08lx[%lu]\n", addr, count_areas, addr_area2, count_areas); + snd_azf3328_dbgcodec("setdma: buffers %08lx[%u] / %08lx[%u]\n", + addr, count_areas, addr_area2, count_areas); /* build combined I/O buffer length word */ - count_tmp = count_areas; - count_areas |= (count_tmp << 16); + lengths = (count_areas << 16) | (count_areas); spin_lock_irqsave(&chip->reg_lock, flags); - outl(addr, portbase + IDX_IO_PLAY_DMA_START_1); - outl(addr_area2, portbase + IDX_IO_PLAY_DMA_START_2); - outl(count_areas, portbase + IDX_IO_PLAY_DMA_LEN_1); + snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_1, addr); + snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_2, + addr_area2); + snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_LENGTHS, + lengths); spin_unlock_irqrestore(&chip->reg_lock, flags); } snd_azf3328_dbgcallleave(); } static int -snd_azf3328_playback_prepare(struct snd_pcm_substream *substream) +snd_azf3328_codec_prepare(struct snd_pcm_substream *substream) { #if 0 struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); @@ -1058,157 +1158,161 @@ snd_azf3328_playback_prepare(struct snd_pcm_substream *substream) snd_azf3328_dbgcallenter(); #if 0 - snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT, + snd_azf3328_codec_setfmt(chip, AZF_CODEC_..., runtime->rate, snd_pcm_format_width(runtime->format), runtime->channels); - snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, AZF_PLAYBACK); + snd_azf3328_codec_setdmaa(chip, AZF_CODEC_..., + runtime->dma_addr, count, size); #endif snd_azf3328_dbgcallleave(); return 0; } static int -snd_azf3328_capture_prepare(struct snd_pcm_substream *substream) -{ -#if 0 - struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int size = snd_pcm_lib_buffer_bytes(substream); - unsigned int count = snd_pcm_lib_period_bytes(substream); -#endif - - snd_azf3328_dbgcallenter(); -#if 0 - snd_azf3328_codec_setfmt(chip, IDX_IO_REC_SOUNDFORMAT, - runtime->rate, - snd_pcm_format_width(runtime->format), - runtime->channels); - snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, AZF_CAPTURE); -#endif - snd_azf3328_dbgcallleave(); - return 0; -} - -static int -snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd) +snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type, + struct snd_pcm_substream *substream, int cmd) { struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); + const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type]; struct snd_pcm_runtime *runtime = substream->runtime; int result = 0; - unsigned int status1; - int previously_muted; + u16 flags1; + bool previously_muted = 0; + bool is_playback_codec = (AZF_CODEC_PLAYBACK == codec_type); - snd_azf3328_dbgcalls("snd_azf3328_playback_trigger cmd %d\n", cmd); + snd_azf3328_dbgcalls("snd_azf3328_codec_trigger cmd %d\n", cmd); switch (cmd) { case SNDRV_PCM_TRIGGER_START: - snd_azf3328_dbgplay("START PLAYBACK\n"); - - /* mute WaveOut (avoid clicking during setup) */ - previously_muted = - snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1); + snd_azf3328_dbgcodec("START %s\n", codec->name); + + if (is_playback_codec) { + /* mute WaveOut (avoid clicking during setup) */ + previously_muted = + snd_azf3328_mixer_set_mute( + chip, IDX_MIXER_WAVEOUT, 1 + ); + } - snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT, + snd_azf3328_codec_setfmt(chip, codec_type, runtime->rate, snd_pcm_format_width(runtime->format), runtime->channels); spin_lock(&chip->reg_lock); /* first, remember current value: */ - status1 = snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS); + flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS); - /* stop playback */ - status1 &= ~DMA_RESUME; - snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1); + /* stop transfer */ + flags1 &= ~DMA_RESUME; + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1); /* FIXME: clear interrupts or what??? */ - snd_azf3328_codec_outw(chip, IDX_IO_PLAY_IRQTYPE, 0xffff); + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff); spin_unlock(&chip->reg_lock); - snd_azf3328_setdmaa(chip, runtime->dma_addr, + snd_azf3328_codec_setdmaa(chip, codec_type, runtime->dma_addr, snd_pcm_lib_period_bytes(substream), - snd_pcm_lib_buffer_bytes(substream), - AZF_PLAYBACK); + snd_pcm_lib_buffer_bytes(substream) + ); spin_lock(&chip->reg_lock); #ifdef WIN9X /* FIXME: enable playback/recording??? */ - status1 |= DMA_PLAY_SOMETHING1 | DMA_PLAY_SOMETHING2; - snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1); + flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2; + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1); - /* start playback again */ + /* start transfer again */ /* FIXME: what is this value (0x0010)??? */ - status1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING; - snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1); + flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING; + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1); #else /* NT4 */ - snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, 0x0000); - snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, - DMA_PLAY_SOMETHING1); - snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, - DMA_PLAY_SOMETHING1 | - DMA_PLAY_SOMETHING2); - snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, + DMA_RUN_SOMETHING1); + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, + DMA_RUN_SOMETHING1 | + DMA_RUN_SOMETHING2); + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, DMA_RESUME | SOMETHING_ALMOST_ALWAYS_SET | DMA_EPILOGUE_SOMETHING | DMA_SOMETHING_ELSE); #endif spin_unlock(&chip->reg_lock); - snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 1); - - /* now unmute WaveOut */ - if (!previously_muted) - snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0); + snd_azf3328_ctrl_codec_activity(chip, codec_type, 1); + + if (is_playback_codec) { + /* now unmute WaveOut */ + if (!previously_muted) + snd_azf3328_mixer_set_mute( + chip, IDX_MIXER_WAVEOUT, 0 + ); + } - snd_azf3328_dbgplay("STARTED PLAYBACK\n"); + snd_azf3328_dbgcodec("STARTED %s\n", codec->name); break; case SNDRV_PCM_TRIGGER_RESUME: - snd_azf3328_dbgplay("RESUME PLAYBACK\n"); - /* resume playback if we were active */ + snd_azf3328_dbgcodec("RESUME %s\n", codec->name); + /* resume codec if we were active */ spin_lock(&chip->reg_lock); - if (chip->audio_stream[AZF_PLAYBACK].running) - snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, - snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | DMA_RESUME); + if (codec->running) + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, + snd_azf3328_codec_inw( + codec, IDX_IO_CODEC_DMA_FLAGS + ) | DMA_RESUME + ); spin_unlock(&chip->reg_lock); break; case SNDRV_PCM_TRIGGER_STOP: - snd_azf3328_dbgplay("STOP PLAYBACK\n"); - - /* mute WaveOut (avoid clicking during setup) */ - previously_muted = - snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1); + snd_azf3328_dbgcodec("STOP %s\n", codec->name); + + if (is_playback_codec) { + /* mute WaveOut (avoid clicking during setup) */ + previously_muted = + snd_azf3328_mixer_set_mute( + chip, IDX_MIXER_WAVEOUT, 1 + ); + } spin_lock(&chip->reg_lock); /* first, remember current value: */ - status1 = snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS); + flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS); - /* stop playback */ - status1 &= ~DMA_RESUME; - snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1); + /* stop transfer */ + flags1 &= ~DMA_RESUME; + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1); /* hmm, is this really required? we're resetting the same bit * immediately thereafter... */ - status1 |= DMA_PLAY_SOMETHING1; - snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1); + flags1 |= DMA_RUN_SOMETHING1; + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1); - status1 &= ~DMA_PLAY_SOMETHING1; - snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1); + flags1 &= ~DMA_RUN_SOMETHING1; + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1); spin_unlock(&chip->reg_lock); - snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0); - - /* now unmute WaveOut */ - if (!previously_muted) - snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0); + snd_azf3328_ctrl_codec_activity(chip, codec_type, 0); + + if (is_playback_codec) { + /* now unmute WaveOut */ + if (!previously_muted) + snd_azf3328_mixer_set_mute( + chip, IDX_MIXER_WAVEOUT, 0 + ); + } - snd_azf3328_dbgplay("STOPPED PLAYBACK\n"); + snd_azf3328_dbgcodec("STOPPED %s\n", codec->name); break; case SNDRV_PCM_TRIGGER_SUSPEND: - snd_azf3328_dbgplay("SUSPEND PLAYBACK\n"); - /* make sure playback is stopped */ - snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, - snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) & ~DMA_RESUME); + snd_azf3328_dbgcodec("SUSPEND %s\n", codec->name); + /* make sure codec is stopped */ + snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, + snd_azf3328_codec_inw( + codec, IDX_IO_CODEC_DMA_FLAGS + ) & ~DMA_RESUME + ); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n"); @@ -1217,7 +1321,7 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd) snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n"); break; default: - printk(KERN_ERR "FIXME: unknown trigger mode!\n"); + snd_printk(KERN_ERR "FIXME: unknown trigger mode!\n"); return -EINVAL; } @@ -1225,172 +1329,74 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd) return result; } -/* this is just analogous to playback; I'm not quite sure whether recording - * should actually be triggered like that */ static int -snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd) +snd_azf3328_codec_playback_trigger(struct snd_pcm_substream *substream, int cmd) { - struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - int result = 0; - unsigned int status1; - - snd_azf3328_dbgcalls("snd_azf3328_capture_trigger cmd %d\n", cmd); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - - snd_azf3328_dbgplay("START CAPTURE\n"); - - snd_azf3328_codec_setfmt(chip, IDX_IO_REC_SOUNDFORMAT, - runtime->rate, - snd_pcm_format_width(runtime->format), - runtime->channels); - - spin_lock(&chip->reg_lock); - /* first, remember current value: */ - status1 = snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS); - - /* stop recording */ - status1 &= ~DMA_RESUME; - snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1); - - /* FIXME: clear interrupts or what??? */ - snd_azf3328_codec_outw(chip, IDX_IO_REC_IRQTYPE, 0xffff); - spin_unlock(&chip->reg_lock); - - snd_azf3328_setdmaa(chip, runtime->dma_addr, - snd_pcm_lib_period_bytes(substream), - snd_pcm_lib_buffer_bytes(substream), - AZF_CAPTURE); - - spin_lock(&chip->reg_lock); -#ifdef WIN9X - /* FIXME: enable playback/recording??? */ - status1 |= DMA_PLAY_SOMETHING1 | DMA_PLAY_SOMETHING2; - snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1); - - /* start capture again */ - /* FIXME: what is this value (0x0010)??? */ - status1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING; - snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1); -#else - snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, - 0x0000); - snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, - DMA_PLAY_SOMETHING1); - snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, - DMA_PLAY_SOMETHING1 | - DMA_PLAY_SOMETHING2); - snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, - DMA_RESUME | - SOMETHING_ALMOST_ALWAYS_SET | - DMA_EPILOGUE_SOMETHING | - DMA_SOMETHING_ELSE); -#endif - spin_unlock(&chip->reg_lock); - snd_azf3328_codec_activity(chip, AZF_CAPTURE, 1); - - snd_azf3328_dbgplay("STARTED CAPTURE\n"); - break; - case SNDRV_PCM_TRIGGER_RESUME: - snd_azf3328_dbgplay("RESUME CAPTURE\n"); - /* resume recording if we were active */ - spin_lock(&chip->reg_lock); - if (chip->audio_stream[AZF_CAPTURE].running) - snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, - snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) | DMA_RESUME); - spin_unlock(&chip->reg_lock); - break; - case SNDRV_PCM_TRIGGER_STOP: - snd_azf3328_dbgplay("STOP CAPTURE\n"); - - spin_lock(&chip->reg_lock); - /* first, remember current value: */ - status1 = snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS); - - /* stop recording */ - status1 &= ~DMA_RESUME; - snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1); - - status1 |= DMA_PLAY_SOMETHING1; - snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1); - - status1 &= ~DMA_PLAY_SOMETHING1; - snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1); - spin_unlock(&chip->reg_lock); - snd_azf3328_codec_activity(chip, AZF_CAPTURE, 0); + return snd_azf3328_codec_trigger(AZF_CODEC_PLAYBACK, substream, cmd); +} - snd_azf3328_dbgplay("STOPPED CAPTURE\n"); - break; - case SNDRV_PCM_TRIGGER_SUSPEND: - snd_azf3328_dbgplay("SUSPEND CAPTURE\n"); - /* make sure recording is stopped */ - snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, - snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) & ~DMA_RESUME); - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n"); - break; - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n"); - break; - default: - printk(KERN_ERR "FIXME: unknown trigger mode!\n"); - return -EINVAL; - } +static int +snd_azf3328_codec_capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + return snd_azf3328_codec_trigger(AZF_CODEC_CAPTURE, substream, cmd); +} - snd_azf3328_dbgcallleave(); - return result; +static int +snd_azf3328_codec_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd) +{ + return snd_azf3328_codec_trigger(AZF_CODEC_I2S_OUT, substream, cmd); } static snd_pcm_uframes_t -snd_azf3328_playback_pointer(struct snd_pcm_substream *substream) +snd_azf3328_codec_pointer(struct snd_pcm_substream *substream, + enum snd_azf3328_codec_type codec_type +) { - struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); + const struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); + const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type]; unsigned long bufptr, result; snd_pcm_uframes_t frmres; #ifdef QUERY_HARDWARE - bufptr = snd_azf3328_codec_inl(chip, IDX_IO_PLAY_DMA_START_1); + bufptr = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1); #else bufptr = substream->runtime->dma_addr; #endif - result = snd_azf3328_codec_inl(chip, IDX_IO_PLAY_DMA_CURRPOS); + result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS); /* calculate offset */ result -= bufptr; frmres = bytes_to_frames( substream->runtime, result); - snd_azf3328_dbgplay("PLAY @ 0x%8lx, frames %8ld\n", result, frmres); + snd_azf3328_dbgcodec("%s @ 0x%8lx, frames %8ld\n", + codec->name, result, frmres); return frmres; } static snd_pcm_uframes_t -snd_azf3328_capture_pointer(struct snd_pcm_substream *substream) +snd_azf3328_codec_playback_pointer(struct snd_pcm_substream *substream) { - struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); - unsigned long bufptr, result; - snd_pcm_uframes_t frmres; + return snd_azf3328_codec_pointer(substream, AZF_CODEC_PLAYBACK); +} -#ifdef QUERY_HARDWARE - bufptr = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_START_1); -#else - bufptr = substream->runtime->dma_addr; -#endif - result = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_CURRPOS); +static snd_pcm_uframes_t +snd_azf3328_codec_capture_pointer(struct snd_pcm_substream *substream) +{ + return snd_azf3328_codec_pointer(substream, AZF_CODEC_CAPTURE); +} - /* calculate offset */ - result -= bufptr; - frmres = bytes_to_frames( substream->runtime, result); - snd_azf3328_dbgplay("REC @ 0x%8lx, frames %8ld\n", result, frmres); - return frmres; +static snd_pcm_uframes_t +snd_azf3328_codec_i2s_out_pointer(struct snd_pcm_substream *substream) +{ + return snd_azf3328_codec_pointer(substream, AZF_CODEC_I2S_OUT); } /******************************************************************/ #ifdef SUPPORT_GAMEPORT static inline void -snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip, int enable) +snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip, + bool enable +) { snd_azf3328_io_reg_setb( chip->game_io+IDX_GAME_HWCONFIG, @@ -1400,7 +1406,9 @@ snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip, int enable) } static inline void -snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip, int enable) +snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip, + bool enable +) { snd_azf3328_io_reg_setb( chip->game_io+IDX_GAME_HWCONFIG, @@ -1409,10 +1417,27 @@ snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip, int enable) ); } +static void +snd_azf3328_gameport_set_counter_frequency(struct snd_azf3328 *chip, + unsigned int freq_cfg +) +{ + snd_azf3328_io_reg_setb( + chip->game_io+IDX_GAME_HWCONFIG, + 0x02, + (freq_cfg & 1) != 0 + ); + snd_azf3328_io_reg_setb( + chip->game_io+IDX_GAME_HWCONFIG, + 0x04, + (freq_cfg & 2) != 0 + ); +} + static inline void -snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, int enable) +snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, bool enable) { - snd_azf3328_codec_reg_6AH_update( + snd_azf3328_ctrl_reg_6AH_update( chip, IO_6A_SOMETHING2_GAMEPORT, enable ); } @@ -1447,6 +1472,8 @@ snd_azf3328_gameport_open(struct gameport *gameport, int mode) break; } + snd_azf3328_gameport_set_counter_frequency(chip, + GAME_HWCFG_ADC_COUNTER_FREQ_STD); snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0)); return res; @@ -1458,6 +1485,8 @@ snd_azf3328_gameport_close(struct gameport *gameport) struct snd_azf3328 *chip = gameport_get_port_data(gameport); snd_azf3328_dbggame("gameport_close\n"); + snd_azf3328_gameport_set_counter_frequency(chip, + GAME_HWCFG_ADC_COUNTER_FREQ_1_200); snd_azf3328_gameport_axis_circuit_enable(chip, 0); } @@ -1491,7 +1520,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport, val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG); if (val & GAME_AXES_SAMPLING_READY) { - for (i = 0; i < 4; ++i) { + for (i = 0; i < ARRAY_SIZE(chip->axes); ++i) { /* configure the axis to read */ val = (i << 4) | 0x0f; snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val); @@ -1514,7 +1543,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport, snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff); spin_unlock_irqrestore(&chip->reg_lock, flags); - for (i = 0; i < 4; i++) { + for (i = 0; i < ARRAY_SIZE(chip->axes); i++) { axes[i] = chip->axes[i]; if (axes[i] == 0xffff) axes[i] = -1; @@ -1552,6 +1581,8 @@ snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) /* DISABLE legacy address: we don't need it! */ snd_azf3328_gameport_legacy_address_enable(chip, 0); + snd_azf3328_gameport_set_counter_frequency(chip, + GAME_HWCFG_ADC_COUNTER_FREQ_1_200); snd_azf3328_gameport_axis_circuit_enable(chip, 0); gameport_register_port(chip->gameport); @@ -1585,40 +1616,77 @@ snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip) static inline void snd_azf3328_irq_log_unknown_type(u8 which) { - snd_azf3328_dbgplay( + snd_azf3328_dbgcodec( "azt3328: unknown IRQ type (%x) occurred, please report!\n", which ); } +static inline void +snd_azf3328_codec_interrupt(struct snd_azf3328 *chip, u8 status) +{ + u8 which; + enum snd_azf3328_codec_type codec_type; + const struct snd_azf3328_codec_data *codec; + + for (codec_type = AZF_CODEC_PLAYBACK; + codec_type <= AZF_CODEC_I2S_OUT; + ++codec_type) { + + /* skip codec if there's no interrupt for it */ + if (!(status & (1 << codec_type))) + continue; + + codec = &chip->codecs[codec_type]; + + spin_lock(&chip->reg_lock); + which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE); + /* ack all IRQ types immediately */ + snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which); + spin_unlock(&chip->reg_lock); + + if ((chip->pcm[codec_type]) && (codec->substream)) { + snd_pcm_period_elapsed(codec->substream); + snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n", + codec->name, + which, + snd_azf3328_codec_inl( + codec, IDX_IO_CODEC_DMA_CURRPOS + ) + ); + } else + printk(KERN_WARNING "azt3328: irq handler problem!\n"); + if (which & IRQ_SOMETHING) + snd_azf3328_irq_log_unknown_type(which); + } +} + static irqreturn_t snd_azf3328_interrupt(int irq, void *dev_id) { struct snd_azf3328 *chip = dev_id; - u8 status, which; -#if DEBUG_PLAY_REC + u8 status; +#if DEBUG_CODEC static unsigned long irq_count; #endif - status = snd_azf3328_codec_inb(chip, IDX_IO_IRQSTATUS); + status = snd_azf3328_ctrl_inb(chip, IDX_IO_IRQSTATUS); /* fast path out, to ease interrupt sharing */ if (!(status & - (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER) + (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT + |IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER) )) return IRQ_NONE; /* must be interrupt for another device */ - snd_azf3328_dbgplay( - "irq_count %ld! IDX_IO_PLAY_FLAGS %04x, " - "IDX_IO_PLAY_IRQTYPE %04x, IDX_IO_IRQSTATUS %04x\n", + snd_azf3328_dbgcodec( + "irq_count %ld! IDX_IO_IRQSTATUS %04x\n", irq_count++ /* debug-only */, - snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS), - snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE), status ); if (status & IRQ_TIMER) { - /* snd_azf3328_dbgplay("timer %ld\n", + /* snd_azf3328_dbgcodec("timer %ld\n", snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE) & TIMER_VALUE_MASK ); */ @@ -1626,71 +1694,36 @@ snd_azf3328_interrupt(int irq, void *dev_id) snd_timer_interrupt(chip->timer, chip->timer->sticks); /* ACK timer */ spin_lock(&chip->reg_lock); - snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07); + snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07); spin_unlock(&chip->reg_lock); - snd_azf3328_dbgplay("azt3328: timer IRQ\n"); + snd_azf3328_dbgcodec("azt3328: timer IRQ\n"); } - if (status & IRQ_PLAYBACK) { - spin_lock(&chip->reg_lock); - which = snd_azf3328_codec_inb(chip, IDX_IO_PLAY_IRQTYPE); - /* ack all IRQ types immediately */ - snd_azf3328_codec_outb(chip, IDX_IO_PLAY_IRQTYPE, which); - spin_unlock(&chip->reg_lock); - if (chip->pcm && chip->audio_stream[AZF_PLAYBACK].substream) { - snd_pcm_period_elapsed( - chip->audio_stream[AZF_PLAYBACK].substream - ); - snd_azf3328_dbgplay("PLAY period done (#%x), @ %x\n", - which, - snd_azf3328_codec_inl( - chip, IDX_IO_PLAY_DMA_CURRPOS - ) - ); - } else - printk(KERN_WARNING "azt3328: irq handler problem!\n"); - if (which & IRQ_PLAY_SOMETHING) - snd_azf3328_irq_log_unknown_type(which); - } - if (status & IRQ_RECORDING) { - spin_lock(&chip->reg_lock); - which = snd_azf3328_codec_inb(chip, IDX_IO_REC_IRQTYPE); - /* ack all IRQ types immediately */ - snd_azf3328_codec_outb(chip, IDX_IO_REC_IRQTYPE, which); - spin_unlock(&chip->reg_lock); + if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT)) + snd_azf3328_codec_interrupt(chip, status); - if (chip->pcm && chip->audio_stream[AZF_CAPTURE].substream) { - snd_pcm_period_elapsed( - chip->audio_stream[AZF_CAPTURE].substream - ); - snd_azf3328_dbgplay("REC period done (#%x), @ %x\n", - which, - snd_azf3328_codec_inl( - chip, IDX_IO_REC_DMA_CURRPOS - ) - ); - } else - printk(KERN_WARNING "azt3328: irq handler problem!\n"); - if (which & IRQ_REC_SOMETHING) - snd_azf3328_irq_log_unknown_type(which); - } if (status & IRQ_GAMEPORT) snd_azf3328_gameport_interrupt(chip); + /* MPU401 has less critical IRQ requirements * than timer and playback/recording, right? */ if (status & IRQ_MPU401) { snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data); /* hmm, do we have to ack the IRQ here somehow? - * If so, then I don't know how... */ - snd_azf3328_dbgplay("azt3328: MPU401 IRQ\n"); + * If so, then I don't know how yet... */ + snd_azf3328_dbgcodec("azt3328: MPU401 IRQ\n"); } return IRQ_HANDLED; } /*****************************************************************/ -static const struct snd_pcm_hardware snd_azf3328_playback = +/* as long as we think we have identical snd_pcm_hardware parameters + for playback, capture and i2s out, we can use the same physical struct + since the struct is simply being copied into a member. +*/ +static const struct snd_pcm_hardware snd_azf3328_hardware = { /* FIXME!! Correct? */ .info = SNDRV_PCM_INFO_MMAP | @@ -1718,31 +1751,6 @@ static const struct snd_pcm_hardware snd_azf3328_playback = .fifo_size = 0, }; -static const struct snd_pcm_hardware snd_azf3328_capture = -{ - /* FIXME */ - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID, - .formats = SNDRV_PCM_FMTBIT_S8 | - SNDRV_PCM_FMTBIT_U8 | - SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_U16_LE, - .rates = SNDRV_PCM_RATE_5512 | - SNDRV_PCM_RATE_8000_48000 | - SNDRV_PCM_RATE_KNOT, - .rate_min = AZF_FREQ_4000, - .rate_max = AZF_FREQ_66200, - .channels_min = 1, - .channels_max = 2, - .buffer_bytes_max = 65536, - .period_bytes_min = 64, - .period_bytes_max = 65536, - .periods_min = 1, - .periods_max = 1024, - .fifo_size = 0, -}; - static unsigned int snd_azf3328_fixed_rates[] = { AZF_FREQ_4000, @@ -1770,14 +1778,19 @@ static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = { /*****************************************************************/ static int -snd_azf3328_playback_open(struct snd_pcm_substream *substream) +snd_azf3328_pcm_open(struct snd_pcm_substream *substream, + enum snd_azf3328_codec_type codec_type +) { struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; snd_azf3328_dbgcallenter(); - chip->audio_stream[AZF_PLAYBACK].substream = substream; - runtime->hw = snd_azf3328_playback; + chip->codecs[codec_type].substream = substream; + + /* same parameters for all our codecs - at least we think so... */ + runtime->hw = snd_azf3328_hardware; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &snd_azf3328_hw_constraints_rates); snd_azf3328_dbgcallleave(); @@ -1785,40 +1798,52 @@ snd_azf3328_playback_open(struct snd_pcm_substream *substream) } static int +snd_azf3328_playback_open(struct snd_pcm_substream *substream) +{ + return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK); +} + +static int snd_azf3328_capture_open(struct snd_pcm_substream *substream) { - struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; + return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE); +} - snd_azf3328_dbgcallenter(); - chip->audio_stream[AZF_CAPTURE].substream = substream; - runtime->hw = snd_azf3328_capture; - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - &snd_azf3328_hw_constraints_rates); - snd_azf3328_dbgcallleave(); - return 0; +static int +snd_azf3328_i2s_out_open(struct snd_pcm_substream *substream) +{ + return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT); } static int -snd_azf3328_playback_close(struct snd_pcm_substream *substream) +snd_azf3328_pcm_close(struct snd_pcm_substream *substream, + enum snd_azf3328_codec_type codec_type +) { struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); snd_azf3328_dbgcallenter(); - chip->audio_stream[AZF_PLAYBACK].substream = NULL; + chip->codecs[codec_type].substream = NULL; snd_azf3328_dbgcallleave(); return 0; } static int +snd_azf3328_playback_close(struct snd_pcm_substream *substream) +{ + return snd_azf3328_pcm_close(substream, AZF_CODEC_PLAYBACK); +} + +static int snd_azf3328_capture_close(struct snd_pcm_substream *substream) { - struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); + return snd_azf3328_pcm_close(substream, AZF_CODEC_CAPTURE); +} - snd_azf3328_dbgcallenter(); - chip->audio_stream[AZF_CAPTURE].substream = NULL; - snd_azf3328_dbgcallleave(); - return 0; +static int +snd_azf3328_i2s_out_close(struct snd_pcm_substream *substream) +{ + return snd_azf3328_pcm_close(substream, AZF_CODEC_I2S_OUT); } /******************************************************************/ @@ -1829,9 +1854,9 @@ static struct snd_pcm_ops snd_azf3328_playback_ops = { .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_azf3328_hw_params, .hw_free = snd_azf3328_hw_free, - .prepare = snd_azf3328_playback_prepare, - .trigger = snd_azf3328_playback_trigger, - .pointer = snd_azf3328_playback_pointer + .prepare = snd_azf3328_codec_prepare, + .trigger = snd_azf3328_codec_playback_trigger, + .pointer = snd_azf3328_codec_playback_pointer }; static struct snd_pcm_ops snd_azf3328_capture_ops = { @@ -1840,30 +1865,67 @@ static struct snd_pcm_ops snd_azf3328_capture_ops = { .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_azf3328_hw_params, .hw_free = snd_azf3328_hw_free, - .prepare = snd_azf3328_capture_prepare, - .trigger = snd_azf3328_capture_trigger, - .pointer = snd_azf3328_capture_pointer + .prepare = snd_azf3328_codec_prepare, + .trigger = snd_azf3328_codec_capture_trigger, + .pointer = snd_azf3328_codec_capture_pointer +}; + +static struct snd_pcm_ops snd_azf3328_i2s_out_ops = { + .open = snd_azf3328_i2s_out_open, + .close = snd_azf3328_i2s_out_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_azf3328_hw_params, + .hw_free = snd_azf3328_hw_free, + .prepare = snd_azf3328_codec_prepare, + .trigger = snd_azf3328_codec_i2s_out_trigger, + .pointer = snd_azf3328_codec_i2s_out_pointer }; static int __devinit -snd_azf3328_pcm(struct snd_azf3328 *chip, int device) +snd_azf3328_pcm(struct snd_azf3328 *chip) { +enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */ + struct snd_pcm *pcm; int err; snd_azf3328_dbgcallenter(); - if ((err = snd_pcm_new(chip->card, "AZF3328 DSP", device, 1, 1, &pcm)) < 0) + + err = snd_pcm_new(chip->card, "AZF3328 DSP", AZF_PCMDEV_STD, + 1, 1, &pcm); + if (err < 0) return err; - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_azf3328_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_azf3328_capture_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_azf3328_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_azf3328_capture_ops); pcm->private_data = chip; pcm->info_flags = 0; strcpy(pcm->name, chip->card->shortname); - chip->pcm = pcm; + /* same pcm object for playback/capture (see snd_pcm_new() above) */ + chip->pcm[AZF_CODEC_PLAYBACK] = pcm; + chip->pcm[AZF_CODEC_CAPTURE] = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->pci), 64*1024, 64*1024); + snd_dma_pci_data(chip->pci), + 64*1024, 64*1024); + + err = snd_pcm_new(chip->card, "AZF3328 I2S OUT", AZF_PCMDEV_I2S_OUT, + 1, 0, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_azf3328_i2s_out_ops); + + pcm->private_data = chip; + pcm->info_flags = 0; + strcpy(pcm->name, chip->card->shortname); + chip->pcm[AZF_CODEC_I2S_OUT] = pcm; + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 64*1024, 64*1024); snd_azf3328_dbgcallleave(); return 0; @@ -1902,7 +1964,7 @@ snd_azf3328_timer_start(struct snd_timer *timer) snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay); delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE; spin_lock_irqsave(&chip->reg_lock, flags); - snd_azf3328_codec_outl(chip, IDX_IO_TIMER_VALUE, delay); + snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_azf3328_dbgcallleave(); return 0; @@ -1919,7 +1981,7 @@ snd_azf3328_timer_stop(struct snd_timer *timer) spin_lock_irqsave(&chip->reg_lock, flags); /* disable timer countdown and interrupt */ /* FIXME: should we write TIMER_IRQ_ACK here? */ - snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0); + snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_azf3328_dbgcallleave(); return 0; @@ -2035,7 +2097,7 @@ snd_azf3328_test_bit(unsigned unsigned reg, int bit) outb(val, reg); - printk(KERN_ERR "reg %04x bit %d: %02x %02x %02x\n", + printk(KERN_DEBUG "reg %04x bit %d: %02x %02x %02x\n", reg, bit, val, valoff, valon ); } @@ -2048,9 +2110,9 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip) u16 tmp; snd_azf3328_dbgmisc( - "codec_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, " + "ctrl_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, " "opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n", - chip->codec_io, chip->game_io, chip->mpu_io, + chip->ctrl_io, chip->game_io, chip->mpu_io, chip->opl3_io, chip->mixer_io, chip->irq ); @@ -2083,9 +2145,9 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip) inb(0x38c + tmp) ); - for (tmp = 0; tmp < AZF_IO_SIZE_CODEC; tmp += 2) - snd_azf3328_dbgmisc("codec 0x%02x: 0x%04x\n", - tmp, snd_azf3328_codec_inw(chip, tmp) + for (tmp = 0; tmp < AZF_IO_SIZE_CTRL; tmp += 2) + snd_azf3328_dbgmisc("ctrl 0x%02x: 0x%04x\n", + tmp, snd_azf3328_ctrl_inw(chip, tmp) ); for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2) @@ -2106,7 +2168,8 @@ snd_azf3328_create(struct snd_card *card, static struct snd_device_ops ops = { .dev_free = snd_azf3328_dev_free, }; - u16 tmp; + u8 dma_init; + enum snd_azf3328_codec_type codec_type; *rchip = NULL; @@ -2138,14 +2201,21 @@ snd_azf3328_create(struct snd_card *card, if (err < 0) goto out_err; - chip->codec_io = pci_resource_start(pci, 0); + chip->ctrl_io = pci_resource_start(pci, 0); chip->game_io = pci_resource_start(pci, 1); chip->mpu_io = pci_resource_start(pci, 2); - chip->opl3_io = pci_resource_start(pci, 3); + chip->opl3_io = pci_resource_start(pci, 3); chip->mixer_io = pci_resource_start(pci, 4); - chip->audio_stream[AZF_PLAYBACK].portbase = chip->codec_io + 0x00; - chip->audio_stream[AZF_CAPTURE].portbase = chip->codec_io + 0x20; + chip->codecs[AZF_CODEC_PLAYBACK].io_base = + chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK; + chip->codecs[AZF_CODEC_PLAYBACK].name = "PLAYBACK"; + chip->codecs[AZF_CODEC_CAPTURE].io_base = + chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE; + chip->codecs[AZF_CODEC_CAPTURE].name = "CAPTURE"; + chip->codecs[AZF_CODEC_I2S_OUT].io_base = + chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT; + chip->codecs[AZF_CODEC_I2S_OUT].name = "I2S_OUT"; if (request_irq(pci->irq, snd_azf3328_interrupt, IRQF_SHARED, card->shortname, chip)) { @@ -2168,20 +2238,25 @@ snd_azf3328_create(struct snd_card *card, if (err < 0) goto out_err; - /* shutdown codecs to save power */ - /* have snd_azf3328_codec_activity() act properly */ - chip->audio_stream[AZF_PLAYBACK].running = 1; - snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0); + /* standard codec init stuff */ + /* default DMA init value */ + dma_init = DMA_RUN_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE; - /* standard chip init stuff */ - /* default IRQ init value */ - tmp = DMA_PLAY_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE; + for (codec_type = AZF_CODEC_PLAYBACK; + codec_type <= AZF_CODEC_I2S_OUT; ++codec_type) { + struct snd_azf3328_codec_data *codec = + &chip->codecs[codec_type]; - spin_lock_irq(&chip->reg_lock); - snd_azf3328_codec_outb(chip, IDX_IO_PLAY_FLAGS, tmp); - snd_azf3328_codec_outb(chip, IDX_IO_REC_FLAGS, tmp); - snd_azf3328_codec_outb(chip, IDX_IO_SOMETHING_FLAGS, tmp); - spin_unlock_irq(&chip->reg_lock); + /* shutdown codecs to save power */ + /* have ...ctrl_codec_activity() act properly */ + codec->running = 1; + snd_azf3328_ctrl_codec_activity(chip, codec_type, 0); + + spin_lock_irq(&chip->reg_lock); + snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS, + dma_init); + spin_unlock_irq(&chip->reg_lock); + } snd_card_set_dev(card, &pci->dev); @@ -2229,8 +2304,11 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) card->private_data = chip; + /* chose to use MPU401_HW_AZT2320 ID instead of MPU401_HW_MPU401, + since our hardware ought to be similar, thus use same ID. */ err = snd_mpu401_uart_new( - card, 0, MPU401_HW_MPU401, chip->mpu_io, MPU401_INFO_INTEGRATED, + card, 0, + MPU401_HW_AZT2320, chip->mpu_io, MPU401_INFO_INTEGRATED, pci->irq, 0, &chip->rmidi ); if (err < 0) { @@ -2244,7 +2322,7 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) if (err < 0) goto out_err; - err = snd_azf3328_pcm(chip, 0); + err = snd_azf3328_pcm(chip); if (err < 0) goto out_err; @@ -2266,14 +2344,14 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) opl3->private_data = chip; sprintf(card->longname, "%s at 0x%lx, irq %i", - card->shortname, chip->codec_io, chip->irq); + card->shortname, chip->ctrl_io, chip->irq); err = snd_card_register(card); if (err < 0) goto out_err; #ifdef MODULE - printk( + printk(KERN_INFO "azt3328: Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n" "azt3328: Hardware was completely undocumented, unfortunately.\n" "azt3328: Feel free to contact andi AT lisas.de for bug reports etc.!\n" @@ -2308,36 +2386,52 @@ snd_azf3328_remove(struct pci_dev *pci) } #ifdef CONFIG_PM +static inline void +snd_azf3328_suspend_regs(unsigned long io_addr, unsigned count, u32 *saved_regs) +{ + unsigned reg; + + for (reg = 0; reg < count; ++reg) { + *saved_regs = inl(io_addr); + snd_azf3328_dbgpm("suspend: io 0x%04lx: 0x%08x\n", + io_addr, *saved_regs); + ++saved_regs; + io_addr += sizeof(*saved_regs); + } +} + static int snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) { struct snd_card *card = pci_get_drvdata(pci); struct snd_azf3328 *chip = card->private_data; - unsigned reg; + u16 *saved_regs_ctrl_u16; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); + snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]); + snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]); - for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg) - chip->saved_regs_mixer[reg] = inw(chip->mixer_io + reg * 2); + snd_azf3328_suspend_regs(chip->mixer_io, + ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer); /* make sure to disable master volume etc. to prevent looping sound */ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1); snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1); - for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg) - chip->saved_regs_codec[reg] = inw(chip->codec_io + reg * 2); + snd_azf3328_suspend_regs(chip->ctrl_io, + ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl); /* manually store the one currently relevant write-only reg, too */ - chip->saved_regs_codec[IDX_IO_6AH / 2] = chip->shadow_reg_codec_6AH; + saved_regs_ctrl_u16 = (u16 *)chip->saved_regs_ctrl; + saved_regs_ctrl_u16[IDX_IO_6AH / 2] = chip->shadow_reg_ctrl_6AH; - for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg) - chip->saved_regs_game[reg] = inw(chip->game_io + reg * 2); - for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg) - chip->saved_regs_mpu[reg] = inw(chip->mpu_io + reg * 2); - for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg) - chip->saved_regs_opl3[reg] = inw(chip->opl3_io + reg * 2); + snd_azf3328_suspend_regs(chip->game_io, + ARRAY_SIZE(chip->saved_regs_game), chip->saved_regs_game); + snd_azf3328_suspend_regs(chip->mpu_io, + ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu); + snd_azf3328_suspend_regs(chip->opl3_io, + ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3); pci_disable_device(pci); pci_save_state(pci); @@ -2345,12 +2439,28 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) return 0; } +static inline void +snd_azf3328_resume_regs(const u32 *saved_regs, + unsigned long io_addr, + unsigned count +) +{ + unsigned reg; + + for (reg = 0; reg < count; ++reg) { + outl(*saved_regs, io_addr); + snd_azf3328_dbgpm("resume: io 0x%04lx: 0x%08x --> 0x%08x\n", + io_addr, *saved_regs, inl(io_addr)); + ++saved_regs; + io_addr += sizeof(*saved_regs); + } +} + static int snd_azf3328_resume(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci); - struct snd_azf3328 *chip = card->private_data; - unsigned reg; + const struct snd_azf3328 *chip = card->private_data; pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); @@ -2362,16 +2472,24 @@ snd_azf3328_resume(struct pci_dev *pci) } pci_set_master(pci); - for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg) - outw(chip->saved_regs_game[reg], chip->game_io + reg * 2); - for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg) - outw(chip->saved_regs_mpu[reg], chip->mpu_io + reg * 2); - for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg) - outw(chip->saved_regs_opl3[reg], chip->opl3_io + reg * 2); - for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg) - outw(chip->saved_regs_mixer[reg], chip->mixer_io + reg * 2); - for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg) - outw(chip->saved_regs_codec[reg], chip->codec_io + reg * 2); + snd_azf3328_resume_regs(chip->saved_regs_game, chip->game_io, + ARRAY_SIZE(chip->saved_regs_game)); + snd_azf3328_resume_regs(chip->saved_regs_mpu, chip->mpu_io, + ARRAY_SIZE(chip->saved_regs_mpu)); + snd_azf3328_resume_regs(chip->saved_regs_opl3, chip->opl3_io, + ARRAY_SIZE(chip->saved_regs_opl3)); + + snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io, + ARRAY_SIZE(chip->saved_regs_mixer)); + + /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02) + and IDX_MIXER_RESET (offset 0x00) get touched at the same time, + resulting in a mixer reset condition persisting until _after_ + master vol was restored. Thus master vol needs an extra restore. */ + outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2); + + snd_azf3328_resume_regs(chip->saved_regs_ctrl, chip->ctrl_io, + ARRAY_SIZE(chip->saved_regs_ctrl)); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; diff --git a/sound/pci/azt3328.h b/sound/pci/azt3328.h index 974e05122f0..6f46b97650c 100644 --- a/sound/pci/azt3328.h +++ b/sound/pci/azt3328.h @@ -6,50 +6,59 @@ /*** main I/O area port indices ***/ /* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */ -#define AZF_IO_SIZE_CODEC 0x80 -#define AZF_IO_SIZE_CODEC_PM 0x70 +#define AZF_IO_SIZE_CTRL 0x80 +#define AZF_IO_SIZE_CTRL_PM 0x70 -/* the driver initialisation suggests a layout of 4 main areas: - * from 0x00 (playback), from 0x20 (recording) and from 0x40 (maybe MPU401??). +/* the driver initialisation suggests a layout of 4 areas + * within the main card control I/O: + * from 0x00 (playback codec), from 0x20 (recording codec) + * and from 0x40 (most certainly I2S out codec). * And another area from 0x60 to 0x6f (DirectX timer, IRQ management, * power management etc.???). */ -/** playback area **/ -#define IDX_IO_PLAY_FLAGS 0x00 /* PU:0x0000 */ +#define AZF_IO_OFFS_CODEC_PLAYBACK 0x00 +#define AZF_IO_OFFS_CODEC_CAPTURE 0x20 +#define AZF_IO_OFFS_CODEC_I2S_OUT 0x40 + +#define IDX_IO_CODEC_DMA_FLAGS 0x00 /* PU:0x0000 */ /* able to reactivate output after output muting due to 8/16bit * output change, just like 0x0002. * 0x0001 is the only bit that's able to start the DMA counter */ - #define DMA_RESUME 0x0001 /* paused if cleared ? */ + #define DMA_RESUME 0x0001 /* paused if cleared? */ /* 0x0002 *temporarily* set during DMA stopping. hmm * both 0x0002 and 0x0004 set in playback setup. */ /* able to reactivate output after output muting due to 8/16bit * output change, just like 0x0001. */ - #define DMA_PLAY_SOMETHING1 0x0002 /* \ alternated (toggled) */ + #define DMA_RUN_SOMETHING1 0x0002 /* \ alternated (toggled) */ /* 0x0004: NOT able to reactivate output */ - #define DMA_PLAY_SOMETHING2 0x0004 /* / bits */ + #define DMA_RUN_SOMETHING2 0x0004 /* / bits */ #define SOMETHING_ALMOST_ALWAYS_SET 0x0008 /* ???; can be modified */ #define DMA_EPILOGUE_SOMETHING 0x0010 #define DMA_SOMETHING_ELSE 0x0020 /* ??? */ - #define SOMETHING_UNMODIFIABLE 0xffc0 /* unused ? not modifiable */ -#define IDX_IO_PLAY_IRQTYPE 0x02 /* PU:0x0001 */ + #define SOMETHING_UNMODIFIABLE 0xffc0 /* unused? not modifiable */ +#define IDX_IO_CODEC_IRQTYPE 0x02 /* PU:0x0001 */ /* write back to flags in case flags are set, in order to ACK IRQ in handler * (bit 1 of port 0x64 indicates interrupt for one of these three types) * sometimes in this case it just writes 0xffff to globally ACK all IRQs * settings written are not reflected when reading back, though. - * seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows ? */ - #define IRQ_PLAY_SOMETHING 0x0001 /* something & ACK */ - #define IRQ_FINISHED_PLAYBUF_1 0x0002 /* 1st dmabuf finished & ACK */ - #define IRQ_FINISHED_PLAYBUF_2 0x0004 /* 2nd dmabuf finished & ACK */ + * seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows? */ + #define IRQ_SOMETHING 0x0001 /* something & ACK */ + #define IRQ_FINISHED_DMABUF_1 0x0002 /* 1st dmabuf finished & ACK */ + #define IRQ_FINISHED_DMABUF_2 0x0004 /* 2nd dmabuf finished & ACK */ #define IRQMASK_SOME_STATUS_1 0x0008 /* \ related bits */ #define IRQMASK_SOME_STATUS_2 0x0010 /* / (checked together in loop) */ - #define IRQMASK_UNMODIFIABLE 0xffe0 /* unused ? not modifiable */ -#define IDX_IO_PLAY_DMA_START_1 0x04 /* start address of 1st DMA play area, PU:0x00000000 */ -#define IDX_IO_PLAY_DMA_START_2 0x08 /* start address of 2nd DMA play area, PU:0x00000000 */ -#define IDX_IO_PLAY_DMA_LEN_1 0x0c /* length of 1st DMA play area, PU:0x0000 */ -#define IDX_IO_PLAY_DMA_LEN_2 0x0e /* length of 2nd DMA play area, PU:0x0000 */ -#define IDX_IO_PLAY_DMA_CURRPOS 0x10 /* current DMA position, PU:0x00000000 */ -#define IDX_IO_PLAY_DMA_CURROFS 0x14 /* offset within current DMA play area, PU:0x0000 */ -#define IDX_IO_PLAY_SOUNDFORMAT 0x16 /* PU:0x0010 */ + #define IRQMASK_UNMODIFIABLE 0xffe0 /* unused? not modifiable */ + /* start address of 1st DMA transfer area, PU:0x00000000 */ +#define IDX_IO_CODEC_DMA_START_1 0x04 + /* start address of 2nd DMA transfer area, PU:0x00000000 */ +#define IDX_IO_CODEC_DMA_START_2 0x08 + /* both lengths of DMA transfer areas, PU:0x00000000 + length1: offset 0x0c, length2: offset 0x0e */ +#define IDX_IO_CODEC_DMA_LENGTHS 0x0c +#define IDX_IO_CODEC_DMA_CURRPOS 0x10 /* current DMA position, PU:0x00000000 */ + /* offset within current DMA transfer area, PU:0x0000 */ +#define IDX_IO_CODEC_DMA_CURROFS 0x14 +#define IDX_IO_CODEC_SOUNDFORMAT 0x16 /* PU:0x0010 */ /* all unspecified bits can't be modified */ #define SOUNDFORMAT_FREQUENCY_MASK 0x000f #define SOUNDFORMAT_XTAL1 0x00 @@ -76,6 +85,7 @@ #define SOUNDFORMAT_FLAG_16BIT 0x0010 #define SOUNDFORMAT_FLAG_2CHANNELS 0x0020 + /* define frequency helpers, for maximum value safety */ enum azf_freq_t { #define AZF_FREQ(rate) AZF_FREQ_##rate = rate @@ -96,29 +106,6 @@ enum azf_freq_t { #undef AZF_FREQ }; -/** recording area (see also: playback bit flag definitions) **/ -#define IDX_IO_REC_FLAGS 0x20 /* ??, PU:0x0000 */ -#define IDX_IO_REC_IRQTYPE 0x22 /* ??, PU:0x0000 */ - #define IRQ_REC_SOMETHING 0x0001 /* something & ACK */ - #define IRQ_FINISHED_RECBUF_1 0x0002 /* 1st dmabuf finished & ACK */ - #define IRQ_FINISHED_RECBUF_2 0x0004 /* 2nd dmabuf finished & ACK */ - /* hmm, maybe these are just the corresponding *recording* flags ? - * but OTOH they are most likely at port 0x22 instead */ - #define IRQMASK_SOME_STATUS_1 0x0008 /* \ related bits */ - #define IRQMASK_SOME_STATUS_2 0x0010 /* / (checked together in loop) */ -#define IDX_IO_REC_DMA_START_1 0x24 /* PU:0x00000000 */ -#define IDX_IO_REC_DMA_START_2 0x28 /* PU:0x00000000 */ -#define IDX_IO_REC_DMA_LEN_1 0x2c /* PU:0x0000 */ -#define IDX_IO_REC_DMA_LEN_2 0x2e /* PU:0x0000 */ -#define IDX_IO_REC_DMA_CURRPOS 0x30 /* PU:0x00000000 */ -#define IDX_IO_REC_DMA_CURROFS 0x34 /* PU:0x00000000 */ -#define IDX_IO_REC_SOUNDFORMAT 0x36 /* PU:0x0000 */ - -/** hmm, what is this I/O area for? MPU401?? or external DAC via I2S?? (after playback, recording, ???, timer) **/ -#define IDX_IO_SOMETHING_FLAGS 0x40 /* gets set to 0x34 just like port 0x0 and 0x20 on card init, PU:0x0000 */ -/* general */ -#define IDX_IO_42H 0x42 /* PU:0x0001 */ - /** DirectX timer, main interrupt area (FIXME: and something else?) **/ #define IDX_IO_TIMER_VALUE 0x60 /* found this timer area by pure luck :-) */ /* timer countdown value; triggers IRQ when timer is finished */ @@ -133,17 +120,19 @@ enum azf_freq_t { #define IDX_IO_IRQSTATUS 0x64 /* some IRQ bit in here might also be used to signal a power-management timer * timeout, to request shutdown of the chip (e.g. AD1815JS has such a thing). - * Some OPL3 hardware (e.g. in LM4560) has some special timer hardware which - * can trigger an OPL3 timer IRQ, so maybe there's such a thing as well... */ + * OPL3 hardware contains several timers which confusingly in most cases + * are NOT routed to an IRQ, but some designs (e.g. LM4560) DO support that, + * so I wouldn't be surprised at all to discover that AZF3328 + * supports that thing as well... */ #define IRQ_PLAYBACK 0x0001 #define IRQ_RECORDING 0x0002 - #define IRQ_UNKNOWN1 0x0004 /* most probably I2S port */ + #define IRQ_I2S_OUT 0x0004 /* this IS I2S, right!? (untested) */ #define IRQ_GAMEPORT 0x0008 /* Interrupt of Digital(ly) Enhanced Game Port */ #define IRQ_MPU401 0x0010 #define IRQ_TIMER 0x0020 /* DirectX timer */ - #define IRQ_UNKNOWN2 0x0040 /* probably unused, or possibly I2S port? */ - #define IRQ_UNKNOWN3 0x0080 /* probably unused, or possibly I2S port? */ + #define IRQ_UNKNOWN2 0x0040 /* probably unused, or possibly OPL3 timer? */ + #define IRQ_UNKNOWN3 0x0080 /* probably unused, or possibly OPL3 timer? */ #define IDX_IO_66H 0x66 /* writing 0xffff returns 0x0000 */ /* this is set to e.g. 0x3ff or 0x300, and writable; * maybe some buffer limit, but I couldn't find out more, PU:0x00ff: */ @@ -206,7 +195,7 @@ enum azf_freq_t { /*** Gameport area port indices ***/ /* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */ #define AZF_IO_SIZE_GAME 0x08 -#define AZF_IO_SIZE_GAME_PM 0x06 +#define AZF_IO_SIZE_GAME_PM 0x06 enum { AZF_GAME_LEGACY_IO_PORT = 0x200 @@ -272,6 +261,12 @@ enum { * 11 --> 1/200: */ #define GAME_HWCFG_ADC_COUNTER_FREQ_MASK 0x06 + /* FIXME: these values might be reversed... */ + #define GAME_HWCFG_ADC_COUNTER_FREQ_STD 0 + #define GAME_HWCFG_ADC_COUNTER_FREQ_1_2 1 + #define GAME_HWCFG_ADC_COUNTER_FREQ_1_20 2 + #define GAME_HWCFG_ADC_COUNTER_FREQ_1_200 3 + /* enable gameport legacy I/O address (0x200) * I was unable to locate any configurability for a different address: */ #define GAME_HWCFG_LEGACY_ADDRESS_ENABLE 0x08 @@ -281,6 +276,7 @@ enum { #define AZF_IO_SIZE_MPU_PM 0x04 /*** OPL3 synth ***/ +/* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */ #define AZF_IO_SIZE_OPL3 0x08 #define AZF_IO_SIZE_OPL3_PM 0x06 /* hmm, given that a standard OPL3 has 4 registers only, @@ -340,4 +336,7 @@ enum { #define SET_CHAN_LEFT 1 #define SET_CHAN_RIGHT 2 +/* helper macro to align I/O port ranges to 32bit I/O width */ +#define AZF_ALIGN(x) (((x) + 3) & (~3)) + #endif /* __SOUND_AZT3328_H */ diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 24585c6c6d0..4e2b925a94c 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -808,6 +808,8 @@ static struct pci_device_id snd_bt87x_ids[] = { BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1002, 0x0001, GENERIC), /* Leadtek Winfast tv 2000xp delux */ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x107d, 0x6606, GENERIC), + /* Pinnacle PCTV */ + BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x11bd, 0x0012, GENERIC), /* Voodoo TV 200 */ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x121a, 0x3000, GENERIC), /* Askey Computer Corp. MagicTView'99 */ diff --git a/sound/pci/cs46xx/cs46xx_lib.h b/sound/pci/cs46xx/cs46xx_lib.h index 4eb55aa3361..b5189495d58 100644 --- a/sound/pci/cs46xx/cs46xx_lib.h +++ b/sound/pci/cs46xx/cs46xx_lib.h @@ -35,7 +35,7 @@ #ifdef CONFIG_SND_CS46XX_NEW_DSP -#define CS46XX_MIN_PERIOD_SIZE 1 +#define CS46XX_MIN_PERIOD_SIZE 64 #define CS46XX_MAX_PERIOD_SIZE 1024*1024 #else #define CS46XX_MIN_PERIOD_SIZE 2048 diff --git a/sound/pci/ctxfi/ct20k2reg.h b/sound/pci/ctxfi/ct20k2reg.h index 2d07986f57c..e0394e3996e 100644 --- a/sound/pci/ctxfi/ct20k2reg.h +++ b/sound/pci/ctxfi/ct20k2reg.h @@ -11,9 +11,12 @@ /* Timer Registers */ -#define TIMER_TIMR 0x1B7004 -#define INTERRUPT_GIP 0x1B7010 -#define INTERRUPT_GIE 0x1B7014 +#define WC 0x1b7000 +#define TIMR 0x1b7004 +# define TIMR_IE (1<<15) +# define TIMR_IP (1<<14) +#define GIP 0x1b7010 +#define GIE 0x1b7014 /* I2C Registers */ #define I2C_IF_ADDRESS 0x1B9000 diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c index a7f4a671f7b..fee35cfc0c7 100644 --- a/sound/pci/ctxfi/ctamixer.c +++ b/sound/pci/ctxfi/ctamixer.c @@ -63,7 +63,7 @@ static int amixer_set_input(struct amixer *amixer, struct rsc *rsc) hw = amixer->rsc.hw; hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE); amixer->input = rsc; - if (NULL == rsc) + if (!rsc) hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT); else hw->amixer_set_x(amixer->rsc.ctrl_blk, @@ -99,7 +99,7 @@ static int amixer_set_sum(struct amixer *amixer, struct sum *sum) hw = amixer->rsc.hw; amixer->sum = sum; - if (NULL == sum) { + if (!sum) { hw->amixer_set_se(amixer->rsc.ctrl_blk, 0); } else { hw->amixer_set_se(amixer->rsc.ctrl_blk, 1); @@ -124,20 +124,20 @@ static int amixer_commit_write(struct amixer *amixer) /* Program master and conjugate resources */ amixer->rsc.ops->master(&amixer->rsc); - if (NULL != input) + if (input) input->ops->master(input); - if (NULL != sum) + if (sum) sum->rsc.ops->master(&sum->rsc); for (i = 0; i < amixer->rsc.msr; i++) { hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk); - if (NULL != input) { + if (input) { hw->amixer_set_x(amixer->rsc.ctrl_blk, input->ops->output_slot(input)); input->ops->next_conj(input); } - if (NULL != sum) { + if (sum) { hw->amixer_set_sadr(amixer->rsc.ctrl_blk, sum->rsc.ops->index(&sum->rsc)); sum->rsc.ops->next_conj(&sum->rsc); @@ -147,10 +147,10 @@ static int amixer_commit_write(struct amixer *amixer) amixer->rsc.ops->next_conj(&amixer->rsc); } amixer->rsc.ops->master(&amixer->rsc); - if (NULL != input) + if (input) input->ops->master(input); - if (NULL != sum) + if (sum) sum->rsc.ops->master(&sum->rsc); return 0; @@ -303,7 +303,7 @@ int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr) *ramixer_mgr = NULL; amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL); - if (NULL == amixer_mgr) + if (!amixer_mgr) return -ENOMEM; err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw); @@ -456,7 +456,7 @@ int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr) *rsum_mgr = NULL; sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL); - if (NULL == sum_mgr) + if (!sum_mgr) return -ENOMEM; err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw); diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index a49c7664730..75454648d50 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -136,7 +136,7 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) struct snd_pcm_runtime *runtime; struct ct_vm *vm; - if (NULL == apcm->substream) + if (!apcm->substream) return 0; runtime = apcm->substream->runtime; @@ -144,7 +144,7 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) apcm->vm_block = vm->map(vm, apcm->substream, runtime->dma_bytes); - if (NULL == apcm->vm_block) + if (!apcm->vm_block) return -ENOENT; return 0; @@ -154,7 +154,7 @@ static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) { struct ct_vm *vm; - if (NULL == apcm->vm_block) + if (!apcm->vm_block) return; vm = atc->vm; @@ -231,16 +231,16 @@ atc_get_pitch(unsigned int input_rate, unsigned int output_rate) static int select_rom(unsigned int pitch) { - if ((pitch > 0x00428f5c) && (pitch < 0x01b851ec)) { + if (pitch > 0x00428f5c && pitch < 0x01b851ec) { /* 0.26 <= pitch <= 1.72 */ return 1; - } else if ((0x01d66666 == pitch) || (0x01d66667 == pitch)) { + } else if (pitch == 0x01d66666 || pitch == 0x01d66667) { /* pitch == 1.8375 */ return 2; - } else if (0x02000000 == pitch) { + } else if (pitch == 0x02000000) { /* pitch == 2 */ return 3; - } else if ((pitch >= 0x0) && (pitch <= 0x08000000)) { + } else if (pitch >= 0x0 && pitch <= 0x08000000) { /* 0 <= pitch <= 8 */ return 0; } else { @@ -283,7 +283,7 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) /* Get AMIXER resource */ n_amixer = (n_amixer < 2) ? 2 : n_amixer; apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); - if (NULL == apcm->amixers) { + if (!apcm->amixers) { err = -ENOMEM; goto error1; } @@ -311,7 +311,7 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) INIT_VOL, atc->pcm[i+device*2]); mutex_unlock(&atc->atc_mutex); src = src->ops->next_interleave(src); - if (NULL == src) + if (!src) src = apcm->src; } @@ -334,7 +334,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) struct srcimp *srcimp; int i; - if (NULL != apcm->srcimps) { + if (apcm->srcimps) { for (i = 0; i < apcm->n_srcimp; i++) { srcimp = apcm->srcimps[i]; srcimp->ops->unmap(srcimp); @@ -345,7 +345,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) apcm->srcimps = NULL; } - if (NULL != apcm->srccs) { + if (apcm->srccs) { for (i = 0; i < apcm->n_srcc; i++) { src_mgr->put_src(src_mgr, apcm->srccs[i]); apcm->srccs[i] = NULL; @@ -354,7 +354,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) apcm->srccs = NULL; } - if (NULL != apcm->amixers) { + if (apcm->amixers) { for (i = 0; i < apcm->n_amixer; i++) { amixer_mgr->put_amixer(amixer_mgr, apcm->amixers[i]); apcm->amixers[i] = NULL; @@ -363,17 +363,17 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) apcm->amixers = NULL; } - if (NULL != apcm->mono) { + if (apcm->mono) { sum_mgr->put_sum(sum_mgr, apcm->mono); apcm->mono = NULL; } - if (NULL != apcm->src) { + if (apcm->src) { src_mgr->put_src(src_mgr, apcm->src); apcm->src = NULL; } - if (NULL != apcm->vm_block) { + if (apcm->vm_block) { /* Undo device virtual mem map */ ct_unmap_audio_buffer(atc, apcm); apcm->vm_block = NULL; @@ -419,7 +419,7 @@ static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm) src->ops->set_state(src, SRC_STATE_OFF); src->ops->commit_write(src); - if (NULL != apcm->srccs) { + if (apcm->srccs) { for (i = 0; i < apcm->n_srcc; i++) { src = apcm->srccs[i]; src->ops->set_bm(src, 0); @@ -544,18 +544,18 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm) if (n_srcc) { apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL); - if (NULL == apcm->srccs) + if (!apcm->srccs) return -ENOMEM; } if (n_amixer) { apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); - if (NULL == apcm->amixers) { + if (!apcm->amixers) { err = -ENOMEM; goto error1; } } apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL); - if (NULL == apcm->srcimps) { + if (!apcm->srcimps) { err = -ENOMEM; goto error1; } @@ -818,7 +818,7 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc, /* Get AMIXER resource */ n_amixer = (n_amixer < 2) ? 2 : n_amixer; apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL); - if (NULL == apcm->amixers) { + if (!apcm->amixers) { err = -ENOMEM; goto error1; } @@ -919,7 +919,7 @@ spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) amixer = apcm->amixers[i]; amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL); src = src->ops->next_interleave(src); - if (NULL == src) + if (!src) src = apcm->src; } /* Connect to SPDIFOO */ @@ -1037,7 +1037,7 @@ static int atc_line_front_unmute(struct ct_atc *atc, unsigned char state) static int atc_line_surround_unmute(struct ct_atc *atc, unsigned char state) { - return atc_daio_unmute(atc, state, LINEO4); + return atc_daio_unmute(atc, state, LINEO2); } static int atc_line_clfe_unmute(struct ct_atc *atc, unsigned char state) @@ -1047,7 +1047,7 @@ static int atc_line_clfe_unmute(struct ct_atc *atc, unsigned char state) static int atc_line_rear_unmute(struct ct_atc *atc, unsigned char state) { - return atc_daio_unmute(atc, state, LINEO2); + return atc_daio_unmute(atc, state, LINEO4); } static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state) @@ -1121,7 +1121,7 @@ static int atc_release_resources(struct ct_atc *atc) struct ct_mixer *mixer = NULL; /* disconnect internal mixer objects */ - if (NULL != atc->mixer) { + if (atc->mixer) { mixer = atc->mixer; mixer->set_input_left(mixer, MIX_LINE_IN, NULL); mixer->set_input_right(mixer, MIX_LINE_IN, NULL); @@ -1131,7 +1131,7 @@ static int atc_release_resources(struct ct_atc *atc) mixer->set_input_right(mixer, MIX_SPDIF_IN, NULL); } - if (NULL != atc->daios) { + if (atc->daios) { daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; for (i = 0; i < atc->n_daio; i++) { daio = atc->daios[i]; @@ -1149,7 +1149,7 @@ static int atc_release_resources(struct ct_atc *atc) atc->daios = NULL; } - if (NULL != atc->pcm) { + if (atc->pcm) { sum_mgr = atc->rsc_mgrs[SUM]; for (i = 0; i < atc->n_pcm; i++) sum_mgr->put_sum(sum_mgr, atc->pcm[i]); @@ -1158,7 +1158,7 @@ static int atc_release_resources(struct ct_atc *atc) atc->pcm = NULL; } - if (NULL != atc->srcs) { + if (atc->srcs) { src_mgr = atc->rsc_mgrs[SRC]; for (i = 0; i < atc->n_src; i++) src_mgr->put_src(src_mgr, atc->srcs[i]); @@ -1167,7 +1167,7 @@ static int atc_release_resources(struct ct_atc *atc) atc->srcs = NULL; } - if (NULL != atc->srcimps) { + if (atc->srcimps) { srcimp_mgr = atc->rsc_mgrs[SRCIMP]; for (i = 0; i < atc->n_srcimp; i++) { srcimp = atc->srcimps[i]; @@ -1185,7 +1185,7 @@ static int ct_atc_destroy(struct ct_atc *atc) { int i = 0; - if (NULL == atc) + if (!atc) return 0; if (atc->timer) { @@ -1196,21 +1196,20 @@ static int ct_atc_destroy(struct ct_atc *atc) atc_release_resources(atc); /* Destroy internal mixer objects */ - if (NULL != atc->mixer) + if (atc->mixer) ct_mixer_destroy(atc->mixer); for (i = 0; i < NUM_RSCTYP; i++) { - if ((NULL != rsc_mgr_funcs[i].destroy) && - (NULL != atc->rsc_mgrs[i])) + if (rsc_mgr_funcs[i].destroy && atc->rsc_mgrs[i]) rsc_mgr_funcs[i].destroy(atc->rsc_mgrs[i]); } - if (NULL != atc->hw) + if (atc->hw) destroy_hw_obj((struct hw *)atc->hw); /* Destroy device virtual memory manager object */ - if (NULL != atc->vm) { + if (atc->vm) { ct_vm_destroy(atc->vm); atc->vm = NULL; } @@ -1275,7 +1274,7 @@ int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc) alsa_dev_funcs[MIXER].public_name = atc->chip_name; for (i = 0; i < NUM_CTALSADEVS; i++) { - if (NULL == alsa_dev_funcs[i].create) + if (!alsa_dev_funcs[i].create) continue; err = alsa_dev_funcs[i].create(atc, i, @@ -1312,7 +1311,7 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc) return err; for (i = 0; i < NUM_RSCTYP; i++) { - if (NULL == rsc_mgr_funcs[i].create) + if (!rsc_mgr_funcs[i].create) continue; err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]); @@ -1339,19 +1338,19 @@ static int atc_get_resources(struct ct_atc *atc) int err, i; atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL); - if (NULL == atc->daios) + if (!atc->daios) return -ENOMEM; atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); - if (NULL == atc->srcs) + if (!atc->srcs) return -ENOMEM; atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL); - if (NULL == atc->srcimps) + if (!atc->srcimps) return -ENOMEM; atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL); - if (NULL == atc->pcm) + if (!atc->pcm) return -ENOMEM; daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO]; @@ -1648,7 +1647,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, *ratc = NULL; atc = kzalloc(sizeof(*atc), GFP_KERNEL); - if (NULL == atc) + if (!atc) return -ENOMEM; /* Set operations */ diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index deb6cfa7360..af56eb949bd 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -173,7 +173,7 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input) int i; entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL); - if (NULL == entry) + if (!entry) return -ENOMEM; /* Program master and conjugate resources */ @@ -201,7 +201,7 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input) int i; entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL); - if (NULL == entry) + if (!entry) return -ENOMEM; /* Program master and conjugate resources */ @@ -228,7 +228,7 @@ static int dao_clear_left_input(struct dao *dao) struct daio *daio = &dao->daio; int i; - if (NULL == dao->imappers[0]) + if (!dao->imappers[0]) return 0; entry = dao->imappers[0]; @@ -252,7 +252,7 @@ static int dao_clear_right_input(struct dao *dao) struct daio *daio = &dao->daio; int i; - if (NULL == dao->imappers[daio->rscl.msr]) + if (!dao->imappers[daio->rscl.msr]) return 0; entry = dao->imappers[daio->rscl.msr]; @@ -408,7 +408,7 @@ static int dao_rsc_init(struct dao *dao, return err; dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL); - if (NULL == dao->imappers) { + if (!dao->imappers) { err = -ENOMEM; goto error1; } @@ -442,11 +442,11 @@ error1: static int dao_rsc_uninit(struct dao *dao) { - if (NULL != dao->imappers) { - if (NULL != dao->imappers[0]) + if (dao->imappers) { + if (dao->imappers[0]) dao_clear_left_input(dao); - if (NULL != dao->imappers[dao->daio.rscl.msr]) + if (dao->imappers[dao->daio.rscl.msr]) dao_clear_right_input(dao); kfree(dao->imappers); @@ -555,7 +555,7 @@ static int get_daio_rsc(struct daio_mgr *mgr, /* Allocate mem for daio resource */ if (desc->type <= DAIO_OUT_MAX) { dao = kzalloc(sizeof(*dao), GFP_KERNEL); - if (NULL == dao) { + if (!dao) { err = -ENOMEM; goto error; } @@ -566,7 +566,7 @@ static int get_daio_rsc(struct daio_mgr *mgr, *rdaio = &dao->daio; } else { dai = kzalloc(sizeof(*dai), GFP_KERNEL); - if (NULL == dai) { + if (!dai) { err = -ENOMEM; goto error; } @@ -583,9 +583,9 @@ static int get_daio_rsc(struct daio_mgr *mgr, return 0; error: - if (NULL != dao) + if (dao) kfree(dao); - else if (NULL != dai) + else if (dai) kfree(dai); spin_lock_irqsave(&mgr->mgr_lock, flags); @@ -663,7 +663,7 @@ static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry) int err; spin_lock_irqsave(&mgr->imap_lock, flags); - if ((0 == entry->addr) && (mgr->init_imap_added)) { + if (!entry->addr && mgr->init_imap_added) { input_mapper_delete(&mgr->imappers, mgr->init_imap, daio_map_op, mgr); mgr->init_imap_added = 0; @@ -707,7 +707,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr) *rdaio_mgr = NULL; daio_mgr = kzalloc(sizeof(*daio_mgr), GFP_KERNEL); - if (NULL == daio_mgr) + if (!daio_mgr) return -ENOMEM; err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw); @@ -718,7 +718,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr) spin_lock_init(&daio_mgr->imap_lock); INIT_LIST_HEAD(&daio_mgr->imappers); entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (NULL == entry) { + if (!entry) { err = -ENOMEM; goto error2; } diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index ad3e1d14446..0cf400f879f 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -168,7 +168,7 @@ static int src_get_rsc_ctrl_blk(void **rblk) *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; *rblk = blk; @@ -494,7 +494,7 @@ static int src_mgr_get_ctrl_blk(void **rblk) *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; *rblk = blk; @@ -515,7 +515,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk) *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; *rblk = blk; @@ -702,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk) *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; *rblk = blk; @@ -723,7 +723,7 @@ static int amixer_mgr_get_ctrl_blk(void **rblk) *rblk = NULL; /*blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; *rblk = blk;*/ @@ -909,7 +909,7 @@ static int dai_get_ctrl_blk(void **rblk) *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; *rblk = blk; @@ -958,7 +958,7 @@ static int dao_get_ctrl_blk(void **rblk) *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; *rblk = blk; @@ -1152,7 +1152,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; blk->i2sctl = hw_read_20kx(hw, I2SCTL); @@ -1808,7 +1808,7 @@ static int uaa_to_xfi(struct pci_dev *pci) /* By default, Hendrix card UAA Bar0 should be using memory... */ io_base = pci_resource_start(pci, 0); mem_base = ioremap(io_base, pci_resource_len(pci, 0)); - if (NULL == mem_base) + if (!mem_base) return -ENOENT; /* Read current mode from Mode Change Register */ @@ -1977,7 +1977,7 @@ static int hw_card_shutdown(struct hw *hw) hw->irq = -1; - if (NULL != ((void *)hw->mem_base)) + if (hw->mem_base) iounmap((void *)hw->mem_base); hw->mem_base = (unsigned long)NULL; @@ -2274,7 +2274,7 @@ int __devinit create_20k1_hw_obj(struct hw **rhw) *rhw = NULL; hw20k1 = kzalloc(sizeof(*hw20k1), GFP_KERNEL); - if (NULL == hw20k1) + if (!hw20k1) return -ENOMEM; spin_lock_init(&hw20k1->reg_20k1_lock); diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index dec46d04b04..b6b11bfe757 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -166,7 +166,7 @@ static int src_get_rsc_ctrl_blk(void **rblk) *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; *rblk = blk; @@ -492,7 +492,7 @@ static int src_mgr_get_ctrl_blk(void **rblk) *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; *rblk = blk; @@ -513,7 +513,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk) *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; *rblk = blk; @@ -702,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk) *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; *rblk = blk; @@ -891,7 +891,7 @@ static int dai_get_ctrl_blk(void **rblk) *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; *rblk = blk; @@ -941,7 +941,7 @@ static int dao_get_ctrl_blk(void **rblk) *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; *rblk = blk; @@ -1092,7 +1092,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) *rblk = NULL; blk = kzalloc(sizeof(*blk), GFP_KERNEL); - if (NULL == blk) + if (!blk) return -ENOMEM; for (i = 0; i < 8; i++) { @@ -1112,6 +1112,26 @@ static int daio_mgr_put_ctrl_blk(void *blk) return 0; } +/* Timer interrupt */ +static int set_timer_irq(struct hw *hw, int enable) +{ + hw_write_20kx(hw, GIE, enable ? IT_INT : 0); + return 0; +} + +static int set_timer_tick(struct hw *hw, unsigned int ticks) +{ + if (ticks) + ticks |= TIMR_IE | TIMR_IP; + hw_write_20kx(hw, TIMR, ticks); + return 0; +} + +static unsigned int get_wc(struct hw *hw) +{ + return hw_read_20kx(hw, WC); +} + /* Card hardware initialization block */ struct dac_conf { unsigned int msr; /* master sample rate in rsrs */ @@ -1841,6 +1861,22 @@ static int hw_have_digit_io_switch(struct hw *hw) return 0; } +static irqreturn_t ct_20k2_interrupt(int irq, void *dev_id) +{ + struct hw *hw = dev_id; + unsigned int status; + + status = hw_read_20kx(hw, GIP); + if (!status) + return IRQ_NONE; + + if (hw->irq_callback) + hw->irq_callback(hw->irq_callback_data, status); + + hw_write_20kx(hw, GIP, status); + return IRQ_HANDLED; +} + static int hw_card_start(struct hw *hw) { int err = 0; @@ -1868,7 +1904,7 @@ static int hw_card_start(struct hw *hw) hw->io_base = pci_resource_start(hw->pci, 2); hw->mem_base = (unsigned long)ioremap(hw->io_base, pci_resource_len(hw->pci, 2)); - if (NULL == (void *)hw->mem_base) { + if (!hw->mem_base) { err = -ENOENT; goto error2; } @@ -1879,12 +1915,15 @@ static int hw_card_start(struct hw *hw) set_field(&gctl, GCTL_UAA, 0); hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); - /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED, - atc->chip_details->nm_card, hw))) { - goto error3; + if (hw->irq < 0) { + err = request_irq(pci->irq, ct_20k2_interrupt, IRQF_SHARED, + "ctxfi", hw); + if (err < 0) { + printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq); + goto error2; + } + hw->irq = pci->irq; } - hw->irq = pci->irq; - */ pci_set_master(pci); @@ -1923,7 +1962,7 @@ static int hw_card_shutdown(struct hw *hw) hw->irq = -1; - if (NULL != ((void *)hw->mem_base)) + if (hw->mem_base) iounmap((void *)hw->mem_base); hw->mem_base = (unsigned long)NULL; @@ -1972,7 +2011,7 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl); /* Reset all global pending interrupts */ - hw_write_20kx(hw, INTERRUPT_GIE, 0); + hw_write_20kx(hw, GIE, 0); /* Reset all SRC pending interrupts */ hw_write_20kx(hw, SRC_IP, 0); @@ -2149,6 +2188,10 @@ static struct hw ct20k2_preset __devinitdata = { .daio_mgr_set_imapnxt = daio_mgr_set_imapnxt, .daio_mgr_set_imapaddr = daio_mgr_set_imapaddr, .daio_mgr_commit_write = daio_mgr_commit_write, + + .set_timer_irq = set_timer_irq, + .set_timer_tick = set_timer_tick, + .get_wc = get_wc, }; int __devinit create_20k2_hw_obj(struct hw **rhw) diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index f26d7cd9db9..15c1e7271ea 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -654,7 +654,7 @@ ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new) int err; kctl = snd_ctl_new1(new, mixer->atc); - if (NULL == kctl) + if (!kctl) return -ENOMEM; if (SNDRV_CTL_ELEM_IFACE_PCM == kctl->id.iface) @@ -837,17 +837,17 @@ static int ct_mixer_get_mem(struct ct_mixer **rmixer) *rmixer = NULL; /* Allocate mem for mixer obj */ mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); - if (NULL == mixer) + if (!mixer) return -ENOMEM; mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM), GFP_KERNEL); - if (NULL == mixer->amixers) { + if (!mixer->amixers) { err = -ENOMEM; goto error1; } mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL); - if (NULL == mixer->sums) { + if (!mixer->sums) { err = -ENOMEM; goto error2; } diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 60ea23180ac..d0dc227fbdd 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -97,7 +97,7 @@ static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm) { struct ct_atc_pcm *apcm = atc_pcm; - if (NULL == apcm->substream) + if (!apcm->substream) return; snd_pcm_period_elapsed(apcm->substream); @@ -123,7 +123,7 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream) int err; apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); - if (NULL == apcm) + if (!apcm) return -ENOMEM; apcm->substream = substream; @@ -271,7 +271,7 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream) int err; apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); - if (NULL == apcm) + if (!apcm) return -ENOMEM; apcm->started = 0; diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c index 889c495bb7d..7dfaf67344d 100644 --- a/sound/pci/ctxfi/ctresource.c +++ b/sound/pci/ctxfi/ctresource.c @@ -144,7 +144,7 @@ int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw) rsc->msr = msr; rsc->hw = hw; rsc->ops = &rsc_generic_ops; - if (NULL == hw) { + if (!hw) { rsc->ctrl_blk = NULL; return 0; } @@ -216,7 +216,7 @@ int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type, mgr->type = NUM_RSCTYP; mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL); - if (NULL == mgr->rscs) + if (!mgr->rscs) return -ENOMEM; switch (type) { diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c index df43a5cd393..c749fa72088 100644 --- a/sound/pci/ctxfi/ctsrc.c +++ b/sound/pci/ctxfi/ctsrc.c @@ -441,7 +441,7 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc) else src = kzalloc(sizeof(*src), GFP_KERNEL); - if (NULL == src) { + if (!src) { err = -ENOMEM; goto error1; } @@ -550,7 +550,7 @@ int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr) *rsrc_mgr = NULL; src_mgr = kzalloc(sizeof(*src_mgr), GFP_KERNEL); - if (NULL == src_mgr) + if (!src_mgr) return -ENOMEM; err = rsc_mgr_init(&src_mgr->mgr, SRC, SRC_RESOURCE_NUM, hw); @@ -679,7 +679,7 @@ static int srcimp_rsc_init(struct srcimp *srcimp, /* Reserve memory for imapper nodes */ srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr, GFP_KERNEL); - if (NULL == srcimp->imappers) { + if (!srcimp->imappers) { err = -ENOMEM; goto error1; } @@ -833,7 +833,7 @@ int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr) *rsrcimp_mgr = NULL; srcimp_mgr = kzalloc(sizeof(*srcimp_mgr), GFP_KERNEL); - if (NULL == srcimp_mgr) + if (!srcimp_mgr) return -ENOMEM; err = rsc_mgr_init(&srcimp_mgr->mgr, SRCIMP, SRCIMP_RESOURCE_NUM, hw); @@ -844,7 +844,7 @@ int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr) spin_lock_init(&srcimp_mgr->imap_lock); INIT_LIST_HEAD(&srcimp_mgr->imappers); entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (NULL == entry) { + if (!entry) { err = -ENOMEM; goto error2; } diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index 67665a7e43c..6b78752e950 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -60,7 +60,7 @@ get_vm_block(struct ct_vm *vm, unsigned int size) } block = kzalloc(sizeof(*block), GFP_KERNEL); - if (NULL == block) + if (!block) goto out; block->addr = entry->addr; @@ -181,7 +181,7 @@ int ct_vm_create(struct ct_vm **rvm) *rvm = NULL; vm = kzalloc(sizeof(*vm), GFP_KERNEL); - if (NULL == vm) + if (!vm) return -ENOMEM; mutex_init(&vm->lock); @@ -189,7 +189,7 @@ int ct_vm_create(struct ct_vm **rvm) /* Allocate page table pages */ for (i = 0; i < CT_PTP_NUM; i++) { vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (NULL == vm->ptp[i]) + if (!vm->ptp[i]) break; } if (!i) { diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index da2065cd2c0..1305f7ca02c 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -950,7 +950,7 @@ static int __devinit snd_echo_new_pcm(struct echoaudio *chip) Control interface ******************************************************************************/ -#ifndef ECHOCARD_HAS_VMIXER +#if !defined(ECHOCARD_HAS_VMIXER) || defined(ECHOCARD_HAS_LINE_OUT_GAIN) /******************* PCM output volume *******************/ static int snd_echo_output_gain_info(struct snd_kcontrol *kcontrol, @@ -1003,6 +1003,19 @@ static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol, return changed; } +#ifdef ECHOCARD_HAS_LINE_OUT_GAIN +/* On the Mia this one controls the line-out volume */ +static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = { + .name = "Line Playback Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = snd_echo_output_gain_info, + .get = snd_echo_output_gain_get, + .put = snd_echo_output_gain_put, + .tlv = {.p = db_scale_output_gain}, +}; +#else static struct snd_kcontrol_new snd_echo_pcm_output_gain __devinitdata = { .name = "PCM Playback Volume", .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1012,9 +1025,10 @@ static struct snd_kcontrol_new snd_echo_pcm_output_gain __devinitdata = { .put = snd_echo_output_gain_put, .tlv = {.p = db_scale_output_gain}, }; - #endif +#endif /* !ECHOCARD_HAS_VMIXER || ECHOCARD_HAS_LINE_OUT_GAIN */ + #ifdef ECHOCARD_HAS_INPUT_GAIN @@ -2030,10 +2044,18 @@ static int __devinit snd_echo_probe(struct pci_dev *pci, snd_echo_vmixer.count = num_pipes_out(chip) * num_busses_out(chip); if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip))) < 0) goto ctl_error; -#else - if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_pcm_output_gain, chip))) < 0) +#ifdef ECHOCARD_HAS_LINE_OUT_GAIN + err = snd_ctl_add(chip->card, + snd_ctl_new1(&snd_echo_line_output_gain, chip)); + if (err < 0) goto ctl_error; #endif +#else /* ECHOCARD_HAS_VMIXER */ + err = snd_ctl_add(chip->card, + snd_ctl_new1(&snd_echo_pcm_output_gain, chip)); + if (err < 0) + goto ctl_error; +#endif /* ECHOCARD_HAS_VMIXER */ #ifdef ECHOCARD_HAS_INPUT_GAIN if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip))) < 0) diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c index f3b9b45c9c1..f05c8c097aa 100644 --- a/sound/pci/echoaudio/mia.c +++ b/sound/pci/echoaudio/mia.c @@ -29,6 +29,7 @@ #define ECHOCARD_HAS_ADAT FALSE #define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 #define ECHOCARD_HAS_MIDI +#define ECHOCARD_HAS_LINE_OUT_GAIN /* Pipe indexes */ #define PX_ANALOG_OUT 0 /* 8 */ diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 04438f1d682..55545e0818b 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -46,6 +46,20 @@ config SND_HDA_INPUT_JACK Say Y here to enable the jack plugging notification via input layer. +config SND_HDA_PATCH_LOADER + bool "Support initialization patch loading for HD-audio" + depends on EXPERIMENTAL + select FW_LOADER + select SND_HDA_HWDEP + select SND_HDA_RECONFIG + help + Say Y here to allow the HD-audio driver to load a pseudo + firmware file ("patch") for overriding the BIOS setup at + start up. The "patch" file can be specified via patch module + option, such as patch=hda-init. + + This option turns on hwdep and reconfig features automatically. + config SND_HDA_CODEC_REALTEK bool "Build Realtek HD-audio codec support" default y @@ -134,6 +148,19 @@ config SND_HDA_ELD def_bool y depends on SND_HDA_CODEC_INTELHDMI +config SND_HDA_CODEC_CIRRUS + bool "Build Cirrus Logic codec support" + depends on SND_HDA_INTEL + default y + help + Say Y here to include Cirrus Logic codec support in + snd-hda-intel driver, such as CS4206. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-cirrus. + This module is automatically loaded at probing. + config SND_HDA_CODEC_CONEXANT bool "Build Conexant HD-audio codec support" default y diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index e3081d4586c..315a1c4f899 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -13,6 +13,7 @@ snd-hda-codec-analog-objs := patch_analog.o snd-hda-codec-idt-objs := patch_sigmatel.o snd-hda-codec-si3054-objs := patch_si3054.o snd-hda-codec-atihdmi-objs := patch_atihdmi.o +snd-hda-codec-cirrus-objs := patch_cirrus.o snd-hda-codec-ca0110-objs := patch_ca0110.o snd-hda-codec-conexant-objs := patch_conexant.o snd-hda-codec-via-objs := patch_via.o @@ -41,6 +42,9 @@ endif ifdef CONFIG_SND_HDA_CODEC_ATIHDMI obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o endif +ifdef CONFIG_SND_HDA_CODEC_CIRRUS +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o +endif ifdef CONFIG_SND_HDA_CODEC_CA0110 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o endif diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index b0275a05087..3f51a981e60 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -24,6 +24,7 @@ #include <linux/workqueue.h> #include <sound/core.h> #include "hda_beep.h" +#include "hda_local.h" enum { DIGBEEP_HZ_STEP = 46875, /* 46.875 Hz */ @@ -118,6 +119,9 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) struct hda_beep *beep; int err; + if (!snd_hda_get_bool_hint(codec, "beep")) + return 0; /* disabled explicitly */ + beep = kzalloc(sizeof(*beep), GFP_KERNEL); if (beep == NULL) return -ENOMEM; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index c7df01b72ca..af989f660cc 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -44,6 +44,7 @@ struct hda_vendor_id { /* codec vendor labels */ static struct hda_vendor_id hda_vendor_ids[] = { { 0x1002, "ATI" }, + { 0x1013, "Cirrus Logic" }, { 0x1057, "Motorola" }, { 0x1095, "Silicon Image" }, { 0x10de, "Nvidia" }, @@ -150,7 +151,14 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct, { u32 val; - val = (u32)(codec->addr & 0x0f) << 28; + if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) || + (verb & ~0xfff) || (parm & ~0xffff)) { + printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n", + codec->addr, direct, nid, verb, parm); + return ~0; + } + + val = (u32)codec->addr << 28; val |= (u32)direct << 27; val |= (u32)nid << 20; val |= verb << 8; @@ -167,6 +175,9 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, struct hda_bus *bus = codec->bus; int err; + if (cmd == ~0) + return -1; + if (res) *res = -1; again: @@ -291,11 +302,20 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, unsigned int parm; int i, conn_len, conns; unsigned int shift, num_elems, mask; + unsigned int wcaps; hda_nid_t prev_nid; if (snd_BUG_ON(!conn_list || max_conns <= 0)) return -EINVAL; + wcaps = get_wcaps(codec, nid); + if (!(wcaps & AC_WCAP_CONN_LIST) && + get_wcaps_type(wcaps) != AC_WID_VOL_KNB) { + snd_printk(KERN_WARNING "hda_codec: " + "connection list not available for 0x%x\n", nid); + return -EINVAL; + } + parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); if (parm & AC_CLIST_LONG) { /* long form */ @@ -316,6 +336,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, /* single connection */ parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_LIST, 0); + if (parm == -1 && codec->bus->rirb_error) + return -EIO; conn_list[0] = parm & mask; return 1; } @@ -327,9 +349,12 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, int range_val; hda_nid_t val, n; - if (i % num_elems == 0) + if (i % num_elems == 0) { parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_LIST, i); + if (parm == -1 && codec->bus->rirb_error) + return -EIO; + } range_val = !!(parm & (1 << (shift-1))); /* ranges */ val = parm & mask; if (val == 0) { @@ -727,8 +752,7 @@ static int read_pin_defaults(struct hda_codec *codec) for (i = 0; i < codec->num_nodes; i++, nid++) { struct hda_pincfg *pin; unsigned int wcaps = get_wcaps(codec, nid); - unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >> - AC_WCAP_TYPE_SHIFT; + unsigned int wid_type = get_wcaps_type(wcaps); if (wid_type != AC_WID_PIN) continue; pin = snd_array_new(&codec->init_pins); @@ -891,7 +915,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, * Returns 0 if successful, or a negative error code. */ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, - int do_init, struct hda_codec **codecp) + struct hda_codec **codecp) { struct hda_codec *codec; char component[31]; @@ -984,11 +1008,6 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr codec->afg ? codec->afg : codec->mfg, AC_PWRST_D0); - if (do_init) { - err = snd_hda_codec_configure(codec); - if (err < 0) - goto error; - } snd_hda_codec_proc_new(codec); snd_hda_create_hwdep(codec); @@ -1042,6 +1061,7 @@ int snd_hda_codec_configure(struct hda_codec *codec) err = init_unsol_queue(codec->bus); return err; } +EXPORT_SYMBOL_HDA(snd_hda_codec_configure); /** * snd_hda_codec_setup_stream - set up the codec for streaming @@ -2356,16 +2376,20 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, hda_nid_t nid; int i; - snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE, + /* this delay seems necessary to avoid click noise at power-down */ + if (power_state == AC_PWRST_D3) + msleep(100); + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); - msleep(10); /* partial workaround for "azx_get_response timeout" */ + /* partial workaround for "azx_get_response timeout" */ + if (power_state == AC_PWRST_D0) + msleep(10); nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) { unsigned int wcaps = get_wcaps(codec, nid); if (wcaps & AC_WCAP_POWER) { - unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >> - AC_WCAP_TYPE_SHIFT; + unsigned int wid_type = get_wcaps_type(wcaps); if (power_state == AC_PWRST_D3 && wid_type == AC_WID_PIN) { unsigned int pincap; @@ -2573,7 +2597,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, case 20: case 24: case 32: - if (maxbps >= 32) + if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE) val |= 0x40; else if (maxbps >= 24) val |= 0x30; @@ -2700,11 +2724,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, bps = 20; } } - else if (streams == AC_SUPFMT_FLOAT32) { - /* should be exclusive */ + if (streams & AC_SUPFMT_FLOAT32) { formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; - bps = 32; - } else if (streams == AC_SUPFMT_AC3) { + if (!bps) + bps = 32; + } + if (streams == AC_SUPFMT_AC3) { /* should be exclusive */ /* temporary hack: we have still no proper support * for the direct AC3 stream... @@ -3102,7 +3127,7 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec, tbl = q; if (tbl->value >= 0 && tbl->value < num_configs) { -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE char tmp[10]; const char *model = NULL; if (models) @@ -3655,8 +3680,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, 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; + unsigned int wid_type = get_wcaps_type(wid_caps); unsigned int def_conf; short assoc, loc; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 1b75f28ed09..99552fb5f75 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -830,7 +830,8 @@ enum { int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp, struct hda_bus **busp); int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, - int do_init, struct hda_codec **codecp); + struct hda_codec **codecp); +int snd_hda_codec_configure(struct hda_codec *codec); /* * low level functions @@ -938,6 +939,13 @@ static inline void snd_hda_power_down(struct hda_codec *codec) {} #define snd_hda_codec_needs_resume(codec) 1 #endif +#ifdef CONFIG_SND_HDA_PATCH_LOADER +/* + * patch firmware + */ +int snd_hda_load_patch(struct hda_bus *bus, const char *patch); +#endif + /* * Codec modularization */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1d5797a9668..b36f6c5a92d 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -121,11 +121,17 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid if (node == NULL) return -ENOMEM; node->nid = nid; - nconns = snd_hda_get_connections(codec, nid, conn_list, - HDA_MAX_CONNECTIONS); - if (nconns < 0) { - kfree(node); - return nconns; + node->wid_caps = get_wcaps(codec, nid); + node->type = get_wcaps_type(node->wid_caps); + if (node->wid_caps & AC_WCAP_CONN_LIST) { + nconns = snd_hda_get_connections(codec, nid, conn_list, + HDA_MAX_CONNECTIONS); + if (nconns < 0) { + kfree(node); + return nconns; + } + } else { + nconns = 0; } if (nconns <= ARRAY_SIZE(node->slist)) node->conn_list = node->slist; @@ -140,8 +146,6 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid } memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t)); node->nconns = nconns; - node->wid_caps = get_wcaps(codec, nid); - node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; if (node->type == AC_WID_PIN) { node->pin_caps = snd_hda_query_pin_caps(codec, node->nid); diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 6812fbe80fa..cc24e6721d7 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -24,6 +24,7 @@ #include <linux/compat.h> #include <linux/mutex.h> #include <linux/ctype.h> +#include <linux/firmware.h> #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" @@ -312,12 +313,8 @@ static ssize_t init_verbs_show(struct device *dev, return len; } -static ssize_t init_verbs_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int parse_init_verbs(struct hda_codec *codec, const char *buf) { - struct snd_hwdep *hwdep = dev_get_drvdata(dev); - struct hda_codec *codec = hwdep->private_data; struct hda_verb *v; int nid, verb, param; @@ -331,6 +328,18 @@ static ssize_t init_verbs_store(struct device *dev, v->nid = nid; v->verb = verb; v->param = param; + return 0; +} + +static ssize_t init_verbs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); + struct hda_codec *codec = hwdep->private_data; + int err = parse_init_verbs(codec, buf); + if (err < 0) + return err; return count; } @@ -376,19 +385,15 @@ static void remove_trail_spaces(char *str) #define MAX_HINTS 1024 -static ssize_t hints_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int parse_hints(struct hda_codec *codec, const char *buf) { - struct snd_hwdep *hwdep = dev_get_drvdata(dev); - struct hda_codec *codec = hwdep->private_data; char *key, *val; struct hda_hint *hint; while (isspace(*buf)) buf++; if (!*buf || *buf == '#' || *buf == '\n') - return count; + return 0; if (*buf == '=') return -EINVAL; key = kstrndup_noeol(buf, 1024); @@ -411,7 +416,7 @@ static ssize_t hints_store(struct device *dev, kfree(hint->key); hint->key = key; hint->val = val; - return count; + return 0; } /* allocate a new hint entry */ if (codec->hints.used >= MAX_HINTS) @@ -424,6 +429,18 @@ static ssize_t hints_store(struct device *dev, } hint->key = key; hint->val = val; + return 0; +} + +static ssize_t hints_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); + struct hda_codec *codec = hwdep->private_data; + int err = parse_hints(codec, buf); + if (err < 0) + return err; return count; } @@ -469,20 +486,24 @@ static ssize_t driver_pin_configs_show(struct device *dev, #define MAX_PIN_CONFIGS 32 -static ssize_t user_pin_configs_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int parse_user_pin_configs(struct hda_codec *codec, const char *buf) { - struct snd_hwdep *hwdep = dev_get_drvdata(dev); - struct hda_codec *codec = hwdep->private_data; int nid, cfg; - int err; if (sscanf(buf, "%i %i", &nid, &cfg) != 2) return -EINVAL; if (!nid) return -EINVAL; - err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); + return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); +} + +static ssize_t user_pin_configs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); + struct hda_codec *codec = hwdep->private_data; + int err = parse_user_pin_configs(codec, buf); if (err < 0) return err; return count; @@ -553,3 +574,180 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint); #endif /* CONFIG_SND_HDA_RECONFIG */ + +#ifdef CONFIG_SND_HDA_PATCH_LOADER + +/* parser mode */ +enum { + LINE_MODE_NONE, + LINE_MODE_CODEC, + LINE_MODE_MODEL, + LINE_MODE_PINCFG, + LINE_MODE_VERB, + LINE_MODE_HINT, + NUM_LINE_MODES, +}; + +static inline int strmatch(const char *a, const char *b) +{ + return strnicmp(a, b, strlen(b)) == 0; +} + +/* parse the contents after the line "[codec]" + * accept only the line with three numbers, and assign the current codec + */ +static void parse_codec_mode(char *buf, struct hda_bus *bus, + struct hda_codec **codecp) +{ + unsigned int vendorid, subid, caddr; + struct hda_codec *codec; + + *codecp = NULL; + if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) { + list_for_each_entry(codec, &bus->codec_list, list) { + if (codec->addr == caddr) { + *codecp = codec; + break; + } + } + } +} + +/* parse the contents after the other command tags, [pincfg], [verb], + * [hint] and [model] + * just pass to the sysfs helper (only when any codec was specified) + */ +static void parse_pincfg_mode(char *buf, struct hda_bus *bus, + struct hda_codec **codecp) +{ + if (!*codecp) + return; + parse_user_pin_configs(*codecp, buf); +} + +static void parse_verb_mode(char *buf, struct hda_bus *bus, + struct hda_codec **codecp) +{ + if (!*codecp) + return; + parse_init_verbs(*codecp, buf); +} + +static void parse_hint_mode(char *buf, struct hda_bus *bus, + struct hda_codec **codecp) +{ + if (!*codecp) + return; + parse_hints(*codecp, buf); +} + +static void parse_model_mode(char *buf, struct hda_bus *bus, + struct hda_codec **codecp) +{ + if (!*codecp) + return; + kfree((*codecp)->modelname); + (*codecp)->modelname = kstrdup(buf, GFP_KERNEL); +} + +struct hda_patch_item { + const char *tag; + void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc); +}; + +static struct hda_patch_item patch_items[NUM_LINE_MODES] = { + [LINE_MODE_CODEC] = { "[codec]", parse_codec_mode }, + [LINE_MODE_MODEL] = { "[model]", parse_model_mode }, + [LINE_MODE_VERB] = { "[verb]", parse_verb_mode }, + [LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode }, + [LINE_MODE_HINT] = { "[hint]", parse_hint_mode }, +}; + +/* check the line starting with '[' -- change the parser mode accodingly */ +static int parse_line_mode(char *buf, struct hda_bus *bus) +{ + int i; + for (i = 0; i < ARRAY_SIZE(patch_items); i++) { + if (!patch_items[i].tag) + continue; + if (strmatch(buf, patch_items[i].tag)) + return i; + } + return LINE_MODE_NONE; +} + +/* copy one line from the buffer in fw, and update the fields in fw + * return zero if it reaches to the end of the buffer, or non-zero + * if successfully copied a line + * + * the spaces at the beginning and the end of the line are stripped + */ +static int get_line_from_fw(char *buf, int size, struct firmware *fw) +{ + int len; + const char *p = fw->data; + while (isspace(*p) && fw->size) { + p++; + fw->size--; + } + if (!fw->size) + return 0; + if (size < fw->size) + size = fw->size; + + for (len = 0; len < fw->size; len++) { + if (!*p) + break; + if (*p == '\n') { + p++; + len++; + break; + } + if (len < size) + *buf++ = *p++; + } + *buf = 0; + fw->size -= len; + fw->data = p; + remove_trail_spaces(buf); + return 1; +} + +/* + * load a "patch" firmware file and parse it + */ +int snd_hda_load_patch(struct hda_bus *bus, const char *patch) +{ + int err; + const struct firmware *fw; + struct firmware tmp; + char buf[128]; + struct hda_codec *codec; + int line_mode; + struct device *dev = bus->card->dev; + + if (snd_BUG_ON(!dev)) + return -ENODEV; + err = request_firmware(&fw, patch, dev); + if (err < 0) { + printk(KERN_ERR "hda-codec: Cannot load the patch '%s'\n", + patch); + return err; + } + + tmp = *fw; + line_mode = LINE_MODE_NONE; + codec = NULL; + while (get_line_from_fw(buf, sizeof(buf) - 1, &tmp)) { + if (!*buf || *buf == '#' || *buf == '\n') + continue; + if (*buf == '[') + line_mode = parse_line_mode(buf, bus); + else if (patch_items[line_mode].parser) + patch_items[line_mode].parser(buf, bus, &codec); + } + release_firmware(fw); + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_load_patch); +#endif /* CONFIG_SND_HDA_PATCH_LOADER */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 175f07a381b..c9ad182e1b4 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -61,6 +61,9 @@ static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int probe_only[SNDRV_CARDS]; static int single_cmd; static int enable_msi; +#ifdef CONFIG_SND_HDA_PATCH_LOADER +static char *patch[SNDRV_CARDS]; +#endif module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); @@ -84,6 +87,10 @@ MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " "(for debugging only)."); module_param(enable_msi, int, 0444); MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); +#ifdef CONFIG_SND_HDA_PATCH_LOADER +module_param_array(patch, charp, NULL, 0444); +MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface."); +#endif #ifdef CONFIG_SND_HDA_POWER_SAVE static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; @@ -1331,8 +1338,7 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = { [AZX_DRIVER_TERA] = 1, }; -static int __devinit azx_codec_create(struct azx *chip, const char *model, - int no_init) +static int __devinit azx_codec_create(struct azx *chip, const char *model) { struct hda_bus_template bus_temp; int c, codecs, err; @@ -1391,7 +1397,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, for (c = 0; c < max_slots; c++) { if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) { struct hda_codec *codec; - err = snd_hda_codec_new(chip->bus, c, !no_init, &codec); + err = snd_hda_codec_new(chip->bus, c, &codec); if (err < 0) continue; codecs++; @@ -1401,7 +1407,16 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, snd_printk(KERN_ERR SFX "no codecs initialized\n"); return -ENXIO; } + return 0; +} +/* configure each codec instance */ +static int __devinit azx_codec_configure(struct azx *chip) +{ + struct hda_codec *codec; + list_for_each_entry(codec, &chip->bus->codec_list, list) { + snd_hda_codec_configure(codec); + } return 0; } @@ -2284,6 +2299,31 @@ static void __devinit check_probe_mask(struct azx *chip, int dev) } } +/* + * white-list for enable_msi + */ +static struct snd_pci_quirk msi_white_list[] __devinitdata = { + SND_PCI_QUIRK(0x103c, 0x30f7, "HP Pavilion dv4t-1300", 1), + SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1), + {} +}; + +static void __devinit check_msi(struct azx *chip) +{ + const struct snd_pci_quirk *q; + + chip->msi = enable_msi; + if (chip->msi) + return; + q = snd_pci_quirk_lookup(chip->pci, msi_white_list); + if (q) { + printk(KERN_INFO + "hda_intel: msi for device %04x:%04x set to %d\n", + q->subvendor, q->subdevice, q->value); + chip->msi = q->value; + } +} + /* * constructor @@ -2318,7 +2358,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, chip->pci = pci; chip->irq = -1; chip->driver_type = driver_type; - chip->msi = enable_msi; + check_msi(chip); chip->dev_index = dev; INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work); @@ -2526,15 +2566,32 @@ static int __devinit azx_probe(struct pci_dev *pci, return err; } + /* set this here since it's referred in snd_hda_load_patch() */ + snd_card_set_dev(card, &pci->dev); + err = azx_create(card, pci, dev, pci_id->driver_data, &chip); if (err < 0) goto out_free; card->private_data = chip; /* create codec instances */ - err = azx_codec_create(chip, model[dev], probe_only[dev]); + err = azx_codec_create(chip, model[dev]); if (err < 0) goto out_free; +#ifdef CONFIG_SND_HDA_PATCH_LOADER + if (patch[dev]) { + snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n", + patch[dev]); + err = snd_hda_load_patch(chip->bus, patch[dev]); + if (err < 0) + goto out_free; + } +#endif + if (!probe_only[dev]) { + err = azx_codec_configure(chip); + if (err < 0) + goto out_free; + } /* create PCM streams */ err = snd_hda_build_pcms(chip->bus); @@ -2546,8 +2603,6 @@ static int __devinit azx_probe(struct pci_dev *pci, if (err < 0) goto out_free; - snd_card_set_dev(card, &pci->dev); - err = snd_card_register(card); if (err < 0) goto out_free; @@ -2649,11 +2704,15 @@ static struct pci_device_id azx_ids[] = { /* this entry seems still valid -- i.e. without emu20kx chip */ { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC }, #endif - /* AMD Generic, PCI class code and Vendor ID for HD Audio */ + /* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, .class_mask = 0xffffff, .driver_data = AZX_DRIVER_GENERIC }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_ANY_ID), + .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, + .class_mask = 0xffffff, + .driver_data = AZX_DRIVER_GENERIC }, { 0, } }; MODULE_DEVICE_TABLE(pci, azx_ids); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 83349013b4d..5f1dcc59002 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -99,7 +99,6 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, int snd_hda_add_vmaster(struct hda_codec *codec, char *name, unsigned int *tlv, const char **slaves); int snd_hda_codec_reset(struct hda_codec *codec); -int snd_hda_codec_configure(struct hda_codec *codec); /* amp value bits */ #define HDA_AMP_MUTE 0x80 @@ -408,6 +407,19 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid) return codec->wcaps[nid - codec->start_nid]; } +/* get the widget type from widget capability bits */ +#define get_wcaps_type(wcaps) (((wcaps) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT) + +static inline unsigned int get_wcaps_channels(u32 wcaps) +{ + unsigned int chans; + + chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13; + chans = ((chans << 1) | 1) + 1; + + return chans; +} + 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); diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 418c5d1bada..95f24e4729f 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -508,17 +508,14 @@ static void print_codec_info(struct snd_info_entry *entry, 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; + unsigned int wid_type = get_wcaps_type(wid_caps); 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); if (wid_caps & AC_WCAP_STEREO) { - unsigned int chans; - chans = (wid_caps & AC_WCAP_CHAN_CNT_EXT) >> 13; - chans = ((chans << 1) | 1) + 1; + unsigned int chans = get_wcaps_channels(wid_caps); if (chans == 2) snd_iprintf(buffer, " Stereo"); else diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 403588c6e3f..2d603f6aba6 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -2982,7 +2982,8 @@ static int patch_ad1988(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST, ad1988_models, ad1988_cfg_tbl); if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); board_config = AD1988_AUTO; } @@ -3702,19 +3703,29 @@ static struct hda_amp_list ad1884a_loopbacks[] = { * Port F: Internal speakers */ -static struct hda_input_mux ad1884a_laptop_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, /* port-B */ - { "Internal Mic", 0x1 }, /* port-C */ - { "Dock Mic", 0x4 }, /* port-E */ - { "Mix", 0x3 }, - }, -}; +static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + int mute = (!ucontrol->value.integer.value[0] && + !ucontrol->value.integer.value[1]); + /* toggle GPIO1 according to the mute state */ + snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + mute ? 0x02 : 0x0); + return ret; +} static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = ad1884a_mobile_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), + }, HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), @@ -3729,36 +3740,9 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT), 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 */ }; -static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - int mute = (!ucontrol->value.integer.value[0] && - !ucontrol->value.integer.value[1]); - /* toggle GPIO1 according to the mute state */ - snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - mute ? 0x02 : 0x0); - return ret; -} - static struct snd_kcontrol_new ad1884a_mobile_mixers[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ @@ -3828,6 +3812,63 @@ static int ad1884a_hp_init(struct hda_codec *codec) return 0; } +/* mute internal speaker if HP or docking HP is plugged */ +static void ad1884a_laptop_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0); + present &= AC_PINSENSE_PRESENCE; + if (!present) { + present = snd_hda_codec_read(codec, 0x12, 0, + AC_VERB_GET_PIN_SENSE, 0); + present &= AC_PINSENSE_PRESENCE; + } + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, + present ? 0x00 : 0x02); +} + +/* switch to external mic if plugged */ +static void ad1884a_laptop_automic(struct hda_codec *codec) +{ + unsigned int idx; + + if (snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & + AC_PINSENSE_PRESENCE) + idx = 0; + else if (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & + AC_PINSENSE_PRESENCE) + idx = 4; + else + idx = 1; + snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx); +} + +/* unsolicited event for HP jack sensing */ +static void ad1884a_laptop_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case AD1884A_HP_EVENT: + ad1884a_laptop_automute(codec); + break; + case AD1884A_MIC_EVENT: + ad1884a_laptop_automic(codec); + break; + } +} + +/* initialize jack-sensing, too */ +static int ad1884a_laptop_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1884a_laptop_automute(codec); + ad1884a_laptop_automic(codec); + return 0; +} + /* additional verbs for laptop model */ static struct hda_verb ad1884a_laptop_verbs[] = { /* Port-A (HP) pin - always unmuted */ @@ -3844,11 +3885,19 @@ static struct hda_verb ad1884a_laptop_verbs[] = { {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ + /* Port-D (docking line-out) pin - default unmuted */ + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* analog mix */ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* unsolicited event for pin-sense */ {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, + {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, + /* allow to touch GPIO1 (for mute control) */ + {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ { } /* end */ }; @@ -3983,6 +4032,127 @@ static int ad1984a_thinkpad_init(struct hda_codec *codec) } /* + * HP Touchsmart + * port-A (0x11) - front hp-out + * port-B (0x14) - unused + * port-C (0x15) - unused + * port-D (0x12) - rear line out + * port-E (0x1c) - front mic-in + * port-F (0x16) - Internal speakers + * digital-mic (0x17) - Internal mic + */ + +static struct hda_verb ad1984a_touchsmart_verbs[] = { + /* DACs; unmute as default */ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ + /* Port-A (HP) mixer - route only from analog mixer */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-A pin */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Port-A (HP) pin - always unmuted */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Port-E (int speaker) mixer - route only from analog mixer */ + {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03}, + /* Port-E pin */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + /* Port-F (int speaker) mixer - route only from analog mixer */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-F pin */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Analog mixer; mute as default */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + /* Analog Mix output amp */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* capture sources */ + /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* unsolicited event for pin-sense */ + {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, + /* allow to touch GPIO1 (for mute control) */ + {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ + /* internal mic - dmic */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + /* set magic COEFs for dmic */ + {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, + {0x01, AC_VERB_SET_PROC_COEF, 0x08}, + { } /* end */ +}; + +static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), +/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = ad1884a_mobile_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), + }, + HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT), + { } /* end */ +}; + +/* switch to external mic if plugged */ +static void ad1984a_touchsmart_automic(struct hda_codec *codec) +{ + if (snd_hda_codec_read(codec, 0x1c, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000) { + snd_hda_codec_write(codec, 0x0c, 0, + AC_VERB_SET_CONNECT_SEL, 0x4); + } else { + snd_hda_codec_write(codec, 0x0c, 0, + AC_VERB_SET_CONNECT_SEL, 0x5); + } +} + + +/* unsolicited event for HP jack sensing */ +static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case AD1884A_HP_EVENT: + ad1884a_hp_automute(codec); + break; + case AD1884A_MIC_EVENT: + ad1984a_touchsmart_automic(codec); + break; + } +} + +/* initialize jack-sensing, too */ +static int ad1984a_touchsmart_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1884a_hp_automute(codec); + ad1984a_touchsmart_automic(codec); + return 0; +} + + +/* */ enum { @@ -3990,6 +4160,7 @@ enum { AD1884A_LAPTOP, AD1884A_MOBILE, AD1884A_THINKPAD, + AD1984A_TOUCHSMART, AD1884A_MODELS }; @@ -3998,6 +4169,7 @@ static const char *ad1884a_models[AD1884A_MODELS] = { [AD1884A_LAPTOP] = "laptop", [AD1884A_MOBILE] = "mobile", [AD1884A_THINKPAD] = "thinkpad", + [AD1984A_TOUCHSMART] = "touchsmart", }; static struct snd_pci_quirk ad1884a_cfg_tbl[] = { @@ -4008,7 +4180,9 @@ static struct snd_pci_quirk ad1884a_cfg_tbl[] = { SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP), SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP), SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP), + SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE), SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD), + SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART), {} }; @@ -4057,9 +4231,8 @@ static int patch_ad1884a(struct hda_codec *codec) spec->mixers[0] = ad1884a_laptop_mixers; spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1884a_laptop_capture_source; - codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; - codec->patch_ops.init = ad1884a_hp_init; + codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event; + codec->patch_ops.init = ad1884a_laptop_init; /* set the upper-limit for mixer amp to 0dB for avoiding the * possible damage by overloading */ @@ -4093,6 +4266,21 @@ static int patch_ad1884a(struct hda_codec *codec) codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event; codec->patch_ops.init = ad1984a_thinkpad_init; break; + case AD1984A_TOUCHSMART: + spec->mixers[0] = ad1984a_touchsmart_mixers; + spec->init_verbs[0] = ad1984a_touchsmart_verbs; + spec->multiout.dig_out_nid = 0; + codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event; + codec->patch_ops.init = ad1984a_touchsmart_init; + /* set the upper-limit for mixer amp to 0dB for avoiding the + * possible damage by overloading + */ + snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); + break; } return 0; diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c index 233e4778bba..fb684f00156 100644 --- a/sound/pci/hda/patch_atihdmi.c +++ b/sound/pci/hda/patch_atihdmi.c @@ -141,8 +141,7 @@ static int atihdmi_build_pcms(struct hda_codec *codec) /* FIXME: we must check ELD and change the PCM parameters dynamically */ chans = get_wcaps(codec, CVT_NID); - chans = (chans & AC_WCAP_CHAN_CNT_EXT) >> 13; - chans = ((chans << 1) | 1) + 1; + chans = get_wcaps_channels(chans); info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans; return 0; diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 019ca7cb56d..d08353d3bb7 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -459,8 +459,7 @@ static void parse_input(struct hda_codec *codec) nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) { unsigned int wcaps = get_wcaps(codec, nid); - unsigned int type = (wcaps & AC_WCAP_TYPE) >> - AC_WCAP_TYPE_SHIFT; + unsigned int type = get_wcaps_type(wcaps); if (type != AC_WID_AUD_IN) continue; if (snd_hda_get_connections(codec, nid, &pin, 1) != 1) diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c new file mode 100644 index 00000000000..8ba306856d3 --- /dev/null +++ b/sound/pci/hda/patch_cirrus.c @@ -0,0 +1,1194 @@ +/* + * HD audio interface patch for Cirrus Logic CS420x chip + * + * Copyright (c) 2009 Takashi Iwai <tiwai@suse.de> + * + * This driver 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 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 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/delay.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <sound/core.h> +#include "hda_codec.h" +#include "hda_local.h" + +/* + */ + +struct cs_spec { + int board_config; + struct auto_pin_cfg autocfg; + struct hda_multi_out multiout; + struct snd_kcontrol *vmaster_sw; + struct snd_kcontrol *vmaster_vol; + + hda_nid_t dac_nid[AUTO_CFG_MAX_OUTS]; + hda_nid_t slave_dig_outs[2]; + + unsigned int input_idx[AUTO_PIN_LAST]; + unsigned int capsrc_idx[AUTO_PIN_LAST]; + hda_nid_t adc_nid[AUTO_PIN_LAST]; + unsigned int adc_idx[AUTO_PIN_LAST]; + unsigned int num_inputs; + unsigned int cur_input; + unsigned int automic_idx; + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; + hda_nid_t dig_in; + + struct hda_bind_ctls *capture_bind[2]; + + unsigned int gpio_mask; + unsigned int gpio_dir; + unsigned int gpio_data; + + struct hda_pcm pcm_rec[2]; /* PCM information */ + + unsigned int hp_detect:1; + unsigned int mic_detect:1; +}; + +/* available models */ +enum { + CS420X_MBP55, + CS420X_AUTO, + CS420X_MODELS +}; + +/* Vendor-specific processing widget */ +#define CS420X_VENDOR_NID 0x11 +#define CS_DIG_OUT1_PIN_NID 0x10 +#define CS_DIG_OUT2_PIN_NID 0x15 +#define CS_DMIC1_PIN_NID 0x12 +#define CS_DMIC2_PIN_NID 0x0e + +/* coef indices */ +#define IDX_SPDIF_STAT 0x0000 +#define IDX_SPDIF_CTL 0x0001 +#define IDX_ADC_CFG 0x0002 +/* SZC bitmask, 4 modes below: + * 0 = immediate, + * 1 = digital immediate, analog zero-cross + * 2 = digtail & analog soft-ramp + * 3 = digital soft-ramp, analog zero-cross + */ +#define CS_COEF_ADC_SZC_MASK (3 << 0) +#define CS_COEF_ADC_MIC_SZC_MODE (3 << 0) /* SZC setup for mic */ +#define CS_COEF_ADC_LI_SZC_MODE (3 << 0) /* SZC setup for line-in */ +/* PGA mode: 0 = differential, 1 = signle-ended */ +#define CS_COEF_ADC_MIC_PGA_MODE (1 << 5) /* PGA setup for mic */ +#define CS_COEF_ADC_LI_PGA_MODE (1 << 6) /* PGA setup for line-in */ +#define IDX_DAC_CFG 0x0003 +/* SZC bitmask, 4 modes below: + * 0 = Immediate + * 1 = zero-cross + * 2 = soft-ramp + * 3 = soft-ramp on zero-cross + */ +#define CS_COEF_DAC_HP_SZC_MODE (3 << 0) /* nid 0x02 */ +#define CS_COEF_DAC_LO_SZC_MODE (3 << 2) /* nid 0x03 */ +#define CS_COEF_DAC_SPK_SZC_MODE (3 << 4) /* nid 0x04 */ + +#define IDX_BEEP_CFG 0x0004 +/* 0x0008 - test reg key */ +/* 0x0009 - 0x0014 -> 12 test regs */ +/* 0x0015 - visibility reg */ + + +static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) +{ + snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, idx); + return snd_hda_codec_read(codec, CS420X_VENDOR_NID, 0, + AC_VERB_GET_PROC_COEF, 0); +} + +static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, + unsigned int coef) +{ + snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, idx); + snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0, + AC_VERB_SET_PROC_COEF, coef); +} + + +#define HP_EVENT 1 +#define MIC_EVENT 2 + +/* + * PCM callbacks + */ +static int cs_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); +} + +static int cs_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static int cs_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + +/* + * Digital out + */ +static int cs_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int cs_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int cs_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, + format, substream); +} + +static int cs_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); +} + +/* + * Analog capture + */ +static int cs_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 cs_spec *spec = codec->spec; + spec->cur_adc = spec->adc_nid[spec->cur_input]; + 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 cs_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; + return 0; +} + +/* + */ +static struct hda_pcm_stream cs_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = cs_playback_pcm_open, + .prepare = cs_playback_pcm_prepare, + .cleanup = cs_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream cs_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .prepare = cs_capture_pcm_prepare, + .cleanup = cs_capture_pcm_cleanup + }, +}; + +static struct hda_pcm_stream cs_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = cs_dig_playback_pcm_open, + .close = cs_dig_playback_pcm_close, + .prepare = cs_dig_playback_pcm_prepare, + .cleanup = cs_dig_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream cs_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +static int cs_build_pcms(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->pcm_info = info; + codec->num_pcms = 0; + + info->name = "Cirrus Analog"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cs_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dac_nid[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = cs_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = + spec->adc_nid[spec->cur_input]; + codec->num_pcms++; + + if (!spec->multiout.dig_out_nid && !spec->dig_in) + return 0; + + info++; + info->name = "Cirrus Digital"; + info->pcm_type = spec->autocfg.dig_out_type[0]; + if (!info->pcm_type) + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->multiout.dig_out_nid) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + cs_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.dig_out_nid; + } + if (spec->dig_in) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + cs_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; + } + codec->num_pcms++; + + return 0; +} + +/* + * parse codec topology + */ + +static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin) +{ + hda_nid_t dac; + if (!pin) + return 0; + if (snd_hda_get_connections(codec, pin, &dac, 1) != 1) + return 0; + return dac; +} + +static int is_ext_mic(struct hda_codec *codec, unsigned int idx) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t pin = cfg->input_pins[idx]; + unsigned int val = snd_hda_query_pin_caps(codec, pin); + if (!(val & AC_PINCAP_PRES_DETECT)) + return 0; + val = snd_hda_codec_get_pincfg(codec, pin); + return (get_defcfg_connect(val) == AC_JACK_PORT_COMPLEX); +} + +static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin, + unsigned int *idxp) +{ + int i; + hda_nid_t nid; + + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + hda_nid_t pins[2]; + unsigned int type; + int j, nums; + type = (get_wcaps(codec, nid) & AC_WCAP_TYPE) + >> AC_WCAP_TYPE_SHIFT; + if (type != AC_WID_AUD_IN) + continue; + nums = snd_hda_get_connections(codec, nid, pins, + ARRAY_SIZE(pins)); + if (nums <= 0) + continue; + for (j = 0; j < nums; j++) { + if (pins[j] == pin) { + *idxp = j; + return nid; + } + } + } + return 0; +} + +static int is_active_pin(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int val; + val = snd_hda_codec_get_pincfg(codec, nid); + return (get_defcfg_connect(val) != AC_JACK_PORT_NONE); +} + +static int parse_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, extra_nids; + hda_nid_t dac; + + for (i = 0; i < cfg->line_outs; i++) { + dac = get_dac(codec, cfg->line_out_pins[i]); + if (!dac) + break; + spec->dac_nid[i] = dac; + } + spec->multiout.num_dacs = i; + spec->multiout.dac_nids = spec->dac_nid; + spec->multiout.max_channels = i * 2; + + /* add HP and speakers */ + extra_nids = 0; + for (i = 0; i < cfg->hp_outs; i++) { + dac = get_dac(codec, cfg->hp_pins[i]); + if (!dac) + break; + if (!i) + spec->multiout.hp_nid = dac; + else + spec->multiout.extra_out_nid[extra_nids++] = dac; + } + for (i = 0; i < cfg->speaker_outs; i++) { + dac = get_dac(codec, cfg->speaker_pins[i]); + if (!dac) + break; + spec->multiout.extra_out_nid[extra_nids++] = dac; + } + + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + cfg->speaker_outs = cfg->line_outs; + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->line_outs = 0; + } + + return 0; +} + +static int parse_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t pin = cfg->input_pins[i]; + if (!pin) + continue; + spec->input_idx[spec->num_inputs] = i; + spec->capsrc_idx[i] = spec->num_inputs++; + spec->cur_input = i; + spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]); + } + if (!spec->num_inputs) + return 0; + + /* check whether the automatic mic switch is available */ + if (spec->num_inputs == 2 && + spec->adc_nid[AUTO_PIN_MIC] && spec->adc_nid[AUTO_PIN_FRONT_MIC]) { + if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_FRONT_MIC])) { + if (!is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) { + spec->mic_detect = 1; + spec->automic_idx = AUTO_PIN_FRONT_MIC; + } + } else { + if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) { + spec->mic_detect = 1; + spec->automic_idx = AUTO_PIN_MIC; + } + } + } + return 0; +} + + +static int parse_digital_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + + if (!cfg->dig_outs) + return 0; + if (snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) < 1) + return 0; + spec->multiout.dig_out_nid = nid; + spec->multiout.share_spdif = 1; + if (cfg->dig_outs > 1 && + snd_hda_get_connections(codec, cfg->dig_out_pins[1], &nid, 1) > 0) { + spec->slave_dig_outs[0] = nid; + codec->slave_dig_outs = spec->slave_dig_outs; + } + return 0; +} + +static int parse_digital_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int idx; + + if (cfg->dig_in_pin) + spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx); + return 0; +} + +/* + * create mixer controls + */ + +static const char *dir_sfx[2] = { "Playback", "Capture" }; + +static int add_mute(struct hda_codec *codec, const char *name, int index, + unsigned int pval, int dir, struct snd_kcontrol **kctlp) +{ + char tmp[44]; + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_IDX(tmp, index, 0, 0, HDA_OUTPUT); + knew.private_value = pval; + snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]); + *kctlp = snd_ctl_new1(&knew, codec); + return snd_hda_ctl_add(codec, *kctlp); +} + +static int add_volume(struct hda_codec *codec, const char *name, + int index, unsigned int pval, int dir, + struct snd_kcontrol **kctlp) +{ + char tmp[32]; + struct snd_kcontrol_new knew = + HDA_CODEC_VOLUME_IDX(tmp, index, 0, 0, HDA_OUTPUT); + knew.private_value = pval; + snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]); + *kctlp = snd_ctl_new1(&knew, codec); + return snd_hda_ctl_add(codec, *kctlp); +} + +static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) +{ + unsigned int caps; + + /* set the upper-limit for mixer amp to 0dB */ + caps = query_amp_caps(codec, dac, HDA_OUTPUT); + caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT); + caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f) + << AC_AMPCAP_NUM_STEPS_SHIFT; + snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps); +} + +static int add_vmaster(struct hda_codec *codec, hda_nid_t dac) +{ + struct cs_spec *spec = codec->spec; + unsigned int tlv[4]; + int err; + + spec->vmaster_sw = + snd_ctl_make_virtual_master("Master Playback Switch", NULL); + err = snd_hda_ctl_add(codec, spec->vmaster_sw); + if (err < 0) + return err; + + snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv); + spec->vmaster_vol = + snd_ctl_make_virtual_master("Master Playback Volume", tlv); + err = snd_hda_ctl_add(codec, spec->vmaster_vol); + if (err < 0) + return err; + return 0; +} + +static int add_output(struct hda_codec *codec, hda_nid_t dac, int idx, + int num_ctls, int type) +{ + struct cs_spec *spec = codec->spec; + const char *name; + int err, index; + struct snd_kcontrol *kctl; + static char *speakers[] = { + "Front Speaker", "Surround Speaker", "Bass Speaker" + }; + static char *line_outs[] = { + "Front Line-Out", "Surround Line-Out", "Bass Line-Out" + }; + + fix_volume_caps(codec, dac); + if (!spec->vmaster_sw) { + err = add_vmaster(codec, dac); + if (err < 0) + return err; + } + + index = 0; + switch (type) { + case AUTO_PIN_HP_OUT: + name = "Headphone"; + index = idx; + break; + case AUTO_PIN_SPEAKER_OUT: + if (num_ctls > 1) + name = speakers[idx]; + else + name = "Speaker"; + break; + default: + if (num_ctls > 1) + name = line_outs[idx]; + else + name = "Line-Out"; + break; + } + + err = add_mute(codec, name, index, + HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); + if (err < 0) + return err; + err = snd_ctl_add_slave(spec->vmaster_sw, kctl); + if (err < 0) + return err; + + err = add_volume(codec, name, index, + HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); + if (err < 0) + return err; + err = snd_ctl_add_slave(spec->vmaster_vol, kctl); + if (err < 0) + return err; + + return 0; +} + +static int build_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; + + for (i = 0; i < cfg->line_outs; i++) { + err = add_output(codec, get_dac(codec, cfg->line_out_pins[i]), + i, cfg->line_outs, cfg->line_out_type); + if (err < 0) + return err; + } + for (i = 0; i < cfg->hp_outs; i++) { + err = add_output(codec, get_dac(codec, cfg->hp_pins[i]), + i, cfg->hp_outs, AUTO_PIN_HP_OUT); + if (err < 0) + return err; + } + for (i = 0; i < cfg->speaker_outs; i++) { + err = add_output(codec, get_dac(codec, cfg->speaker_pins[i]), + i, cfg->speaker_outs, AUTO_PIN_SPEAKER_OUT); + if (err < 0) + return err; + } + return 0; +} + +/* + */ + +static struct snd_kcontrol_new cs_capture_ctls[] = { + HDA_BIND_SW("Capture Switch", 0), + HDA_BIND_VOL("Capture Volume", 0), +}; + +static int change_cur_input(struct hda_codec *codec, unsigned int idx, + int force) +{ + struct cs_spec *spec = codec->spec; + + if (spec->cur_input == idx && !force) + return 0; + if (spec->cur_adc && spec->cur_adc != spec->adc_nid[idx]) { + /* stream is running, let's swap the current ADC */ + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = spec->adc_nid[idx]; + snd_hda_codec_setup_stream(codec, spec->cur_adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + } + snd_hda_codec_write(codec, spec->cur_adc, 0, + AC_VERB_SET_CONNECT_SEL, + spec->adc_idx[idx]); + spec->cur_input = idx; + return 1; +} + +static int cs_capture_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + unsigned int idx; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = spec->num_inputs; + if (uinfo->value.enumerated.item >= spec->num_inputs) + uinfo->value.enumerated.item = spec->num_inputs - 1; + idx = spec->input_idx[uinfo->value.enumerated.item]; + strcpy(uinfo->value.enumerated.name, auto_pin_cfg_labels[idx]); + return 0; +} + +static int cs_capture_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->capsrc_idx[spec->cur_input]; + return 0; +} + +static int cs_capture_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + unsigned int idx = ucontrol->value.enumerated.item[0]; + + if (idx >= spec->num_inputs) + return -EINVAL; + idx = spec->input_idx[idx]; + return change_cur_input(codec, idx, 0); +} + +static struct snd_kcontrol_new cs_capture_source = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = cs_capture_source_info, + .get = cs_capture_source_get, + .put = cs_capture_source_put, +}; + +static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec, + struct hda_ctl_ops *ops) +{ + struct cs_spec *spec = codec->spec; + struct hda_bind_ctls *bind; + int i, n; + + bind = kzalloc(sizeof(*bind) + sizeof(long) * (spec->num_inputs + 1), + GFP_KERNEL); + if (!bind) + return NULL; + bind->ops = ops; + n = 0; + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!spec->adc_nid[i]) + continue; + bind->values[n++] = + HDA_COMPOSE_AMP_VAL(spec->adc_nid[i], 3, + spec->adc_idx[i], HDA_INPUT); + } + return bind; +} + +static int build_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + int i, err; + + if (!spec->num_inputs) + return 0; + + /* make bind-capture */ + spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw); + spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol); + for (i = 0; i < 2; i++) { + struct snd_kcontrol *kctl; + if (!spec->capture_bind[i]) + return -ENOMEM; + kctl = snd_ctl_new1(&cs_capture_ctls[i], codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = (long)spec->capture_bind[i]; + err = snd_hda_ctl_add(codec, kctl); + if (err < 0) + return err; + } + + if (spec->num_inputs > 1 && !spec->mic_detect) { + err = snd_hda_ctl_add(codec, + snd_ctl_new1(&cs_capture_source, codec)); + if (err < 0) + return err; + } + + return 0; +} + +/* + */ + +static int build_digital_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + int err; + + if (!spec->multiout.dig_out_nid) + return 0; + + err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + if (err < 0) + return err; + err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); + if (err < 0) + return err; + return 0; +} + +static int build_digital_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + if (spec->dig_in) + return snd_hda_create_spdif_in_ctls(codec, spec->dig_in); + return 0; +} + +/* + * auto-mute and auto-mic switching + */ + +static void cs_automute(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int caps, present, hp_present; + hda_nid_t nid; + int i; + + hp_present = 0; + for (i = 0; i < cfg->hp_outs; i++) { + nid = cfg->hp_pins[i]; + caps = snd_hda_query_pin_caps(codec, nid); + if (!(caps & AC_PINCAP_PRES_DETECT)) + continue; + if (caps & AC_PINCAP_TRIG_REQ) + snd_hda_codec_read(codec, nid, 0, + AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_SENSE, 0); + hp_present |= (present & AC_PINSENSE_PRESENCE) != 0; + if (hp_present) + break; + } + for (i = 0; i < cfg->speaker_outs; i++) { + nid = cfg->speaker_pins[i]; + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + hp_present ? 0 : PIN_OUT); + } + if (spec->board_config == CS420X_MBP55) { + unsigned int gpio = hp_present ? 0x02 : 0x08; + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, gpio); + } +} + +static void cs_automic(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + unsigned int caps, present; + + nid = cfg->input_pins[spec->automic_idx]; + caps = snd_hda_query_pin_caps(codec, nid); + if (caps & AC_PINCAP_TRIG_REQ) + snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_SENSE, 0); + if (present & AC_PINSENSE_PRESENCE) + change_cur_input(codec, spec->automic_idx, 0); + else { + unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ? + AUTO_PIN_FRONT_MIC : AUTO_PIN_MIC; + change_cur_input(codec, imic, 0); + } +} + +/* + */ + +static void init_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + /* mute first */ + for (i = 0; i < spec->multiout.num_dacs; i++) + snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + if (spec->multiout.hp_nid) + snd_hda_codec_write(codec, spec->multiout.hp_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) { + if (!spec->multiout.extra_out_nid[i]) + break; + snd_hda_codec_write(codec, spec->multiout.extra_out_nid[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + } + + /* set appropriate pin controls */ + for (i = 0; i < cfg->line_outs; i++) + snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + for (i = 0; i < cfg->hp_outs; i++) { + hda_nid_t nid = cfg->hp_pins[i]; + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + if (!cfg->speaker_outs) + continue; + if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | HP_EVENT); + spec->hp_detect = 1; + } + } + for (i = 0; i < cfg->speaker_outs; i++) + snd_hda_codec_write(codec, cfg->speaker_pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + if (spec->hp_detect) + cs_automute(codec); +} + +static void init_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int coef; + int i; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + unsigned int ctl; + hda_nid_t pin = cfg->input_pins[i]; + if (!pin || !spec->adc_nid[i]) + continue; + /* set appropriate pin control and mute first */ + ctl = PIN_IN; + if (i <= AUTO_PIN_FRONT_MIC) { + unsigned int caps = snd_hda_query_pin_caps(codec, pin); + caps >>= AC_PINCAP_VREF_SHIFT; + if (caps & AC_PINCAP_VREF_80) + ctl = PIN_VREF80; + } + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); + snd_hda_codec_write(codec, spec->adc_nid[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(spec->adc_idx[i])); + if (spec->mic_detect && spec->automic_idx == i) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | MIC_EVENT); + } + change_cur_input(codec, spec->cur_input, 1); + if (spec->mic_detect) + cs_automic(codec); + + coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */ + if (is_active_pin(codec, CS_DMIC2_PIN_NID)) + coef |= 0x0500; /* DMIC2 enable 2 channels, disable GPIO1 */ + if (is_active_pin(codec, CS_DMIC1_PIN_NID)) + coef |= 0x1800; /* DMIC1 enable 2 channels, disable GPIO0 + * No effect if SPDIF_OUT2 is slected in + * IDX_SPDIF_CTL. + */ + cs_vendor_coef_set(codec, IDX_ADC_CFG, coef); +} + +static struct hda_verb cs_coef_init_verbs[] = { + {0x11, AC_VERB_SET_PROC_STATE, 1}, + {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG}, + {0x11, AC_VERB_SET_PROC_COEF, + (0x002a /* DAC1/2/3 SZCMode Soft Ramp */ + | 0x0040 /* Mute DACs on FIFO error */ + | 0x1000 /* Enable DACs High Pass Filter */ + | 0x0400 /* Disable Coefficient Auto increment */ + )}, + /* Beep */ + {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG}, + {0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */ + + {} /* terminator */ +}; + +/* SPDIF setup */ +static void init_digital(struct hda_codec *codec) +{ + unsigned int coef; + + coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */ + coef |= 0x0008; /* Replace with mute on error */ + if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID)) + coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2 + * SPDIF_OUT2 is shared with GPIO1 and + * DMIC_SDA2. + */ + cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef); +} + +static int cs_init(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + + snd_hda_sequence_write(codec, cs_coef_init_verbs); + + if (spec->gpio_mask) { + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, + spec->gpio_mask); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, + spec->gpio_dir); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + spec->gpio_data); + } + + init_output(codec); + init_input(codec); + init_digital(codec); + return 0; +} + +static int cs_build_controls(struct hda_codec *codec) +{ + int err; + + err = build_output(codec); + if (err < 0) + return err; + err = build_input(codec); + if (err < 0) + return err; + err = build_digital_output(codec); + if (err < 0) + return err; + err = build_digital_input(codec); + if (err < 0) + return err; + return cs_init(codec); +} + +static void cs_free(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + kfree(spec->capture_bind[0]); + kfree(spec->capture_bind[1]); + kfree(codec->spec); +} + +static void cs_unsol_event(struct hda_codec *codec, unsigned int res) +{ + switch ((res >> 26) & 0x7f) { + case HP_EVENT: + cs_automute(codec); + break; + case MIC_EVENT: + cs_automic(codec); + break; + } +} + +static struct hda_codec_ops cs_patch_ops = { + .build_controls = cs_build_controls, + .build_pcms = cs_build_pcms, + .init = cs_init, + .free = cs_free, + .unsol_event = cs_unsol_event, +}; + +static int cs_parse_auto_config(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + + err = parse_output(codec); + if (err < 0) + return err; + err = parse_input(codec); + if (err < 0) + return err; + err = parse_digital_output(codec); + if (err < 0) + return err; + err = parse_digital_input(codec); + if (err < 0) + return err; + return 0; +} + +static const char *cs420x_models[CS420X_MODELS] = { + [CS420X_MBP55] = "mbp55", + [CS420X_AUTO] = "auto", +}; + + +static struct snd_pci_quirk cs420x_cfg_tbl[] = { + SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55), + {} /* terminator */ +}; + +struct cs_pincfg { + hda_nid_t nid; + u32 val; +}; + +static struct cs_pincfg mbp55_pincfgs[] = { + { 0x09, 0x012b4030 }, + { 0x0a, 0x90100121 }, + { 0x0b, 0x90100120 }, + { 0x0c, 0x400000f0 }, + { 0x0d, 0x90a00110 }, + { 0x0e, 0x400000f0 }, + { 0x0f, 0x400000f0 }, + { 0x10, 0x014be040 }, + { 0x12, 0x400000f0 }, + { 0x15, 0x400000f0 }, + {} /* terminator */ +}; + +static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = { + [CS420X_MBP55] = mbp55_pincfgs, +}; + +static void fix_pincfg(struct hda_codec *codec, int model) +{ + const struct cs_pincfg *cfg = cs_pincfgs[model]; + if (!cfg) + return; + for (; cfg->nid; cfg++) + snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); +} + + +static int patch_cs420x(struct hda_codec *codec) +{ + struct cs_spec *spec; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + codec->spec = spec; + + spec->board_config = + snd_hda_check_board_config(codec, CS420X_MODELS, + cs420x_models, cs420x_cfg_tbl); + if (spec->board_config >= 0) + fix_pincfg(codec, spec->board_config); + + switch (spec->board_config) { + case CS420X_MBP55: + /* GPIO1 = headphones */ + /* GPIO3 = speakers */ + spec->gpio_mask = 0x0a; + spec->gpio_dir = 0x0a; + break; + } + + err = cs_parse_auto_config(codec); + if (err < 0) + goto error; + + codec->patch_ops = cs_patch_ops; + + return 0; + + error: + kfree(codec->spec); + codec->spec = NULL; + return err; +} + + +/* + * patch entries + */ +static struct hda_codec_preset snd_hda_preset_cirrus[] = { + { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x }, + { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x }, + {} /* terminator */ +}; + +MODULE_ALIAS("snd-hda-codec-id:10134206"); +MODULE_ALIAS("snd-hda-codec-id:10134207"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cirrus Logic HD-audio codec"); + +static struct hda_codec_preset_list cirrus_list = { + .preset = snd_hda_preset_cirrus, + .owner = THIS_MODULE, +}; + +static int __init patch_cirrus_init(void) +{ + return snd_hda_add_codec_preset(&cirrus_list); +} + +static void __exit patch_cirrus_exit(void) +{ + snd_hda_delete_codec_preset(&cirrus_list); +} + +module_init(patch_cirrus_init) +module_exit(patch_cirrus_exit) diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index c921264bbd7..780e1a72114 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -635,7 +635,8 @@ static int patch_cmi9880(struct hda_codec *codec) cmi9880_models, cmi9880_cfg_tbl); if (spec->board_config < 0) { - snd_printdd(KERN_INFO "hda_codec: Unknown model for CMI9880\n"); + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); spec->board_config = CMI_AUTO; /* try everything */ } diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index ac868c59f9e..3fbbc8c01e7 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -108,6 +108,8 @@ struct conexant_spec { struct hda_input_mux private_imux; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + unsigned int dell_automute; + unsigned int port_d_mode; }; static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, @@ -680,11 +682,13 @@ static struct hda_input_mux cxt5045_capture_source = { }; static struct hda_input_mux cxt5045_capture_source_benq = { - .num_items = 3, + .num_items = 5, .items = { { "IntMic", 0x1 }, { "ExtMic", 0x2 }, { "LineIn", 0x3 }, + { "CD", 0x4 }, + { "Mixer", 0x0 }, } }; @@ -809,11 +813,19 @@ static struct snd_kcontrol_new cxt5045_mixers[] = { }; static struct snd_kcontrol_new cxt5045_benq_mixers[] = { + HDA_CODEC_VOLUME("CD Capture Volume", 0x1a, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Capture Switch", 0x1a, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x17, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x17, 0x4, HDA_INPUT), + 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), + HDA_CODEC_VOLUME("Mixer Capture Volume", 0x1a, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mixer Capture Switch", 0x1a, 0x0, HDA_INPUT), + {} }; @@ -1908,6 +1920,480 @@ static int patch_cxt5051(struct hda_codec *codec) return 0; } +/* Conexant 5066 specific */ + +static hda_nid_t cxt5066_dac_nids[1] = { 0x10 }; +static hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 }; +static hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 }; +#define CXT5066_SPDIF_OUT 0x21 + +static struct hda_channel_mode cxt5066_modes[1] = { + { 2, NULL }, +}; + +static void cxt5066_update_speaker(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + unsigned int pinctl; + + snd_printdd("CXT5066: update speaker, hp_present=%d\n", + spec->hp_present); + + /* Port A (HP) */ + pinctl = ((spec->hp_present & 1) && spec->cur_eapd) ? PIN_HP : 0; + snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl); + + /* Port D (HP/LO) */ + pinctl = ((spec->hp_present & 2) && spec->cur_eapd) + ? spec->port_d_mode : 0; + snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl); + + /* CLASS_D AMP */ + pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; + snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl); + + if (spec->dell_automute) { + /* DELL AIO Port Rule: PortA > PortD > IntSpk */ + pinctl = (!(spec->hp_present & 1) && spec->cur_eapd) + ? PIN_OUT : 0; + snd_hda_codec_write(codec, 0x1c, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); + } +} + +/* turn on/off EAPD (+ mute HP) as a master switch */ +static int cxt5066_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; + + cxt5066_update_speaker(codec); + return 1; +} + +/* toggle input of built-in and mic jack appropriately */ +static void cxt5066_automic(struct hda_codec *codec) +{ + static struct hda_verb ext_mic_present[] = { + /* enable external mic, port B */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + + /* switch to external mic input */ + {0x17, AC_VERB_SET_CONNECT_SEL, 0}, + + /* disable internal mic, port C */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {} + }; + static struct hda_verb ext_mic_absent[] = { + /* enable internal mic, port C */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + + /* switch to internal mic input */ + {0x17, AC_VERB_SET_CONNECT_SEL, 1}, + + /* disable external mic, port B */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {} + }; + unsigned int present; + + present = snd_hda_codec_read(codec, 0x1a, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + if (present) { + snd_printdd("CXT5066: external microphone detected\n"); + snd_hda_sequence_write(codec, ext_mic_present); + } else { + snd_printdd("CXT5066: external microphone absent\n"); + snd_hda_sequence_write(codec, ext_mic_absent); + } +} + +/* mute internal speaker if HP is plugged */ +static void cxt5066_hp_automute(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + unsigned int portA, portD; + + /* Port A */ + portA = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + + /* Port D */ + portD = (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE) << 1; + + spec->hp_present = !!(portA | portD); + snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n", + portA, portD, spec->hp_present); + cxt5066_update_speaker(codec); +} + +/* unsolicited event for jack sensing */ +static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) +{ + snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); + switch (res >> 26) { + case CONEXANT_HP_EVENT: + cxt5066_hp_automute(codec); + break; + case CONEXANT_MIC_EVENT: + cxt5066_automic(codec); + break; + } +} + +static const struct hda_input_mux cxt5066_analog_mic_boost = { + .num_items = 5, + .items = { + { "0dB", 0 }, + { "10dB", 1 }, + { "20dB", 2 }, + { "30dB", 3 }, + { "40dB", 4 }, + }, +}; + +static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return snd_hda_input_mux_info(&cxt5066_analog_mic_boost, uinfo); +} + +static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + int val; + + val = snd_hda_codec_read(codec, 0x17, 0, + AC_VERB_GET_AMP_GAIN_MUTE, AC_AMP_GET_OUTPUT); + + ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN; + return 0; +} + +static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + const struct hda_input_mux *imux = &cxt5066_analog_mic_boost; + unsigned int idx; + + if (!imux->num_items) + return 0; + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + + snd_hda_codec_write_cache(codec, 0x17, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | + imux->items[idx].index); + + return 1; +} + +static struct hda_input_mux cxt5066_capture_source = { + .num_items = 4, + .items = { + { "Mic B", 0 }, + { "Mic C", 1 }, + { "Mic E", 2 }, + { "Mic F", 3 }, + }, +}; + +static struct hda_bind_ctls cxt5066_bind_capture_vol_others = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT), + 0 + }, +}; + +static struct hda_bind_ctls cxt5066_bind_capture_sw_others = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT), + 0 + }, +}; + +static struct snd_kcontrol_new cxt5066_mixer_master[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), + {} +}; + +static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, + .info = snd_hda_mixer_amp_volume_info, + .get = snd_hda_mixer_amp_volume_get, + .put = snd_hda_mixer_amp_volume_put, + .tlv = { .c = snd_hda_mixer_amp_tlv }, + /* offset by 28 volume steps to limit minimum gain to -46dB */ + .private_value = + HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28), + }, + {} +}; + +static struct snd_kcontrol_new cxt5066_mixers[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = cxt_eapd_info, + .get = cxt_eapd_get, + .put = cxt5066_hp_master_sw_put, + .private_value = 0x1d, + }, + + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Mic Boost Capture Enum", + .info = cxt5066_mic_boost_mux_enum_info, + .get = cxt5066_mic_boost_mux_enum_get, + .put = cxt5066_mic_boost_mux_enum_put, + }, + + HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others), + HDA_BIND_SW("Capture Switch", &cxt5066_bind_capture_sw_others), + {} +}; + +static struct hda_verb cxt5066_init_verbs[] = { + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */ + {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */ + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */ + + /* Speakers */ + {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + /* HP, Amp */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + /* DAC1 */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + + /* no digital microphone support yet */ + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* Audio input selector */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3}, + + /* SPDIF route: PCM */ + {0x20, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x0}, + + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* EAPD */ + {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ + + /* not handling these yet */ + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, + {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, + {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, + {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, + {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, 0}, + { } /* end */ +}; + +static struct hda_verb cxt5066_init_verbs_olpc[] = { + /* Port A: headphones */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + /* Port B: external microphone */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + + /* Port C: internal microphone */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + + /* Port D: unused */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* Port E: unused, but has primary EAPD */ + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ + + /* Port F: unused */ + {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* Port G: internal speakers */ + {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + /* DAC1 */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* DAC2: unused */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + + /* Disable digital microphone port */ + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* Audio input selectors */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + + /* Disable SPDIF */ + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* enable unsolicited events for Port A and B */ + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, + { } /* end */ +}; + +static struct hda_verb cxt5066_init_verbs_portd_lo[] = { + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { } /* end */ +}; + +/* initialize jack-sensing, too */ +static int cxt5066_init(struct hda_codec *codec) +{ + snd_printdd("CXT5066: init\n"); + conexant_init(codec); + if (codec->patch_ops.unsol_event) { + cxt5066_hp_automute(codec); + cxt5066_automic(codec); + } + return 0; +} + +enum { + CXT5066_LAPTOP, /* Laptops w/ EAPD support */ + CXT5066_DELL_LAPTOP, /* Dell Laptop */ + CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ + CXT5066_MODELS +}; + +static const char *cxt5066_models[CXT5066_MODELS] = { + [CXT5066_LAPTOP] = "laptop", + [CXT5066_DELL_LAPTOP] = "dell-laptop", + [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", +}; + +static struct snd_pci_quirk cxt5066_cfg_tbl[] = { + SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", + CXT5066_LAPTOP), + SND_PCI_QUIRK(0x1028, 0x02f5, "Dell", + CXT5066_DELL_LAPTOP), + {} +}; + +static int patch_cxt5066(struct hda_codec *codec) +{ + struct conexant_spec *spec; + int board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + codec->spec = spec; + + codec->patch_ops = conexant_patch_ops; + codec->patch_ops.init = cxt5066_init; + + spec->dell_automute = 0; + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(cxt5066_dac_nids); + spec->multiout.dac_nids = cxt5066_dac_nids; + spec->multiout.dig_out_nid = CXT5066_SPDIF_OUT; + spec->num_adc_nids = 1; + spec->adc_nids = cxt5066_adc_nids; + spec->capsrc_nids = cxt5066_capsrc_nids; + spec->input_mux = &cxt5066_capture_source; + + spec->port_d_mode = PIN_HP; + + spec->num_init_verbs = 1; + spec->init_verbs[0] = cxt5066_init_verbs; + spec->num_channel_mode = ARRAY_SIZE(cxt5066_modes); + spec->channel_mode = cxt5066_modes; + spec->cur_adc = 0; + spec->cur_adc_idx = 0; + + board_config = snd_hda_check_board_config(codec, CXT5066_MODELS, + cxt5066_models, cxt5066_cfg_tbl); + switch (board_config) { + default: + case CXT5066_LAPTOP: + spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; + spec->mixers[spec->num_mixers++] = cxt5066_mixers; + break; + case CXT5066_DELL_LAPTOP: + spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; + spec->mixers[spec->num_mixers++] = cxt5066_mixers; + + spec->port_d_mode = PIN_OUT; + spec->init_verbs[spec->num_init_verbs] = cxt5066_init_verbs_portd_lo; + spec->num_init_verbs++; + spec->dell_automute = 1; + break; + case CXT5066_OLPC_XO_1_5: + codec->patch_ops.unsol_event = cxt5066_unsol_event; + spec->init_verbs[0] = cxt5066_init_verbs_olpc; + spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; + spec->mixers[spec->num_mixers++] = cxt5066_mixers; + spec->port_d_mode = 0; + + /* no S/PDIF out */ + spec->multiout.dig_out_nid = 0; + + /* input source automatically selected */ + spec->input_mux = NULL; + break; + } + + return 0; +} /* */ @@ -1919,12 +2405,15 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = { .patch = patch_cxt5047 }, { .id = 0x14f15051, .name = "CX20561 (Hermosa)", .patch = patch_cxt5051 }, + { .id = 0x14f15066, .name = "CX20582 (Pebble)", + .patch = patch_cxt5066 }, {} /* terminator */ }; MODULE_ALIAS("snd-hda-codec-id:14f15045"); MODULE_ALIAS("snd-hda-codec-id:14f15047"); MODULE_ALIAS("snd-hda-codec-id:14f15051"); +MODULE_ALIAS("snd-hda-codec-id:14f15066"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Conexant HD-audio codec"); diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index fcc77fec448..01a18ed475a 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -33,8 +33,8 @@ #include "hda_codec.h" #include "hda_local.h" -#define CVT_NID 0x02 /* audio converter */ -#define PIN_NID 0x03 /* HDMI output pin */ +static hda_nid_t cvt_nid; /* audio converter */ +static hda_nid_t pin_nid; /* HDMI output pin */ #define INTEL_HDMI_EVENT_TAG 0x08 @@ -44,30 +44,6 @@ struct intel_hdmi_spec { struct hdmi_eld sink_eld; }; -static struct hda_verb pinout_enable_verb[] = { - {PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {} /* terminator */ -}; - -static struct hda_verb unsolicited_response_verb[] = { - {PIN_NID, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | - INTEL_HDMI_EVENT_TAG}, - {} -}; - -static struct hda_verb def_chan_map[] = { - {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x00}, - {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x11}, - {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x22}, - {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x33}, - {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x44}, - {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x55}, - {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x66}, - {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x77}, - {} -}; - - struct hdmi_audio_infoframe { u8 type; /* 0x84 */ u8 ver; /* 0x01 */ @@ -244,11 +220,12 @@ static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid, static void hdmi_enable_output(struct hda_codec *codec) { /* Unmute */ - if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, PIN_NID, 0, + if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); /* Enable pin out */ - snd_hda_sequence_write(codec, pinout_enable_verb); + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); } /* @@ -256,8 +233,8 @@ static void hdmi_enable_output(struct hda_codec *codec) */ static void hdmi_start_infoframe_trans(struct hda_codec *codec) { - hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0); - snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT, + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST); } @@ -266,20 +243,20 @@ static void hdmi_start_infoframe_trans(struct hda_codec *codec) */ static void hdmi_stop_infoframe_trans(struct hda_codec *codec) { - hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0); - snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT, + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE); } static int hdmi_get_channel_count(struct hda_codec *codec) { - return 1 + snd_hda_codec_read(codec, CVT_NID, 0, + return 1 + snd_hda_codec_read(codec, cvt_nid, 0, AC_VERB_GET_CVT_CHAN_COUNT, 0); } static void hdmi_set_channel_count(struct hda_codec *codec, int chs) { - snd_hda_codec_write(codec, CVT_NID, 0, + snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); if (chs != hdmi_get_channel_count(codec)) @@ -294,7 +271,7 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec) int slot; for (i = 0; i < 8; i++) { - slot = snd_hda_codec_read(codec, CVT_NID, 0, + slot = snd_hda_codec_read(codec, cvt_nid, 0, AC_VERB_GET_HDMI_CHAN_SLOT, i); printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", slot >> 4, slot & 0x7); @@ -307,7 +284,7 @@ static void hdmi_parse_eld(struct hda_codec *codec) struct intel_hdmi_spec *spec = codec->spec; struct hdmi_eld *eld = &spec->sink_eld; - if (!snd_hdmi_get_eld(eld, codec, PIN_NID)) + if (!snd_hdmi_get_eld(eld, codec, pin_nid)) snd_hdmi_show_eld(eld); } @@ -322,11 +299,11 @@ static void hdmi_debug_dip_size(struct hda_codec *codec) int i; int size; - size = snd_hdmi_get_eld_size(codec, PIN_NID); + size = snd_hdmi_get_eld_size(codec, pin_nid); printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size); for (i = 0; i < 8; i++) { - size = snd_hda_codec_read(codec, PIN_NID, 0, + size = snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, i); printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size); } @@ -340,15 +317,15 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec) int size; int pi, bi; for (i = 0; i < 8; i++) { - size = snd_hda_codec_read(codec, PIN_NID, 0, + size = snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, i); if (size == 0) continue; - hdmi_set_dip_index(codec, PIN_NID, i, 0x0); + hdmi_set_dip_index(codec, pin_nid, i, 0x0); for (j = 1; j < 1000; j++) { - hdmi_write_dip_byte(codec, PIN_NID, 0x0); - hdmi_get_dip_index(codec, PIN_NID, &pi, &bi); + hdmi_write_dip_byte(codec, pin_nid, 0x0); + hdmi_get_dip_index(codec, pin_nid, &pi, &bi); if (pi != i) snd_printd(KERN_INFO "dip index %d: %d != %d\n", bi, pi, i); @@ -376,9 +353,9 @@ static void hdmi_fill_audio_infoframe(struct hda_codec *codec, sum += params[i]; ai->checksum = - sum; - hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0); + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); for (i = 0; i < sizeof(ai); i++) - hdmi_write_dip_byte(codec, PIN_NID, params[i]); + hdmi_write_dip_byte(codec, pin_nid, params[i]); } /* @@ -465,6 +442,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, static void hdmi_setup_channel_mapping(struct hda_codec *codec, struct hdmi_audio_infoframe *ai) { + int i; + if (!ai->CA) return; @@ -473,7 +452,11 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, * ALSA sequence is front/surr/clfe/side? */ - snd_hda_sequence_write(codec, def_chan_map); + for (i = 0; i < 8; i++) + snd_hda_codec_write(codec, cvt_nid, 0, + AC_VERB_SET_HDMI_CHAN_SLOT, + (i << 4) | i); + hdmi_debug_channel_mapping(codec); } @@ -597,7 +580,6 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = { .substreams = 1, .channels_min = 2, .channels_max = 8, - .nid = CVT_NID, /* NID to query formats and rates and setup streams */ .ops = { .open = intel_hdmi_playback_pcm_open, .close = intel_hdmi_playback_pcm_close, @@ -613,6 +595,9 @@ static int intel_hdmi_build_pcms(struct hda_codec *codec) codec->num_pcms = 1; codec->pcm_info = info; + /* NID to query formats and rates and setup streams */ + intel_hdmi_pcm_playback.nid = cvt_nid; + info->name = "INTEL HDMI"; info->pcm_type = HDA_PCM_TYPE_HDMI; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback; @@ -636,8 +621,9 @@ static int intel_hdmi_init(struct hda_codec *codec) { hdmi_enable_output(codec); - snd_hda_sequence_write(codec, unsolicited_response_verb); - + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | INTEL_HDMI_EVENT_TAG); return 0; } @@ -657,7 +643,7 @@ static struct hda_codec_ops intel_hdmi_patch_ops = { .unsol_event = intel_hdmi_unsol_event, }; -static int patch_intel_hdmi(struct hda_codec *codec) +static int do_patch_intel_hdmi(struct hda_codec *codec) { struct intel_hdmi_spec *spec; @@ -667,7 +653,7 @@ static int patch_intel_hdmi(struct hda_codec *codec) spec->multiout.num_dacs = 0; /* no analog */ spec->multiout.max_channels = 8; - spec->multiout.dig_out_nid = CVT_NID; + spec->multiout.dig_out_nid = cvt_nid; codec->spec = spec; codec->patch_ops = intel_hdmi_patch_ops; @@ -679,12 +665,27 @@ static int patch_intel_hdmi(struct hda_codec *codec) return 0; } +static int patch_intel_hdmi(struct hda_codec *codec) +{ + cvt_nid = 0x02; + pin_nid = 0x03; + return do_patch_intel_hdmi(codec); +} + +static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec) +{ + cvt_nid = 0x02; + pin_nid = 0x04; + return do_patch_intel_hdmi(codec); +} + static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { { .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi }, { .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi }, { .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi }, { .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi }, { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi }, + { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak }, { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, {} /* terminator */ }; @@ -694,6 +695,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862801"); MODULE_ALIAS("snd-hda-codec-id:80862802"); MODULE_ALIAS("snd-hda-codec-id:80862803"); MODULE_ALIAS("snd-hda-codec-id:80862804"); +MODULE_ALIAS("snd-hda-codec-id:80860054"); MODULE_ALIAS("snd-hda-codec-id:10951392"); MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c index f5792e2eea8..9fb60276f5c 100644 --- a/sound/pci/hda/patch_nvhdmi.c +++ b/sound/pci/hda/patch_nvhdmi.c @@ -29,6 +29,9 @@ #include "hda_codec.h" #include "hda_local.h" +/* define below to restrict the supported rates and formats */ +/* #define LIMITED_RATE_FMT_SUPPORT */ + struct nvhdmi_spec { struct hda_multi_out multiout; @@ -60,6 +63,22 @@ static struct hda_verb nvhdmi_basic_init[] = { {} /* terminator */ }; +#ifdef LIMITED_RATE_FMT_SUPPORT +/* support only the safe format and rate */ +#define SUPPORTED_RATES SNDRV_PCM_RATE_48000 +#define SUPPORTED_MAXBPS 16 +#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE +#else +/* support all rates and formats */ +#define SUPPORTED_RATES \ + (SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) +#define SUPPORTED_MAXBPS 24 +#define SUPPORTED_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) +#endif + /* * Controls */ @@ -258,9 +277,9 @@ static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch = { .channels_min = 2, .channels_max = 8, .nid = Nv_Master_Convert_nid, - .rates = SNDRV_PCM_RATE_48000, - .maxbps = 16, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SUPPORTED_RATES, + .maxbps = SUPPORTED_MAXBPS, + .formats = SUPPORTED_FORMATS, .ops = { .open = nvhdmi_dig_playback_pcm_open, .close = nvhdmi_dig_playback_pcm_close_8ch, @@ -273,9 +292,9 @@ static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = { .channels_min = 2, .channels_max = 2, .nid = Nv_Master_Convert_nid, - .rates = SNDRV_PCM_RATE_48000, - .maxbps = 16, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SUPPORTED_RATES, + .maxbps = SUPPORTED_MAXBPS, + .formats = SUPPORTED_FORMATS, .ops = { .open = nvhdmi_dig_playback_pcm_open, .close = nvhdmi_dig_playback_pcm_close_2ch, @@ -377,6 +396,7 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec) */ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch }, + { .id = 0x10de0003, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch }, { .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch }, { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch }, { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, @@ -385,6 +405,7 @@ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { }; MODULE_ALIAS("snd-hda-codec-id:10de0002"); +MODULE_ALIAS("snd-hda-codec-id:10de0003"); MODULE_ALIAS("snd-hda-codec-id:10de0006"); MODULE_ALIAS("snd-hda-codec-id:10de0007"); MODULE_ALIAS("snd-hda-codec-id:10de0067"); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 30eeb304351..c08ca660dab 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -208,12 +208,6 @@ enum { ALC885_MBP3, ALC885_MB5, ALC885_IMAC24, - ALC882_AUTO, - ALC882_MODEL_LAST, -}; - -/* ALC883 models */ -enum { ALC883_3ST_2ch_DIG, ALC883_3ST_6ch_DIG, ALC883_3ST_6ch, @@ -226,6 +220,7 @@ enum { ALC888_ACER_ASPIRE_4930G, ALC888_ACER_ASPIRE_6530G, ALC888_ACER_ASPIRE_8930G, + ALC888_ACER_ASPIRE_7730G, ALC883_MEDION, ALC883_MEDION_MD2, ALC883_LAPTOP_EAPD, @@ -237,17 +232,20 @@ enum { ALC888_3ST_HP, ALC888_6ST_DELL, ALC883_MITAC, + ALC883_CLEVO_M540R, ALC883_CLEVO_M720, ALC883_FUJITSU_PI2515, ALC888_FUJITSU_XA3530, ALC883_3ST_6ch_INTEL, + ALC889A_INTEL, + ALC889_INTEL, ALC888_ASUS_M90V, ALC888_ASUS_EEE1601, ALC889A_MB31, ALC1200_ASUS_P5Q, ALC883_SONY_VAIO_TT, - ALC883_AUTO, - ALC883_MODEL_LAST, + ALC882_AUTO, + ALC882_MODEL_LAST, }; /* for GPIO Poll */ @@ -262,6 +260,14 @@ enum { ALC_INIT_GPIO3, }; +struct alc_mic_route { + hda_nid_t pin; + unsigned char mux_idx; + unsigned char amix_idx; +}; + +#define MUX_IDX_UNDEF ((unsigned char)-1) + struct alc_spec { /* codec parameterization */ struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ @@ -269,7 +275,7 @@ struct alc_spec { struct snd_kcontrol_new *cap_mixer; /* capture mixer */ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ - const struct hda_verb *init_verbs[5]; /* initialization verbs + const struct hda_verb *init_verbs[10]; /* initialization verbs * don't forget NULL * termination! */ @@ -304,6 +310,8 @@ struct alc_spec { unsigned int num_mux_defs; const struct hda_input_mux *input_mux; unsigned int cur_mux[3]; + struct alc_mic_route ext_mic; + struct alc_mic_route int_mic; /* channel model */ const struct hda_channel_mode *channel_mode; @@ -320,6 +328,8 @@ struct alc_spec { struct snd_array kctls; struct hda_input_mux private_imux[3]; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS]; + hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS]; /* hooks */ void (*init_hook)(struct hda_codec *codec); @@ -329,6 +339,7 @@ struct alc_spec { unsigned int sense_updated: 1; unsigned int jack_present: 1; unsigned int master_sw: 1; + unsigned int auto_mic:1; /* other flags */ unsigned int no_analog :1; /* digital I/O only */ @@ -370,6 +381,7 @@ struct alc_config_preset { unsigned int num_mux_defs; const struct hda_input_mux *input_mux; void (*unsol_event)(struct hda_codec *, unsigned int); + void (*setup)(struct hda_codec *); void (*init_hook)(struct hda_codec *); #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_amp_list *loopbacks; @@ -417,7 +429,7 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; imux = &spec->input_mux[mux_idx]; - type = (get_wcaps(codec, nid) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + type = get_wcaps_type(get_wcaps(codec, nid)); if (type == AC_WID_AUD_MIX) { /* Matrix-mixer style (e.g. ALC882) */ unsigned int *cur_val = &spec->cur_mux[adc_idx]; @@ -842,9 +854,10 @@ static void print_realtek_coef(struct snd_info_buffer *buffer, /* * set up from the preset table */ -static void setup_preset(struct alc_spec *spec, +static void setup_preset(struct hda_codec *codec, const struct alc_config_preset *preset) { + struct alc_spec *spec = codec->spec; int i; for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++) @@ -886,6 +899,9 @@ static void setup_preset(struct alc_spec *spec, #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = preset->loopbacks; #endif + + if (preset->setup) + preset->setup(codec); } /* Enable GPIO mask and set output */ @@ -965,30 +981,64 @@ static void alc_automute_pin(struct hda_codec *codec) } } -#if 0 /* it's broken in some cases -- temporarily disabled */ +static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t nid) +{ + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + int i, nums; + + nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) + if (conn[i] == nid) + return i; + return -1; +} + static void alc_mic_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int present; - unsigned int mic_nid = spec->autocfg.input_pins[AUTO_PIN_MIC]; - unsigned int fmic_nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC]; - unsigned int mix_nid = spec->capsrc_nids[0]; - unsigned int capsrc_idx_mic, capsrc_idx_fmic; - - capsrc_idx_mic = mic_nid - 0x18; - capsrc_idx_fmic = fmic_nid - 0x18; - present = snd_hda_codec_read(codec, mic_nid, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - 0x7000 | (capsrc_idx_mic << 8) | (present ? 0 : 0x80)); - snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - 0x7000 | (capsrc_idx_fmic << 8) | (present ? 0x80 : 0)); - snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, capsrc_idx_fmic, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + struct alc_mic_route *dead, *alive; + unsigned int present, type; + hda_nid_t cap_nid; + + if (!spec->auto_mic) + return; + if (!spec->int_mic.pin || !spec->ext_mic.pin) + return; + if (snd_BUG_ON(!spec->adc_nids)) + return; + + cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0]; + + present = snd_hda_codec_read(codec, spec->ext_mic.pin, 0, + AC_VERB_GET_PIN_SENSE, 0); + present &= AC_PINSENSE_PRESENCE; + if (present) { + alive = &spec->ext_mic; + dead = &spec->int_mic; + } else { + alive = &spec->int_mic; + dead = &spec->ext_mic; + } + + type = get_wcaps_type(get_wcaps(codec, cap_nid)); + if (type == AC_WID_AUD_MIX) { + /* Matrix-mixer style (e.g. ALC882) */ + snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT, + alive->mux_idx, + HDA_AMP_MUTE, 0); + snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT, + dead->mux_idx, + HDA_AMP_MUTE, HDA_AMP_MUTE); + } else { + /* MUX style (e.g. ALC880) */ + snd_hda_codec_write_cache(codec, cap_nid, 0, + AC_VERB_SET_CONNECT_SEL, + alive->mux_idx); + } + + /* FIXME: analog mixer */ } -#else -#define alc_mic_automute(codec) do {} while(0) /* NOP */ -#endif /* disabled */ /* unsolicited event for HP jack sensing */ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) @@ -1031,6 +1081,16 @@ static void alc888_coef_init(struct hda_codec *codec) AC_VERB_SET_PROC_COEF, 0x3030); } +static void alc889_coef_init(struct hda_codec *codec) +{ + unsigned int tmp; + + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7); + tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, tmp|0x2010); +} + static void alc_auto_init_amp(struct hda_codec *codec, int type) { unsigned int tmp; @@ -1088,15 +1148,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) case 0x10ec0885: case 0x10ec0887: case 0x10ec0889: - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_COEF_INDEX, 7); - tmp = snd_hda_codec_read(codec, 0x20, 0, - AC_VERB_GET_PROC_COEF, 0); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_COEF_INDEX, 7); - snd_hda_codec_write(codec, 0x20, 0, - AC_VERB_SET_PROC_COEF, - tmp | 0x2010); + alc889_coef_init(codec); break; case 0x10ec0888: alc888_coef_init(codec); @@ -1142,6 +1194,55 @@ static void alc_init_auto_hp(struct hda_codec *codec) spec->unsol_event = alc_sku_unsol_event; } +static void alc_init_auto_mic(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t fixed, ext; + int i; + + /* there must be only two mic inputs exclusively */ + for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) + if (cfg->input_pins[i]) + return; + + fixed = ext = 0; + for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) { + hda_nid_t nid = cfg->input_pins[i]; + unsigned int defcfg; + if (!nid) + return; + defcfg = snd_hda_codec_get_pincfg(codec, nid); + switch (get_defcfg_connect(defcfg)) { + case AC_JACK_PORT_FIXED: + if (fixed) + return; /* already occupied */ + fixed = nid; + break; + case AC_JACK_PORT_COMPLEX: + if (ext) + return; /* already occupied */ + ext = nid; + break; + default: + return; /* invalid entry */ + } + } + if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP)) + return; /* no unsol support */ + snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x\n", + ext, fixed); + spec->ext_mic.pin = ext; + spec->int_mic.pin = fixed; + spec->ext_mic.mux_idx = MUX_IDX_UNDEF; /* set later */ + spec->int_mic.mux_idx = MUX_IDX_UNDEF; /* set later */ + spec->auto_mic = 1; + snd_hda_codec_write_cache(codec, spec->ext_mic.pin, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | ALC880_MIC_EVENT); + spec->unsol_event = alc_sku_unsol_event; +} + /* check subsystem ID and set up device-specific initialization; * return 1 if initialized, 0 if invalid SSID */ @@ -1231,18 +1332,24 @@ do_sku: * when the external headphone out jack is plugged" */ if (!spec->autocfg.hp_pins[0]) { + hda_nid_t nid; tmp = (ass >> 11) & 0x3; /* HP to chassis */ if (tmp == 0) - spec->autocfg.hp_pins[0] = porta; + nid = porta; else if (tmp == 1) - spec->autocfg.hp_pins[0] = porte; + nid = porte; else if (tmp == 2) - spec->autocfg.hp_pins[0] = portd; + nid = portd; else return 1; + for (i = 0; i < spec->autocfg.line_outs; i++) + if (spec->autocfg.line_out_pins[i] == nid) + return 1; + spec->autocfg.hp_pins[0] = nid; } alc_init_auto_hp(codec); + alc_init_auto_mic(codec); return 1; } @@ -1255,11 +1362,12 @@ static void alc_ssid_check(struct hda_codec *codec, "Enable default setup for auto mode as fallback\n"); spec->init_amp = ALC_INIT_DEFAULT; alc_init_auto_hp(codec); + alc_init_auto_mic(codec); } } /* - * Fix-up pin default configurations + * Fix-up pin default configurations and add default verbs */ struct alc_pincfg { @@ -1267,9 +1375,14 @@ struct alc_pincfg { u32 val; }; -static void alc_fix_pincfg(struct hda_codec *codec, +struct alc_fixup { + const struct alc_pincfg *pins; + const struct hda_verb *verbs; +}; + +static void alc_pick_fixup(struct hda_codec *codec, const struct snd_pci_quirk *quirk, - const struct alc_pincfg **pinfix) + const struct alc_fixup *fix) { const struct alc_pincfg *cfg; @@ -1277,9 +1390,14 @@ static void alc_fix_pincfg(struct hda_codec *codec, if (!quirk) return; - cfg = pinfix[quirk->value]; - for (; cfg->nid; cfg++) - snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); + fix += quirk->value; + cfg = fix->pins; + if (cfg) { + for (; cfg->nid; cfg++) + snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); + } + if (fix->verbs) + add_verb(codec->spec, fix->verbs); } /* @@ -1436,7 +1554,25 @@ static void alc_automute_amp_unsol_event(struct hda_codec *codec, alc_automute_amp(codec); } -static void alc888_fujitsu_xa3530_init_hook(struct hda_codec *codec) +static void alc889_automute_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x17; + spec->autocfg.speaker_pins[3] = 0x19; + spec->autocfg.speaker_pins[4] = 0x1a; +} + +static void alc889_intel_init_hook(struct hda_codec *codec) +{ + alc889_coef_init(codec); + alc_automute_amp(codec); +} + +static void alc888_fujitsu_xa3530_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1444,7 +1580,6 @@ static void alc888_fujitsu_xa3530_init_hook(struct hda_codec *codec) spec->autocfg.hp_pins[1] = 0x1b; /* hp */ spec->autocfg.speaker_pins[0] = 0x14; /* speaker */ spec->autocfg.speaker_pins[1] = 0x15; /* bass */ - alc_automute_amp(codec); } /* @@ -1643,16 +1778,15 @@ static struct snd_kcontrol_new alc888_base_mixer[] = { { } /* end */ }; -static void alc888_acer_aspire_4930g_init_hook(struct hda_codec *codec) +static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; - alc_automute_amp(codec); } -static void alc888_acer_aspire_6530g_init_hook(struct hda_codec *codec) +static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1660,10 +1794,9 @@ static void alc888_acer_aspire_6530g_init_hook(struct hda_codec *codec) spec->autocfg.speaker_pins[0] = 0x14; spec->autocfg.speaker_pins[1] = 0x16; spec->autocfg.speaker_pins[2] = 0x17; - alc_automute_amp(codec); } -static void alc889_acer_aspire_8930g_init_hook(struct hda_codec *codec) +static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1671,7 +1804,6 @@ static void alc889_acer_aspire_8930g_init_hook(struct hda_codec *codec) spec->autocfg.speaker_pins[0] = 0x14; spec->autocfg.speaker_pins[1] = 0x16; spec->autocfg.speaker_pins[2] = 0x1b; - alc_automute_amp(codec); } /* @@ -2651,13 +2783,17 @@ static void alc880_uniwill_mic_automute(struct hda_codec *codec) snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); } -static void alc880_uniwill_init_hook(struct hda_codec *codec) +static void alc880_uniwill_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x14; spec->autocfg.speaker_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x16; +} + +static void alc880_uniwill_init_hook(struct hda_codec *codec) +{ alc_automute_amp(codec); alc880_uniwill_mic_automute(codec); } @@ -2678,13 +2814,12 @@ static void alc880_uniwill_unsol_event(struct hda_codec *codec, } } -static void alc880_uniwill_p53_init_hook(struct hda_codec *codec) +static void alc880_uniwill_p53_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x14; spec->autocfg.speaker_pins[0] = 0x15; - alc_automute_amp(codec); } static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec) @@ -2947,13 +3082,12 @@ static struct hda_verb alc880_lg_init_verbs[] = { }; /* toggle speaker-output according to the hp-jack state */ -static void alc880_lg_init_hook(struct hda_codec *codec) +static void alc880_lg_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x1b; spec->autocfg.speaker_pins[0] = 0x17; - alc_automute_amp(codec); } /* @@ -3032,13 +3166,12 @@ static struct hda_verb alc880_lg_lw_init_verbs[] = { }; /* toggle speaker-output according to the hp-jack state */ -static void alc880_lg_lw_init_hook(struct hda_codec *codec) +static void alc880_lg_lw_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x1b; spec->autocfg.speaker_pins[0] = 0x14; - alc_automute_amp(codec); } static struct snd_kcontrol_new alc880_medion_rim_mixer[] = { @@ -3104,13 +3237,12 @@ static void alc880_medion_rim_unsol_event(struct hda_codec *codec, alc880_medion_rim_automute(codec); } -static void alc880_medion_rim_init_hook(struct hda_codec *codec) +static void alc880_medion_rim_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x14; spec->autocfg.speaker_pins[0] = 0x1b; - alc880_medion_rim_automute(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -3977,7 +4109,8 @@ static struct alc_config_preset alc880_presets[] = { .channel_mode = alc880_2_jack_modes, .input_mux = &alc880_f1734_capture_source, .unsol_event = alc880_uniwill_p53_unsol_event, - .init_hook = alc880_uniwill_p53_init_hook, + .setup = alc880_uniwill_p53_setup, + .init_hook = alc_automute_amp, }, [ALC880_ASUS] = { .mixers = { alc880_asus_mixer }, @@ -4054,6 +4187,7 @@ static struct alc_config_preset alc880_presets[] = { .need_dac_fix = 1, .input_mux = &alc880_capture_source, .unsol_event = alc880_uniwill_unsol_event, + .setup = alc880_uniwill_setup, .init_hook = alc880_uniwill_init_hook, }, [ALC880_UNIWILL_P53] = { @@ -4066,7 +4200,8 @@ static struct alc_config_preset alc880_presets[] = { .channel_mode = alc880_threestack_modes, .input_mux = &alc880_capture_source, .unsol_event = alc880_uniwill_p53_unsol_event, - .init_hook = alc880_uniwill_p53_init_hook, + .setup = alc880_uniwill_p53_setup, + .init_hook = alc_automute_amp, }, [ALC880_FUJITSU] = { .mixers = { alc880_fujitsu_mixer }, @@ -4080,7 +4215,8 @@ static struct alc_config_preset alc880_presets[] = { .channel_mode = alc880_2_jack_modes, .input_mux = &alc880_capture_source, .unsol_event = alc880_uniwill_p53_unsol_event, - .init_hook = alc880_uniwill_p53_init_hook, + .setup = alc880_uniwill_p53_setup, + .init_hook = alc_automute_amp, }, [ALC880_CLEVO] = { .mixers = { alc880_three_stack_mixer }, @@ -4106,7 +4242,8 @@ static struct alc_config_preset alc880_presets[] = { .need_dac_fix = 1, .input_mux = &alc880_lg_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc880_lg_init_hook, + .setup = alc880_lg_setup, + .init_hook = alc_automute_amp, #ifdef CONFIG_SND_HDA_POWER_SAVE .loopbacks = alc880_lg_loopbacks, #endif @@ -4122,7 +4259,8 @@ static struct alc_config_preset alc880_presets[] = { .channel_mode = alc880_lg_lw_modes, .input_mux = &alc880_lg_lw_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc880_lg_lw_init_hook, + .setup = alc880_lg_lw_setup, + .init_hook = alc_automute_amp, }, [ALC880_MEDION_RIM] = { .mixers = { alc880_medion_rim_mixer }, @@ -4136,7 +4274,8 @@ static struct alc_config_preset alc880_presets[] = { .channel_mode = alc880_2_jack_modes, .input_mux = &alc880_medion_rim_capture_source, .unsol_event = alc880_medion_rim_unsol_event, - .init_hook = alc880_medion_rim_init_hook, + .setup = alc880_medion_rim_setup, + .init_hook = alc880_medion_rim_automute, }, #ifdef CONFIG_SND_DEBUG [ALC880_TEST] = { @@ -4189,8 +4328,6 @@ static int add_control(struct alc_spec *spec, int type, const char *name, #define alc880_fixed_pin_idx(nid) ((nid) - 0x14) #define alc880_is_multi_pin(nid) ((nid) >= 0x18) #define alc880_multi_pin_idx(nid) ((nid) - 0x18) -#define alc880_is_input_pin(nid) ((nid) >= 0x18) -#define alc880_input_pin_idx(nid) ((nid) - 0x18) #define alc880_idx_to_dac(nid) ((nid) + 0x02) #define alc880_dac_to_idx(nid) ((nid) - 0x02) #define alc880_idx_to_mixer(nid) ((nid) + 0x0c) @@ -4278,13 +4415,19 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, if (err < 0) return err; } else { - sprintf(name, "%s Playback Volume", chname[i]); + const char *pfx; + if (cfg->line_outs == 1 && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + pfx = "Speaker"; + else + pfx = chname[i]; + sprintf(name, "%s Playback Volume", pfx); err = add_control(spec, ALC_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]); + sprintf(name, "%s Playback Switch", pfx); err = add_control(spec, ALC_CTL_BIND_MUTE, name, HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); @@ -4358,31 +4501,61 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, return 0; } +static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int pincap = snd_hda_query_pin_caps(codec, nid); + return (pincap & AC_PINCAP_IN) != 0; +} + /* create playback/capture controls for input pins */ -static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) +static int alc_auto_create_input_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg, + hda_nid_t mixer, + hda_nid_t cap1, hda_nid_t cap2) { + struct alc_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->private_imux[0]; int i, err, idx; for (i = 0; i < AUTO_PIN_LAST; i++) { - if (alc880_is_input_pin(cfg->input_pins[i])) { - idx = alc880_input_pin_idx(cfg->input_pins[i]); - err = new_analog_input(spec, cfg->input_pins[i], - auto_pin_cfg_labels[i], - idx, 0x0b); - if (err < 0) - return err; + hda_nid_t pin; + + pin = cfg->input_pins[i]; + if (!alc_is_input_pin(codec, pin)) + continue; + + if (mixer) { + idx = get_connection_index(codec, mixer, pin); + if (idx >= 0) { + err = new_analog_input(spec, pin, + auto_pin_cfg_labels[i], + idx, mixer); + if (err < 0) + return err; + } + } + + if (!cap1) + continue; + idx = get_connection_index(codec, cap1, pin); + if (idx < 0 && cap2) + idx = get_connection_index(codec, cap2, pin); + if (idx >= 0) { imux->items[imux->num_items].label = auto_pin_cfg_labels[i]; - imux->items[imux->num_items].index = - alc880_input_pin_idx(cfg->input_pins[i]); + imux->items[imux->num_items].index = idx; imux->num_items++; } } return 0; } +static int alc880_auto_create_input_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x08, 0x09); +} + static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, unsigned int pin_type) { @@ -4448,7 +4621,7 @@ static void alc880_auto_init_analog_input(struct hda_codec *codec) for (i = 0; i < AUTO_PIN_LAST; i++) { hda_nid_t nid = spec->autocfg.input_pins[i]; - if (alc880_is_input_pin(nid)) { + if (alc_is_input_pin(codec, nid)) { alc_set_input_pin(codec, nid, i); if (nid != ALC880_PIN_CD_NID && (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) @@ -4491,7 +4664,7 @@ static int alc880_parse_auto_config(struct hda_codec *codec) "Headphone"); if (err < 0) return err; - err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = alc880_auto_create_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -4505,12 +4678,6 @@ static int alc880_parse_auto_config(struct hda_codec *codec) &dig_nid, 1); if (err < 0) continue; - if (dig_nid > 0x7f) { - printk(KERN_ERR "alc880_auto: invalid dig_nid " - "connection 0x%x for NID 0x%x\n", dig_nid, - spec->autocfg.dig_out_pins[i]); - continue; - } if (!i) spec->multiout.dig_out_nid = dig_nid; else { @@ -4547,8 +4714,42 @@ static void alc880_auto_init(struct hda_codec *codec) alc_inithook(codec); } -static void set_capture_mixer(struct alc_spec *spec) +/* check the ADC/MUX contains all input pins; some ADC/MUX contains only + * one of two digital mic pins, e.g. on ALC272 + */ +static void fixup_automic_adc(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t cap = spec->capsrc_nids ? + spec->capsrc_nids[i] : spec->adc_nids[i]; + int iidx, eidx; + + iidx = get_connection_index(codec, cap, spec->int_mic.pin); + if (iidx < 0) + continue; + eidx = get_connection_index(codec, cap, spec->ext_mic.pin); + if (eidx < 0) + continue; + spec->int_mic.mux_idx = iidx; + spec->ext_mic.mux_idx = eidx; + if (spec->capsrc_nids) + spec->capsrc_nids += i; + spec->adc_nids += i; + spec->num_adc_nids = 1; + return; + } + snd_printd(KERN_INFO "hda_codec: %s: " + "No ADC/MUX containing both 0x%x and 0x%x pins\n", + codec->chip_name, spec->int_mic.pin, spec->ext_mic.pin); + spec->auto_mic = 0; /* disable auto-mic to be sure */ +} + +static void set_capture_mixer(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; static struct snd_kcontrol_new *caps[2][3] = { { alc_capture_mixer_nosrc1, alc_capture_mixer_nosrc2, @@ -4559,7 +4760,10 @@ static void set_capture_mixer(struct alc_spec *spec) }; if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) { int mux; - if (spec->input_mux && spec->input_mux->num_items > 1) + if (spec->auto_mic) { + mux = 0; + fixup_automic_adc(codec); + } else if (spec->input_mux && spec->input_mux->num_items > 1) mux = 1; else mux = 0; @@ -4590,8 +4794,8 @@ static int patch_alc880(struct hda_codec *codec) alc880_models, alc880_cfg_tbl); if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for %s, " - "trying auto-probe from BIOS...\n", codec->chip_name); + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); board_config = ALC880_AUTO; } @@ -4616,7 +4820,7 @@ static int patch_alc880(struct hda_codec *codec) } if (board_config != ALC880_AUTO) - setup_preset(spec, &alc880_presets[board_config]); + setup_preset(codec, &alc880_presets[board_config]); spec->stream_analog_playback = &alc880_pcm_analog_playback; spec->stream_analog_capture = &alc880_pcm_analog_capture; @@ -4629,7 +4833,7 @@ static int patch_alc880(struct hda_codec *codec) /* check whether NID 0x07 is valid */ unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]); /* get type */ - wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + wcap = get_wcaps_type(wcap); if (wcap != AC_WID_AUD_IN) { spec->adc_nids = alc880_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt); @@ -4638,7 +4842,7 @@ static int patch_alc880(struct hda_codec *codec) spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids); } } - set_capture_mixer(spec); + set_capture_mixer(codec); set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); spec->vmaster_nid = 0x0c; @@ -5830,7 +6034,14 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec, nid = cfg->line_out_pins[0]; if (nid) { - err = alc260_add_playback_controls(spec, nid, "Front", &vols); + const char *pfx; + if (!cfg->speaker_pins[0] && !cfg->hp_pins[0]) + pfx = "Master"; + else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + pfx = "Speaker"; + else + pfx = "Front"; + err = alc260_add_playback_controls(spec, nid, pfx, &vols); if (err < 0) return err; } @@ -5853,39 +6064,10 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec, } /* create playback/capture controls for input pins */ -static int alc260_auto_create_analog_input_ctls(struct alc_spec *spec, +static int alc260_auto_create_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx; - - for (i = 0; i < AUTO_PIN_LAST; i++) { - if (cfg->input_pins[i] >= 0x12) { - idx = cfg->input_pins[i] - 0x12; - err = new_analog_input(spec, cfg->input_pins[i], - auto_pin_cfg_labels[i], idx, - 0x07); - if (err < 0) - return err; - imux->items[imux->num_items].label = - auto_pin_cfg_labels[i]; - imux->items[imux->num_items].index = idx; - imux->num_items++; - } - if (cfg->input_pins[i] >= 0x0f && cfg->input_pins[i] <= 0x10){ - idx = cfg->input_pins[i] - 0x09; - err = new_analog_input(spec, cfg->input_pins[i], - auto_pin_cfg_labels[i], idx, - 0x07); - if (err < 0) - return err; - imux->items[imux->num_items].label = - auto_pin_cfg_labels[i]; - imux->items[imux->num_items].index = idx; - imux->num_items++; - } - } - return 0; + return alc_auto_create_input_ctls(codec, cfg, 0x07, 0x04, 0x05); } static void alc260_auto_set_output_and_unmute(struct hda_codec *codec, @@ -5999,7 +6181,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec) return err; if (!spec->kctls.list) return 0; /* can't find valid BIOS pin config */ - err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = alc260_auto_create_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -6234,8 +6416,7 @@ static int patch_alc260(struct hda_codec *codec) alc260_models, alc260_cfg_tbl); if (board_config < 0) { - snd_printd(KERN_INFO "hda_codec: Unknown model for %s, " - "trying auto-probe from BIOS...\n", + snd_printd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", codec->chip_name); board_config = ALC260_AUTO; } @@ -6261,7 +6442,7 @@ static int patch_alc260(struct hda_codec *codec) } if (board_config != ALC260_AUTO) - setup_preset(spec, &alc260_presets[board_config]); + setup_preset(codec, &alc260_presets[board_config]); spec->stream_analog_playback = &alc260_pcm_analog_playback; spec->stream_analog_capture = &alc260_pcm_analog_capture; @@ -6272,7 +6453,7 @@ static int patch_alc260(struct hda_codec *codec) if (!spec->adc_nids && spec->input_mux) { /* check whether NID 0x04 is valid */ unsigned int wcap = get_wcaps(codec, 0x04); - wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + wcap = get_wcaps_type(wcap); /* get type */ if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { spec->adc_nids = alc260_adc_nids_alt; @@ -6282,7 +6463,7 @@ static int patch_alc260(struct hda_codec *codec) spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids); } } - set_capture_mixer(spec); + set_capture_mixer(codec); set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); spec->vmaster_nid = 0x08; @@ -6301,7 +6482,7 @@ static int patch_alc260(struct hda_codec *codec) /* - * ALC882 support + * ALC882/883/885/888/889 support * * ALC882 is almost identical with ALC880 but has cleaner and more flexible * configuration. Each pin widget can choose any input DACs and a mixer. @@ -6313,22 +6494,35 @@ static int patch_alc260(struct hda_codec *codec) */ #define ALC882_DIGOUT_NID 0x06 #define ALC882_DIGIN_NID 0x0a +#define ALC883_DIGOUT_NID ALC882_DIGOUT_NID +#define ALC883_DIGIN_NID ALC882_DIGIN_NID +#define ALC1200_DIGOUT_NID 0x10 + static struct hda_channel_mode alc882_ch_modes[1] = { { 8, NULL } }; +/* DACs */ static hda_nid_t alc882_dac_nids[4] = { /* front, rear, clfe, rear_surr */ 0x02, 0x03, 0x04, 0x05 }; +#define alc883_dac_nids alc882_dac_nids -/* identical with ALC880 */ +/* ADCs */ #define alc882_adc_nids alc880_adc_nids #define alc882_adc_nids_alt alc880_adc_nids_alt +#define alc883_adc_nids alc882_adc_nids_alt +static hda_nid_t alc883_adc_nids_alt[1] = { 0x08 }; +static hda_nid_t alc883_adc_nids_rev[2] = { 0x09, 0x08 }; +#define alc889_adc_nids alc880_adc_nids static hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 }; static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 }; +#define alc883_capsrc_nids alc882_capsrc_nids_alt +static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 }; +#define alc889_capsrc_nids alc882_capsrc_nids /* input MUX */ /* FIXME: should be a matrix-type input source selection */ @@ -6343,6 +6537,17 @@ static struct hda_input_mux alc882_capture_source = { }, }; +#define alc883_capture_source alc882_capture_source + +static struct hda_input_mux alc889_capture_source = { + .num_items = 3, + .items = { + { "Front Mic", 0x0 }, + { "Mic", 0x3 }, + { "Line", 0x2 }, + }, +}; + static struct hda_input_mux mb5_capture_source = { .num_items = 3, .items = { @@ -6352,6 +6557,77 @@ static struct hda_input_mux mb5_capture_source = { }, }; +static struct hda_input_mux alc883_3stack_6ch_intel = { + .num_items = 4, + .items = { + { "Mic", 0x1 }, + { "Front Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +static struct hda_input_mux alc883_lenovo_101e_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, + { "Line", 0x2 }, + }, +}; + +static struct hda_input_mux alc883_lenovo_nb0763_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "iMic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + +static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Int Mic", 0x1 }, + }, +}; + +static struct hda_input_mux alc883_lenovo_sky_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x4 }, + }, +}; + +static struct hda_input_mux alc883_asus_eee1601_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + }, +}; + +static struct hda_input_mux alc889A_mb31_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + /* Front Mic (0x01) unused */ + { "Line", 0x2 }, + /* Line 2 (0x03) unused */ + /* CD (0x04) unsused? */ + }, +}; + +/* + * 2ch mode + */ +static struct hda_channel_mode alc883_3ST_2ch_modes[1] = { + { 2, NULL } +}; + /* * 2ch mode */ @@ -6364,6 +6640,18 @@ static struct hda_verb alc882_3ST_ch2_init[] = { }; /* + * 4ch mode + */ +static struct hda_verb alc882_3ST_ch4_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* * 6ch mode */ static struct hda_verb alc882_3ST_ch6_init[] = { @@ -6376,11 +6664,60 @@ static struct hda_verb alc882_3ST_ch6_init[] = { { } /* end */ }; -static struct hda_channel_mode alc882_3ST_6ch_modes[2] = { +static struct hda_channel_mode alc882_3ST_6ch_modes[3] = { { 2, alc882_3ST_ch2_init }, + { 4, alc882_3ST_ch4_init }, { 6, alc882_3ST_ch6_init }, }; +#define alc883_3ST_6ch_modes alc882_3ST_6ch_modes + +/* + * 2ch mode + */ +static struct hda_verb alc883_3ST_ch2_clevo_init[] = { + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 4ch mode + */ +static struct hda_verb alc883_3ST_ch4_clevo_init[] = { + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static struct hda_verb alc883_3ST_ch6_clevo_init[] = { + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +static struct hda_channel_mode alc883_3ST_6ch_clevo_modes[3] = { + { 2, alc883_3ST_ch2_clevo_init }, + { 4, alc883_3ST_ch4_clevo_init }, + { 6, alc883_3ST_ch6_clevo_init }, +}; + + /* * 6ch mode */ @@ -6468,6 +6805,189 @@ static struct hda_channel_mode alc885_mb5_6ch_modes[2] = { { 6, alc885_mb5_ch6_init }, }; + +/* + * 2ch mode + */ +static struct hda_verb alc883_4ST_ch2_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 4ch mode + */ +static struct hda_verb alc883_4ST_ch4_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static struct hda_verb alc883_4ST_ch6_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static struct hda_verb alc883_4ST_ch8_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +static struct hda_channel_mode alc883_4ST_8ch_modes[4] = { + { 2, alc883_4ST_ch2_init }, + { 4, alc883_4ST_ch4_init }, + { 6, alc883_4ST_ch6_init }, + { 8, alc883_4ST_ch8_init }, +}; + + +/* + * 2ch mode + */ +static struct hda_verb alc883_3ST_ch2_intel_init[] = { + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 4ch mode + */ +static struct hda_verb alc883_3ST_ch4_intel_init[] = { + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static struct hda_verb alc883_3ST_ch6_intel_init[] = { + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +static struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = { + { 2, alc883_3ST_ch2_intel_init }, + { 4, alc883_3ST_ch4_intel_init }, + { 6, alc883_3ST_ch6_intel_init }, +}; + +/* + * 2ch mode + */ +static struct hda_verb alc889_ch2_intel_init[] = { + { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x19, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x16, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static struct hda_verb alc889_ch6_intel_init[] = { + { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static struct hda_verb alc889_ch8_intel_init[] = { + { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x03 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { } /* end */ +}; + +static struct hda_channel_mode alc889_8ch_intel_modes[3] = { + { 2, alc889_ch2_intel_init }, + { 6, alc889_ch6_intel_init }, + { 8, alc889_ch8_intel_init }, +}; + +/* + * 6ch mode + */ +static struct hda_verb alc883_sixstack_ch6_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static struct hda_verb alc883_sixstack_ch8_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +static struct hda_channel_mode alc883_sixstack_modes[2] = { + { 6, alc883_sixstack_ch6_init }, + { 8, alc883_sixstack_ch8_init }, +}; + + /* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b */ @@ -6604,7 +7124,7 @@ static struct snd_kcontrol_new alc882_chmode_mixer[] = { { } /* end */ }; -static struct hda_verb alc882_init_verbs[] = { +static struct hda_verb alc882_base_init_verbs[] = { /* Front mixer: unmute input/output amp left and right (volume = 0) */ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, @@ -6622,6 +7142,13 @@ static struct hda_verb alc882_init_verbs[] = { {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* mute analog input loopbacks */ + {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)}, + /* Front Pin: output 0 (0x0c) */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, @@ -6656,11 +7183,6 @@ static struct hda_verb alc882_init_verbs[] = { /* FIXME: use matrix-type input source selection */ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 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(3)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* Input mixer2 */ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, @@ -6671,9 +7193,6 @@ static struct hda_verb alc882_init_verbs[] = { {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* ADC1: mute amp left and right */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, /* ADC2: mute amp left and right */ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, @@ -6684,6 +7203,18 @@ static struct hda_verb alc882_init_verbs[] = { { } }; +static struct hda_verb alc882_adc1_init_verbs[] = { + /* 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(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* ADC1: mute amp left and right */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } +}; + static struct hda_verb alc882_eapd_verbs[] = { /* change to EAPD mode */ {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, @@ -6691,6 +7222,110 @@ static struct hda_verb alc882_eapd_verbs[] = { { } }; +static struct hda_verb alc889_eapd_verbs[] = { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +static struct hda_verb alc_hp15_unsol_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static struct hda_verb alc885_init_verbs[] = { + /* Front mixer: unmute input/output amp left and right (volume = 0) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Rear mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* CLFE mixer */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Side mixer */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + + /* mute analog input loopbacks */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + + /* Front HP Pin: output 0 (0x0c) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Front Pin: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Rear Pin: output 1 (0x0d) */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x19, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* CLFE Pin: output 2 (0x0e) */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* Side Pin: output 3 (0x0f) */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + /* Mic (rear) pin: input vref at 80% */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin: input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + /* Mixer elements: 0x18, , 0x1a, 0x1b */ + /* Input mixer1 */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + /* ADC2: mute amp left and right */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + /* ADC3: mute amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + + { } +}; + +static struct hda_verb alc885_init_input_verbs[] = { + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + { } +}; + + +/* Unmute Selector 24h and set the default input to front mic */ +static struct hda_verb alc889_init_input_verbs[] = { + {0x24, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + { } +}; + + +#define alc883_init_verbs alc882_base_init_verbs + /* Mac Pro test */ static struct snd_kcontrol_new alc882_macpro_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), @@ -6898,23 +7533,21 @@ static struct hda_verb alc885_imac24_init_verbs[] = { }; /* Toggle speaker-output according to the hp-jack state */ -static void alc885_imac24_automute_init_hook(struct hda_codec *codec) +static void alc885_imac24_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x14; spec->autocfg.speaker_pins[0] = 0x18; spec->autocfg.speaker_pins[1] = 0x1a; - alc_automute_amp(codec); } -static void alc885_mbp3_init_hook(struct hda_codec *codec) +static void alc885_mbp3_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; - alc_automute_amp(codec); } @@ -6942,13 +7575,12 @@ static void alc882_targa_automute(struct hda_codec *codec) spec->jack_present ? 1 : 3); } -static void alc882_targa_init_hook(struct hda_codec *codec) +static void alc882_targa_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x14; spec->autocfg.speaker_pins[0] = 0x1b; - alc882_targa_automute(codec); } static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res) @@ -7036,18 +7668,16 @@ static void alc885_macpro_init_hook(struct hda_codec *codec) static void alc885_imac24_init_hook(struct hda_codec *codec) { alc885_macpro_init_hook(codec); - alc885_imac24_automute_init_hook(codec); + alc_automute_amp(codec); } /* * generic initialization of ADC, input mixers and output mixers */ -static struct hda_verb alc882_auto_init_verbs[] = { +static struct hda_verb alc883_auto_init_verbs[] = { /* * Unmute ADC0-2 and set the default input to mic-in */ - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, @@ -7088,11 +7718,6 @@ static struct hda_verb alc882_auto_init_verbs[] = { /* FIXME: use matrix-type input source selection */ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ - /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, /* Input mixer2 */ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, @@ -7107,821 +7732,6 @@ static struct hda_verb alc882_auto_init_verbs[] = { { } }; -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc882_loopbacks alc880_loopbacks -#endif - -/* pcm configuration: identical with ALC880 */ -#define alc882_pcm_analog_playback alc880_pcm_analog_playback -#define alc882_pcm_analog_capture alc880_pcm_analog_capture -#define alc882_pcm_digital_playback alc880_pcm_digital_playback -#define alc882_pcm_digital_capture alc880_pcm_digital_capture - -/* - * configuration and preset - */ -static const char *alc882_models[ALC882_MODEL_LAST] = { - [ALC882_3ST_DIG] = "3stack-dig", - [ALC882_6ST_DIG] = "6stack-dig", - [ALC882_ARIMA] = "arima", - [ALC882_W2JC] = "w2jc", - [ALC882_TARGA] = "targa", - [ALC882_ASUS_A7J] = "asus-a7j", - [ALC882_ASUS_A7M] = "asus-a7m", - [ALC885_MACPRO] = "macpro", - [ALC885_MB5] = "mb5", - [ALC885_MBP3] = "mbp3", - [ALC885_IMAC24] = "imac24", - [ALC882_AUTO] = "auto", -}; - -static struct snd_pci_quirk alc882_cfg_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG), - 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(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), - {} -}; - -static struct alc_config_preset alc882_presets[] = { - [ALC882_3ST_DIG] = { - .mixers = { alc882_base_mixer }, - .init_verbs = { alc882_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), - .channel_mode = alc882_ch_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - }, - [ALC882_6ST_DIG] = { - .mixers = { alc882_base_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), - .channel_mode = alc882_sixstack_modes, - .input_mux = &alc882_capture_source, - }, - [ALC882_ARIMA] = { - .mixers = { alc882_base_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_init_verbs, alc882_eapd_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), - .channel_mode = alc882_sixstack_modes, - .input_mux = &alc882_capture_source, - }, - [ALC882_W2JC] = { - .mixers = { alc882_w2jc_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_init_verbs, alc882_eapd_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), - .channel_mode = alc880_threestack_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - .dig_out_nid = ALC882_DIGOUT_NID, - }, - [ALC885_MBP3] = { - .mixers = { alc885_mbp3_mixer, alc882_chmode_mixer }, - .init_verbs = { alc885_mbp3_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = 2, - .dac_nids = alc882_dac_nids, - .hp_nid = 0x04, - .channel_mode = alc885_mbp_4ch_modes, - .num_channel_mode = ARRAY_SIZE(alc885_mbp_4ch_modes), - .input_mux = &alc882_capture_source, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc885_mbp3_init_hook, - }, - [ALC885_MB5] = { - .mixers = { alc885_mb5_mixer, alc882_chmode_mixer }, - .init_verbs = { alc885_mb5_init_verbs, - alc880_gpio1_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .channel_mode = alc885_mb5_6ch_modes, - .num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes), - .input_mux = &mb5_capture_source, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - }, - [ALC885_MACPRO] = { - .mixers = { alc882_macpro_mixer }, - .init_verbs = { alc882_macpro_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), - .channel_mode = alc882_ch_modes, - .input_mux = &alc882_capture_source, - .init_hook = alc885_macpro_init_hook, - }, - [ALC885_IMAC24] = { - .mixers = { alc885_imac24_mixer }, - .init_verbs = { alc885_imac24_init_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .dig_in_nid = ALC882_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), - .channel_mode = alc882_ch_modes, - .input_mux = &alc882_capture_source, - .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc885_imac24_init_hook, - }, - [ALC882_TARGA] = { - .mixers = { alc882_targa_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_init_verbs, alc880_gpio3_init_verbs, - alc882_targa_verbs}, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), - .adc_nids = alc882_adc_nids, - .capsrc_nids = alc882_capsrc_nids, - .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), - .channel_mode = alc882_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - .unsol_event = alc882_targa_unsol_event, - .init_hook = alc882_targa_init_hook, - }, - [ALC882_ASUS_A7J] = { - .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_init_verbs, alc882_asus_a7j_verbs}, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), - .adc_nids = alc882_adc_nids, - .capsrc_nids = alc882_capsrc_nids, - .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), - .channel_mode = alc882_3ST_6ch_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - }, - [ALC882_ASUS_A7M] = { - .mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer }, - .init_verbs = { alc882_init_verbs, alc882_eapd_verbs, - alc880_gpio1_init_verbs, - alc882_asus_a7m_verbs }, - .num_dacs = ARRAY_SIZE(alc882_dac_nids), - .dac_nids = alc882_dac_nids, - .dig_out_nid = ALC882_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), - .channel_mode = alc880_threestack_modes, - .need_dac_fix = 1, - .input_mux = &alc882_capture_source, - }, -}; - - -/* - * Pin config fixes - */ -enum { - PINFIX_ABIT_AW9D_MAX -}; - -static struct alc_pincfg alc882_abit_aw9d_pinfix[] = { - { 0x15, 0x01080104 }, /* side */ - { 0x16, 0x01011012 }, /* rear */ - { 0x17, 0x01016011 }, /* clfe */ - { } -}; - -static const struct alc_pincfg *alc882_pin_fixes[] = { - [PINFIX_ABIT_AW9D_MAX] = alc882_abit_aw9d_pinfix, -}; - -static struct snd_pci_quirk alc882_pinfix_tbl[] = { - SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), - {} -}; - -/* - * BIOS auto configuration - */ -static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int dac_idx) -{ - /* set as output */ - struct alc_spec *spec = codec->spec; - int idx; - - alc_set_pin_output(codec, nid, pin_type); - if (spec->multiout.dac_nids[dac_idx] == 0x25) - idx = 4; - else - idx = spec->multiout.dac_nids[dac_idx] - 2; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); - -} - -static void alc882_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - 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); - if (nid) - alc882_auto_set_output_and_unmute(codec, nid, pin_type, - i); - } -} - -static void alc882_auto_init_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t pin; - - pin = spec->autocfg.hp_pins[0]; - if (pin) /* connect to front */ - /* use dac 0 */ - alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); - pin = spec->autocfg.speaker_pins[0]; - if (pin) - alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); -} - -#define alc882_is_input_pin(nid) alc880_is_input_pin(nid) -#define ALC882_PIN_CD_NID ALC880_PIN_CD_NID - -static void alc882_auto_init_analog_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < AUTO_PIN_LAST; i++) { - hda_nid_t nid = spec->autocfg.input_pins[i]; - if (!nid) - continue; - alc_set_input_pin(codec, nid, AUTO_PIN_FRONT_MIC /*i*/); - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - } -} - -static void alc882_auto_init_input_src(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int c; - - for (c = 0; c < spec->num_adc_nids; c++) { - hda_nid_t conn_list[HDA_MAX_NUM_INPUTS]; - hda_nid_t nid = spec->capsrc_nids[c]; - unsigned int mux_idx; - const struct hda_input_mux *imux; - int conns, mute, idx, item; - - conns = snd_hda_get_connections(codec, nid, conn_list, - ARRAY_SIZE(conn_list)); - if (conns < 0) - continue; - mux_idx = c >= spec->num_mux_defs ? 0 : c; - imux = &spec->input_mux[mux_idx]; - for (idx = 0; idx < conns; idx++) { - /* if the current connection is the selected one, - * unmute it as default - otherwise mute it - */ - mute = AMP_IN_MUTE(idx); - for (item = 0; item < imux->num_items; item++) { - if (imux->items[item].index == idx) { - if (spec->cur_mux[c] == item) - mute = AMP_IN_UNMUTE(idx); - break; - } - } - /* check if we have a selector or mixer - * we could check for the widget type instead, but - * just check for Amp-In presence (in case of mixer - * without amp-in there is something wrong, this - * function shouldn't be used or capsrc nid is wrong) - */ - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - mute); - else if (mute != AMP_IN_MUTE(idx)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - idx); - } - } -} - -/* add mic boosts if needed */ -static int alc_auto_add_mic_boost(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int err; - hda_nid_t nid; - - nid = spec->autocfg.input_pins[AUTO_PIN_MIC]; - 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)); - if (err < 0) - return err; - } - nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC]; - 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)); - if (err < 0) - return err; - } - return 0; -} - -/* almost identical with ALC880 parser... */ -static int alc882_parse_auto_config(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int err = alc880_parse_auto_config(codec); - - if (err < 0) - return err; - else if (!err) - return 0; /* no config found */ - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - - /* hack - override the init verbs */ - spec->init_verbs[0] = alc882_auto_init_verbs; - - return 1; /* config found */ -} - -/* additional initialization for auto-configuration model */ -static void alc882_auto_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc882_auto_init_multi_out(codec); - alc882_auto_init_hp_out(codec); - alc882_auto_init_analog_input(codec); - alc882_auto_init_input_src(codec); - if (spec->unsol_event) - alc_inithook(codec); -} - -static int patch_alc883(struct hda_codec *codec); /* called in patch_alc882() */ - -static int patch_alc882(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err, board_config; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST, - alc882_models, - alc882_cfg_tbl); - - if (board_config < 0 || board_config >= ALC882_MODEL_LAST) { - /* Pick up systems that don't supply PCI SSID */ - switch (codec->subsystem_id) { - case 0x106b0c00: /* Mac Pro */ - board_config = ALC885_MACPRO; - break; - case 0x106b1000: /* iMac 24 */ - case 0x106b2800: /* AppleTV */ - case 0x106b3e00: /* iMac 24 Aluminium */ - board_config = ALC885_IMAC24; - break; - case 0x106b00a0: /* MacBookPro3,1 - Another revision */ - case 0x106b00a1: /* Macbook (might be wrong - PCI SSID?) */ - case 0x106b00a4: /* MacbookPro4,1 */ - case 0x106b2c00: /* Macbook Pro rev3 */ - /* Macbook 3.1 (0x106b3600) is handled by patch_alc883() */ - case 0x106b3800: /* MacbookPro4,1 - latter revision */ - board_config = ALC885_MBP3; - break; - case 0x106b3f00: /* Macbook 5,1 */ - case 0x106b4000: /* Macbook Pro 5,1 - FIXME: HP jack sense - * seems not working, so apparently - * no perfect solution yet - */ - board_config = ALC885_MB5; - break; - default: - /* ALC889A is handled better as ALC888-compatible */ - if (codec->revision_id == 0x100101 || - codec->revision_id == 0x100103) { - alc_free(codec); - return patch_alc883(codec); - } - printk(KERN_INFO "hda_codec: Unknown model for %s, " - "trying auto-probe from BIOS...\n", - codec->chip_name); - board_config = ALC882_AUTO; - } - } - - alc_fix_pincfg(codec, alc882_pinfix_tbl, alc882_pin_fixes); - - if (board_config == ALC882_AUTO) { - /* automatic parse from the BIOS config */ - err = alc882_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 = ALC882_3ST_DIG; - } - } - - err = snd_hda_attach_beep_device(codec, 0x1); - if (err < 0) { - alc_free(codec); - return err; - } - - if (board_config != ALC882_AUTO) - setup_preset(spec, &alc882_presets[board_config]); - - 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_digital_playback = &alc882_pcm_digital_playback; - spec->stream_digital_capture = &alc882_pcm_digital_capture; - - 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 = alc882_adc_nids_alt; - spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt); - spec->capsrc_nids = alc882_capsrc_nids_alt; - } else { - spec->adc_nids = alc882_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids); - spec->capsrc_nids = alc882_capsrc_nids; - } - } - set_capture_mixer(spec); - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - - spec->vmaster_nid = 0x0c; - - codec->patch_ops = alc_patch_ops; - if (board_config == ALC882_AUTO) - spec->init_hook = alc882_auto_init; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc882_loopbacks; -#endif - codec->proc_widget_hook = print_realtek_coef; - - return 0; -} - -/* - * ALC883 support - * - * ALC883 is almost identical with ALC880 but has cleaner and more flexible - * configuration. Each pin widget can choose any input DACs and a mixer. - * Each ADC is connected from a mixer of all inputs. This makes possible - * 6-channel independent captures. - * - * In addition, an independent DAC for the multi-playback (not used in this - * driver yet). - */ -#define ALC883_DIGOUT_NID 0x06 -#define ALC883_DIGIN_NID 0x0a - -#define ALC1200_DIGOUT_NID 0x10 - -static hda_nid_t alc883_dac_nids[4] = { - /* front, rear, clfe, rear_surr */ - 0x02, 0x03, 0x04, 0x05 -}; - -static hda_nid_t alc883_adc_nids[2] = { - /* ADC1-2 */ - 0x08, 0x09, -}; - -static hda_nid_t alc883_adc_nids_alt[1] = { - /* ADC1 */ - 0x08, -}; - -static hda_nid_t alc883_adc_nids_rev[2] = { - /* ADC2-1 */ - 0x09, 0x08 -}; - -#define alc889_adc_nids alc880_adc_nids - -static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 }; - -static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 }; - -#define alc889_capsrc_nids alc882_capsrc_nids - -/* input MUX */ -/* FIXME: should be a matrix-type input source selection */ - -static struct hda_input_mux alc883_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -static struct hda_input_mux alc883_3stack_6ch_intel = { - .num_items = 4, - .items = { - { "Mic", 0x1 }, - { "Front Mic", 0x0 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -static struct hda_input_mux alc883_lenovo_101e_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x1 }, - { "Line", 0x2 }, - }, -}; - -static struct hda_input_mux alc883_lenovo_nb0763_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "iMic", 0x1 }, - { "Line", 0x2 }, - { "CD", 0x4 }, - }, -}; - -static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - { "Int Mic", 0x1 }, - }, -}; - -static struct hda_input_mux alc883_lenovo_sky_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Front Mic", 0x1 }, - { "Line", 0x4 }, - }, -}; - -static struct hda_input_mux alc883_asus_eee1601_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - { "Line", 0x2 }, - }, -}; - -static struct hda_input_mux alc889A_mb31_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - /* Front Mic (0x01) unused */ - { "Line", 0x2 }, - /* Line 2 (0x03) unused */ - /* CD (0x04) unsused? */ - }, -}; - -/* - * 2ch mode - */ -static struct hda_channel_mode alc883_3ST_2ch_modes[1] = { - { 2, NULL } -}; - -/* - * 2ch mode - */ -static struct hda_verb alc883_3ST_ch2_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* - * 4ch mode - */ -static struct hda_verb alc883_3ST_ch4_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static struct hda_verb alc883_3ST_ch6_init[] = { - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -static struct hda_channel_mode alc883_3ST_6ch_modes[3] = { - { 2, alc883_3ST_ch2_init }, - { 4, alc883_3ST_ch4_init }, - { 6, alc883_3ST_ch6_init }, -}; - - -/* - * 2ch mode - */ -static struct hda_verb alc883_4ST_ch2_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* - * 4ch mode - */ -static struct hda_verb alc883_4ST_ch4_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static struct hda_verb alc883_4ST_ch6_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -/* - * 8ch mode - */ -static struct hda_verb alc883_4ST_ch8_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 }, - { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -static struct hda_channel_mode alc883_4ST_8ch_modes[4] = { - { 2, alc883_4ST_ch2_init }, - { 4, alc883_4ST_ch4_init }, - { 6, alc883_4ST_ch6_init }, - { 8, alc883_4ST_ch8_init }, -}; - - -/* - * 2ch mode - */ -static struct hda_verb alc883_3ST_ch2_intel_init[] = { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; - -/* - * 4ch mode - */ -static struct hda_verb alc883_3ST_ch4_intel_init[] = { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -/* - * 6ch mode - */ -static struct hda_verb alc883_3ST_ch6_intel_init[] = { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 }, - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { } /* end */ -}; - -static struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = { - { 2, alc883_3ST_ch2_intel_init }, - { 4, alc883_3ST_ch4_intel_init }, - { 6, alc883_3ST_ch6_intel_init }, -}; - -/* - * 6ch mode - */ -static struct hda_verb alc883_sixstack_ch6_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { } /* end */ -}; - -/* - * 8ch mode - */ -static struct hda_verb alc883_sixstack_ch8_init[] = { - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { } /* end */ -}; - -static struct hda_channel_mode alc883_sixstack_modes[2] = { - { 6, alc883_sixstack_ch6_init }, - { 8, alc883_sixstack_ch8_init }, -}; - /* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */ static struct hda_verb alc889A_mb31_ch2_init[] = { {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */ @@ -7972,34 +7782,7 @@ static struct hda_verb alc883_medion_eapd_verbs[] = { { } }; -/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 - * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b - */ - -static struct snd_kcontrol_new alc883_base_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", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround 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), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 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), - { } /* end */ -}; +#define alc883_base_mixer alc882_base_mixer static struct snd_kcontrol_new alc883_mitac_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), @@ -8110,6 +7893,30 @@ static struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc885_8ch_intel_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", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround 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), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 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, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x1b, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + static struct snd_kcontrol_new alc883_fivestack_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -8135,8 +7942,9 @@ static struct snd_kcontrol_new alc883_fivestack_mixer[] = { static struct snd_kcontrol_new alc883_targa_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), @@ -8155,8 +7963,9 @@ static struct snd_kcontrol_new alc883_targa_mixer[] = { static struct snd_kcontrol_new alc883_targa_2ch_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker 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("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), @@ -8168,6 +7977,15 @@ static struct snd_kcontrol_new alc883_targa_2ch_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc883_targa_8ch_mixer[] = { + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + 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), @@ -8350,93 +8168,14 @@ static struct snd_kcontrol_new alc883_chmode_mixer[] = { { } /* end */ }; -static struct hda_verb alc883_init_verbs[] = { - /* ADC1: mute amp left and right */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* ADC2: mute amp left and right */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Front mixer: unmute input/output amp left and right (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Rear mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* CLFE mixer */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Side mixer */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - - /* mute analog input loopbacks */ - {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)}, - - /* Front Pin: output 0 (0x0c) */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Rear Pin: output 1 (0x0d) */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* CLFE Pin: output 2 (0x0e) */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* Side Pin: output 3 (0x0f) */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, - /* Mic (rear) pin: input vref at 80% */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Front Mic pin: input vref at 80% */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line In pin: input */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Line-2 In: Headphone output (output 0 - 0x0c) */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* CD pin widget for input */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - { } -}; - /* toggle speaker-output according to the hp-jack state */ -static void alc883_mitac_init_hook(struct hda_codec *codec) +static void alc883_mitac_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; spec->autocfg.speaker_pins[1] = 0x17; - alc_automute_amp(codec); } /* auto-toggle front mic */ @@ -8468,6 +8207,22 @@ static struct hda_verb alc883_mitac_verbs[] = { { } /* end */ }; +static struct hda_verb alc883_clevo_m540r_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Int speaker */ + /*{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},*/ + + /* 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_clevo_m720_verbs[] = { /* HP */ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, @@ -8591,7 +8346,7 @@ static struct hda_verb alc883_vaiott_verbs[] = { { } /* end */ }; -static void alc888_3st_hp_init_hook(struct hda_codec *codec) +static void alc888_3st_hp_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -8599,7 +8354,6 @@ static void alc888_3st_hp_init_hook(struct hda_codec *codec) spec->autocfg.speaker_pins[0] = 0x14; spec->autocfg.speaker_pins[1] = 0x16; spec->autocfg.speaker_pins[2] = 0x18; - alc_automute_amp(codec); } static struct hda_verb alc888_3st_hp_verbs[] = { @@ -8696,13 +8450,12 @@ static struct hda_verb alc883_medion_md2_verbs[] = { }; /* toggle speaker-output according to the hp-jack state */ -static void alc883_medion_md2_init_hook(struct hda_codec *codec) +static void alc883_medion_md2_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x14; spec->autocfg.speaker_pins[0] = 0x15; - alc_automute_amp(codec); } /* toggle speaker-output according to the hp-jack state */ @@ -8719,12 +8472,16 @@ static void alc883_clevo_m720_mic_automute(struct hda_codec *codec) HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } -static void alc883_clevo_m720_init_hook(struct hda_codec *codec) +static void alc883_clevo_m720_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; +} + +static void alc883_clevo_m720_init_hook(struct hda_codec *codec) +{ alc_automute_amp(codec); alc883_clevo_m720_mic_automute(codec); } @@ -8743,22 +8500,20 @@ static void alc883_clevo_m720_unsol_event(struct hda_codec *codec, } /* toggle speaker-output according to the hp-jack state */ -static void alc883_2ch_fujitsu_pi2515_init_hook(struct hda_codec *codec) +static void alc883_2ch_fujitsu_pi2515_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x14; spec->autocfg.speaker_pins[0] = 0x15; - alc_automute_amp(codec); } -static void alc883_haier_w66_init_hook(struct hda_codec *codec) +static void alc883_haier_w66_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x1b; spec->autocfg.speaker_pins[0] = 0x14; - alc_automute_amp(codec); } static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec) @@ -8797,14 +8552,13 @@ static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec, } /* toggle speaker-output according to the hp-jack state */ -static void alc883_acer_aspire_init_hook(struct hda_codec *codec) +static void alc883_acer_aspire_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x14; spec->autocfg.speaker_pins[0] = 0x15; spec->autocfg.speaker_pins[1] = 0x16; - alc_automute_amp(codec); } static struct hda_verb alc883_acer_eapd_verbs[] = { @@ -8825,7 +8579,14 @@ static struct hda_verb alc883_acer_eapd_verbs[] = { { } }; -static void alc888_6st_dell_init_hook(struct hda_codec *codec) +static struct hda_verb alc888_acer_aspire_7730G_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x02}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +static void alc888_6st_dell_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -8834,10 +8595,9 @@ static void alc888_6st_dell_init_hook(struct hda_codec *codec) spec->autocfg.speaker_pins[1] = 0x15; spec->autocfg.speaker_pins[2] = 0x16; spec->autocfg.speaker_pins[3] = 0x17; - alc_automute_amp(codec); } -static void alc888_lenovo_sky_init_hook(struct hda_codec *codec) +static void alc888_lenovo_sky_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -8847,82 +8607,17 @@ static void alc888_lenovo_sky_init_hook(struct hda_codec *codec) spec->autocfg.speaker_pins[2] = 0x16; spec->autocfg.speaker_pins[3] = 0x17; spec->autocfg.speaker_pins[4] = 0x1a; - alc_automute_amp(codec); } -static void alc883_vaiott_init_hook(struct hda_codec *codec) +static void alc883_vaiott_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; spec->autocfg.speaker_pins[1] = 0x17; - alc_automute_amp(codec); } -/* - * generic initialization of ADC, input mixers and output mixers - */ -static struct hda_verb alc883_auto_init_verbs[] = { - /* - * Unmute ADC0-2 and set the default input to mic-in - */ - {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Mute input amps (CD, 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 - 0x0f) - */ - /* set vol=0 to output mixers */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0f, 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)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ - /* Input mixer1 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - /* {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - /* Input mixer2 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {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)}, */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - - { } -}; - static struct hda_verb alc888_asus_m90v_verbs[] = { {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, @@ -8933,19 +8628,7 @@ static struct hda_verb alc888_asus_m90v_verbs[] = { { } /* end */ }; -static void alc883_nb_mic_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, - 0x7000 | (0x00 << 8) | (present ? 0 : 0x80)); - snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, - 0x7000 | (0x01 << 8) | (present ? 0x80 : 0)); -} - -static void alc883_M90V_init_hook(struct hda_codec *codec) +static void alc883_mode2_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -8953,26 +8636,11 @@ static void alc883_M90V_init_hook(struct hda_codec *codec) spec->autocfg.speaker_pins[0] = 0x14; spec->autocfg.speaker_pins[1] = 0x15; spec->autocfg.speaker_pins[2] = 0x16; - alc_automute_pin(codec); -} - -static void alc883_mode2_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case ALC880_MIC_EVENT: - alc883_nb_mic_automute(codec); - break; - default: - alc_sku_unsol_event(codec, res); - break; - } -} - -static void alc883_mode2_inithook(struct hda_codec *codec) -{ - alc883_M90V_init_hook(codec); - alc883_nb_mic_automute(codec); + spec->ext_mic.pin = 0x18; + spec->int_mic.pin = 0x19; + spec->ext_mic.mux_idx = 0; + spec->int_mic.mux_idx = 1; + spec->auto_mic = 1; } static struct hda_verb alc888_asus_eee1601_verbs[] = { @@ -9033,25 +8701,44 @@ static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res) alc889A_mb31_automute(codec); } + #ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc883_loopbacks alc880_loopbacks +#define alc882_loopbacks alc880_loopbacks #endif /* pcm configuration: identical 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 +#define alc882_pcm_analog_playback alc880_pcm_analog_playback +#define alc882_pcm_analog_capture alc880_pcm_analog_capture +#define alc882_pcm_digital_playback alc880_pcm_digital_playback +#define alc882_pcm_digital_capture alc880_pcm_digital_capture + +static hda_nid_t alc883_slave_dig_outs[] = { + ALC1200_DIGOUT_NID, 0, +}; + +static hda_nid_t alc1200_slave_dig_outs[] = { + ALC883_DIGOUT_NID, 0, +}; /* * configuration and preset */ -static const char *alc883_models[ALC883_MODEL_LAST] = { - [ALC883_3ST_2ch_DIG] = "3stack-dig", +static const char *alc882_models[ALC882_MODEL_LAST] = { + [ALC882_3ST_DIG] = "3stack-dig", + [ALC882_6ST_DIG] = "6stack-dig", + [ALC882_ARIMA] = "arima", + [ALC882_W2JC] = "w2jc", + [ALC882_TARGA] = "targa", + [ALC882_ASUS_A7J] = "asus-a7j", + [ALC882_ASUS_A7M] = "asus-a7m", + [ALC885_MACPRO] = "macpro", + [ALC885_MB5] = "mb5", + [ALC885_MBP3] = "mbp3", + [ALC885_IMAC24] = "imac24", + [ALC883_3ST_2ch_DIG] = "3stack-2ch-dig", [ALC883_3ST_6ch_DIG] = "3stack-6ch-dig", [ALC883_3ST_6ch] = "3stack-6ch", - [ALC883_6ST_DIG] = "6stack-dig", + [ALC883_6ST_DIG] = "alc883-6stack-dig", [ALC883_TARGA_DIG] = "targa-dig", [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig", [ALC883_TARGA_8ch_DIG] = "targa-8ch-dig", @@ -9060,6 +8747,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { [ALC888_ACER_ASPIRE_4930G] = "acer-aspire-4930g", [ALC888_ACER_ASPIRE_6530G] = "acer-aspire-6530g", [ALC888_ACER_ASPIRE_8930G] = "acer-aspire-8930g", + [ALC888_ACER_ASPIRE_7730G] = "acer-aspire-7730g", [ALC883_MEDION] = "medion", [ALC883_MEDION_MD2] = "medion-md2", [ALC883_LAPTOP_EAPD] = "laptop-eapd", @@ -9071,18 +8759,22 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { [ALC888_3ST_HP] = "3stack-hp", [ALC888_6ST_DELL] = "6stack-dell", [ALC883_MITAC] = "mitac", + [ALC883_CLEVO_M540R] = "clevo-m540r", [ALC883_CLEVO_M720] = "clevo-m720", [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515", [ALC888_FUJITSU_XA3530] = "fujitsu-xa3530", [ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel", + [ALC889A_INTEL] = "intel-alc889a", + [ALC889_INTEL] = "intel-x58", [ALC1200_ASUS_P5Q] = "asus-p5q", [ALC889A_MB31] = "mb31", [ALC883_SONY_VAIO_TT] = "sony-vaio-tt", - [ALC883_AUTO] = "auto", + [ALC882_AUTO] = "auto", }; -static struct snd_pci_quirk alc883_cfg_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG), +static struct snd_pci_quirk alc882_cfg_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE), SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_ACER_ASPIRE), SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_ACER_ASPIRE), @@ -9097,40 +8789,56 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { ALC888_ACER_ASPIRE_8930G), SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", ALC888_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC883_AUTO), - SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC883_AUTO), + SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC882_AUTO), + SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC882_AUTO), SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G", ALC888_ACER_ASPIRE_6530G), SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G", ALC888_ACER_ASPIRE_6530G), + SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G", + ALC888_ACER_ASPIRE_7730G), /* default Acer -- disabled as it causes more problems. * model=auto should work fine now */ /* SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_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(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG), SND_PCI_QUIRK(0x103c, 0x2a66, "HP Acacia", ALC888_3ST_HP), SND_PCI_QUIRK(0x103c, 0x2a72, "HP Educ.ar", ALC888_3ST_HP), + + 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, 0x1873, "Asus M90V", ALC888_ASUS_M90V), + 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, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), SND_PCI_QUIRK(0x1043, 0x8284, "Asus Z37E", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q), SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601), + + SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT), SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG), SND_PCI_QUIRK(0x1071, 0x8227, "Mitac 82801H", ALC883_MITAC), SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC), SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL), SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), - SND_PCI_QUIRK(0x1458, 0xa002, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_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, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */ SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG), @@ -9139,6 +8847,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { 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, 0x42cd, "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), @@ -9152,11 +8861,15 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7350, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0xaa08, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720), SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720), + SND_PCI_QUIRK(0x1558, 0x5409, "Clevo laptop M540R", ALC883_CLEVO_M540R), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC883_LAPTOP_EAPD), SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch), + /* SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), */ SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1100, "FSC AMILO Xi/Pi25xx", ALC883_FUJITSU_PI2515), @@ -9171,24 +8884,186 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x17c0, 0x4085, "MEDION MD96630", ALC888_LENOVO_MS7195_DIG), SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66), + SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL), SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL), SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC), - SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC883_3ST_6ch_INTEL), + SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_INTEL), + SND_PCI_QUIRK(0x8086, 0x0021, "Intel IbexPeak", ALC889A_INTEL), + SND_PCI_QUIRK(0x8086, 0x3b56, "Intel IbexPeak", ALC889A_INTEL), SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch), - SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT), - {} -}; -static hda_nid_t alc883_slave_dig_outs[] = { - ALC1200_DIGOUT_NID, 0, + {} }; -static hda_nid_t alc1200_slave_dig_outs[] = { - ALC883_DIGOUT_NID, 0, +/* codec SSID table for Intel Mac */ +static struct snd_pci_quirk alc882_ssid_cfg_tbl[] = { + SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC885_MBP3), + SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC885_MBP3), + SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC885_MBP3), + SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC885_MACPRO), + SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_IMAC24), + SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_IMAC24), + SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC885_MBP3), + SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889A_MB31), + SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC885_MBP3), + SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24), + SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC885_MB5), + /* FIXME: HP jack sense seems not working for MBP 5,1, so apparently + * no perfect solution yet + */ + SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC885_MB5), + {} /* terminator */ }; -static struct alc_config_preset alc883_presets[] = { +static struct alc_config_preset alc882_presets[] = { + [ALC882_3ST_DIG] = { + .mixers = { alc882_base_mixer }, + .init_verbs = { alc882_base_init_verbs, + alc882_adc1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), + .channel_mode = alc882_ch_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + }, + [ALC882_6ST_DIG] = { + .mixers = { alc882_base_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, + alc882_adc1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), + .channel_mode = alc882_sixstack_modes, + .input_mux = &alc882_capture_source, + }, + [ALC882_ARIMA] = { + .mixers = { alc882_base_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc882_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), + .channel_mode = alc882_sixstack_modes, + .input_mux = &alc882_capture_source, + }, + [ALC882_W2JC] = { + .mixers = { alc882_w2jc_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc882_eapd_verbs, alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), + .channel_mode = alc880_threestack_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + .dig_out_nid = ALC882_DIGOUT_NID, + }, + [ALC885_MBP3] = { + .mixers = { alc885_mbp3_mixer, alc882_chmode_mixer }, + .init_verbs = { alc885_mbp3_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = 2, + .dac_nids = alc882_dac_nids, + .hp_nid = 0x04, + .channel_mode = alc885_mbp_4ch_modes, + .num_channel_mode = ARRAY_SIZE(alc885_mbp_4ch_modes), + .input_mux = &alc882_capture_source, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .unsol_event = alc_automute_amp_unsol_event, + .setup = alc885_mbp3_setup, + .init_hook = alc_automute_amp, + }, + [ALC885_MB5] = { + .mixers = { alc885_mb5_mixer, alc882_chmode_mixer }, + .init_verbs = { alc885_mb5_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .channel_mode = alc885_mb5_6ch_modes, + .num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes), + .input_mux = &mb5_capture_source, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + }, + [ALC885_MACPRO] = { + .mixers = { alc882_macpro_mixer }, + .init_verbs = { alc882_macpro_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), + .channel_mode = alc882_ch_modes, + .input_mux = &alc882_capture_source, + .init_hook = alc885_macpro_init_hook, + }, + [ALC885_IMAC24] = { + .mixers = { alc885_imac24_mixer }, + .init_verbs = { alc885_imac24_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), + .channel_mode = alc882_ch_modes, + .input_mux = &alc882_capture_source, + .unsol_event = alc_automute_amp_unsol_event, + .setup = alc885_imac24_setup, + .init_hook = alc885_imac24_init_hook, + }, + [ALC882_TARGA] = { + .mixers = { alc882_targa_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc880_gpio3_init_verbs, alc882_targa_verbs}, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), + .adc_nids = alc882_adc_nids, + .capsrc_nids = alc882_capsrc_nids, + .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), + .channel_mode = alc882_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + .unsol_event = alc882_targa_unsol_event, + .setup = alc882_targa_setup, + .init_hook = alc882_targa_automute, + }, + [ALC882_ASUS_A7J] = { + .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc882_asus_a7j_verbs}, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), + .adc_nids = alc882_adc_nids, + .capsrc_nids = alc882_capsrc_nids, + .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), + .channel_mode = alc882_3ST_6ch_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + }, + [ALC882_ASUS_A7M] = { + .mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, + alc882_eapd_verbs, alc880_gpio1_init_verbs, + alc882_asus_a7m_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes), + .channel_mode = alc880_threestack_modes, + .need_dac_fix = 1, + .input_mux = &alc882_capture_source, + }, [ALC883_3ST_2ch_DIG] = { .mixers = { alc883_3ST_2ch_mixer }, .init_verbs = { alc883_init_verbs }, @@ -9235,6 +9110,46 @@ static struct alc_config_preset alc883_presets[] = { .need_dac_fix = 1, .input_mux = &alc883_3stack_6ch_intel, }, + [ALC889A_INTEL] = { + .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer }, + .init_verbs = { alc885_init_verbs, alc885_init_input_verbs, + alc_hp15_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), + .adc_nids = alc889_adc_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .slave_dig_outs = alc883_slave_dig_outs, + .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes), + .channel_mode = alc889_8ch_intel_modes, + .capsrc_nids = alc889_capsrc_nids, + .input_mux = &alc889_capture_source, + .setup = alc889_automute_setup, + .init_hook = alc_automute_amp, + .unsol_event = alc_automute_amp_unsol_event, + .need_dac_fix = 1, + }, + [ALC889_INTEL] = { + .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer }, + .init_verbs = { alc885_init_verbs, alc889_init_input_verbs, + alc889_eapd_verbs, alc_hp15_unsol_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), + .adc_nids = alc889_adc_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .slave_dig_outs = alc883_slave_dig_outs, + .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes), + .channel_mode = alc889_8ch_intel_modes, + .capsrc_nids = alc889_capsrc_nids, + .input_mux = &alc889_capture_source, + .setup = alc889_automute_setup, + .init_hook = alc889_intel_init_hook, + .unsol_event = alc_automute_amp_unsol_event, + .need_dac_fix = 1, + }, [ALC883_6ST_DIG] = { .mixers = { alc883_base_mixer, alc883_chmode_mixer }, .init_verbs = { alc883_init_verbs }, @@ -9258,7 +9173,8 @@ static struct alc_config_preset alc883_presets[] = { .need_dac_fix = 1, .input_mux = &alc883_capture_source, .unsol_event = alc883_targa_unsol_event, - .init_hook = alc883_targa_init_hook, + .setup = alc882_targa_setup, + .init_hook = alc882_targa_automute, }, [ALC883_TARGA_2ch_DIG] = { .mixers = { alc883_targa_2ch_mixer}, @@ -9273,10 +9189,12 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, .unsol_event = alc883_targa_unsol_event, - .init_hook = alc883_targa_init_hook, + .setup = alc882_targa_setup, + .init_hook = alc882_targa_automute, }, [ALC883_TARGA_8ch_DIG] = { - .mixers = { alc883_base_mixer, alc883_chmode_mixer }, + .mixers = { alc883_targa_mixer, alc883_targa_8ch_mixer, + alc883_chmode_mixer }, .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs, alc883_targa_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), @@ -9291,7 +9209,8 @@ static struct alc_config_preset alc883_presets[] = { .need_dac_fix = 1, .input_mux = &alc883_capture_source, .unsol_event = alc883_targa_unsol_event, - .init_hook = alc883_targa_init_hook, + .setup = alc882_targa_setup, + .init_hook = alc882_targa_automute, }, [ALC883_ACER] = { .mixers = { alc883_base_mixer }, @@ -9317,7 +9236,8 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc883_acer_aspire_init_hook, + .setup = alc883_acer_aspire_setup, + .init_hook = alc_automute_amp, }, [ALC888_ACER_ASPIRE_4930G] = { .mixers = { alc888_base_mixer, @@ -9337,7 +9257,8 @@ static struct alc_config_preset alc883_presets[] = { ARRAY_SIZE(alc888_2_capture_sources), .input_mux = alc888_2_capture_sources, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc888_acer_aspire_4930g_init_hook, + .setup = alc888_acer_aspire_4930g_setup, + .init_hook = alc_automute_amp, }, [ALC888_ACER_ASPIRE_6530G] = { .mixers = { alc888_acer_aspire_6530_mixer }, @@ -9355,7 +9276,8 @@ static struct alc_config_preset alc883_presets[] = { ARRAY_SIZE(alc888_2_capture_sources), .input_mux = alc888_acer_aspire_6530_sources, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc888_acer_aspire_6530g_init_hook, + .setup = alc888_acer_aspire_6530g_setup, + .init_hook = alc_automute_amp, }, [ALC888_ACER_ASPIRE_8930G] = { .mixers = { alc888_base_mixer, @@ -9376,7 +9298,28 @@ static struct alc_config_preset alc883_presets[] = { ARRAY_SIZE(alc889_capture_sources), .input_mux = alc889_capture_sources, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc889_acer_aspire_8930g_init_hook, + .setup = alc889_acer_aspire_8930g_setup, + .init_hook = alc_automute_amp, + }, + [ALC888_ACER_ASPIRE_7730G] = { + .mixers = { alc883_3ST_6ch_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, + alc888_acer_aspire_7730G_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .const_channel_count = 6, + .input_mux = &alc883_capture_source, + .unsol_event = alc_automute_amp_unsol_event, + .setup = alc888_acer_aspire_6530g_setup, + .init_hook = alc_automute_amp, }, [ALC883_MEDION] = { .mixers = { alc883_fivestack_mixer, @@ -9401,7 +9344,8 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc883_medion_md2_init_hook, + .setup = alc883_medion_md2_setup, + .init_hook = alc_automute_amp, }, [ALC883_LAPTOP_EAPD] = { .mixers = { alc883_base_mixer }, @@ -9412,6 +9356,21 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, }, + [ALC883_CLEVO_M540R] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc883_clevo_m540r_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_clevo_modes), + .channel_mode = alc883_3ST_6ch_clevo_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + /* This machine has the hardware HP auto-muting, thus + * we need no software mute via unsol event + */ + }, [ALC883_CLEVO_M720] = { .mixers = { alc883_clevo_m720_mixer }, .init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs }, @@ -9422,6 +9381,7 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, .unsol_event = alc883_clevo_m720_unsol_event, + .setup = alc883_clevo_m720_setup, .init_hook = alc883_clevo_m720_init_hook, }, [ALC883_LENOVO_101E_2ch] = { @@ -9447,7 +9407,8 @@ static struct alc_config_preset alc883_presets[] = { .need_dac_fix = 1, .input_mux = &alc883_lenovo_nb0763_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc883_medion_md2_init_hook, + .setup = alc883_medion_md2_setup, + .init_hook = alc_automute_amp, }, [ALC888_LENOVO_MS7195_DIG] = { .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, @@ -9472,7 +9433,8 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc883_haier_w66_init_hook, + .setup = alc883_haier_w66_setup, + .init_hook = alc_automute_amp, }, [ALC888_3ST_HP] = { .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, @@ -9484,7 +9446,8 @@ static struct alc_config_preset alc883_presets[] = { .need_dac_fix = 1, .input_mux = &alc883_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc888_3st_hp_init_hook, + .setup = alc888_3st_hp_setup, + .init_hook = alc_automute_amp, }, [ALC888_6ST_DELL] = { .mixers = { alc883_base_mixer, alc883_chmode_mixer }, @@ -9497,7 +9460,8 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_sixstack_modes, .input_mux = &alc883_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc888_6st_dell_init_hook, + .setup = alc888_6st_dell_setup, + .init_hook = alc_automute_amp, }, [ALC883_MITAC] = { .mixers = { alc883_mitac_mixer }, @@ -9508,7 +9472,8 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc883_mitac_init_hook, + .setup = alc883_mitac_setup, + .init_hook = alc_automute_amp, }, [ALC883_FUJITSU_PI2515] = { .mixers = { alc883_2ch_fujitsu_pi2515_mixer }, @@ -9521,7 +9486,8 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_fujitsu_pi2515_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc883_2ch_fujitsu_pi2515_init_hook, + .setup = alc883_2ch_fujitsu_pi2515_setup, + .init_hook = alc_automute_amp, }, [ALC888_FUJITSU_XA3530] = { .mixers = { alc888_base_mixer, alc883_chmode_mixer }, @@ -9539,7 +9505,8 @@ static struct alc_config_preset alc883_presets[] = { ARRAY_SIZE(alc888_2_capture_sources), .input_mux = alc888_2_capture_sources, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc888_fujitsu_xa3530_init_hook, + .setup = alc888_fujitsu_xa3530_setup, + .init_hook = alc_automute_amp, }, [ALC888_LENOVO_SKY] = { .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer }, @@ -9552,7 +9519,8 @@ static struct alc_config_preset alc883_presets[] = { .need_dac_fix = 1, .input_mux = &alc883_lenovo_sky_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc888_lenovo_sky_init_hook, + .setup = alc888_lenovo_sky_setup, + .init_hook = alc_automute_amp, }, [ALC888_ASUS_M90V] = { .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, @@ -9565,8 +9533,9 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_6ch_modes, .need_dac_fix = 1, .input_mux = &alc883_fujitsu_pi2515_capture_source, - .unsol_event = alc883_mode2_unsol_event, - .init_hook = alc883_mode2_inithook, + .unsol_event = alc_sku_unsol_event, + .setup = alc883_mode2_setup, + .init_hook = alc_inithook, }, [ALC888_ASUS_EEE1601] = { .mixers = { alc883_asus_eee1601_mixer }, @@ -9619,15 +9588,47 @@ static struct alc_config_preset alc883_presets[] = { .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc883_vaiott_init_hook, + .setup = alc883_vaiott_setup, + .init_hook = alc_automute_amp, + }, +}; + + +/* + * Pin config fixes + */ +enum { + PINFIX_ABIT_AW9D_MAX +}; + +static struct alc_pincfg alc882_abit_aw9d_pinfix[] = { + { 0x15, 0x01080104 }, /* side */ + { 0x16, 0x01011012 }, /* rear */ + { 0x17, 0x01016011 }, /* clfe */ + { } +}; + +static const struct alc_fixup alc882_fixups[] = { + [PINFIX_ABIT_AW9D_MAX] = { + .pins = alc882_abit_aw9d_pinfix }, }; +static struct snd_pci_quirk alc882_fixup_tbl[] = { + SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), + {} +}; /* * BIOS auto configuration */ -static void alc883_auto_set_output_and_unmute(struct hda_codec *codec, +static int alc882_auto_create_input_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x23, 0x22); +} + +static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int dac_idx) { @@ -9644,7 +9645,7 @@ static void alc883_auto_set_output_and_unmute(struct hda_codec *codec, } -static void alc883_auto_init_multi_out(struct hda_codec *codec) +static void alc882_auto_init_multi_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int i; @@ -9653,12 +9654,12 @@ static void alc883_auto_init_multi_out(struct hda_codec *codec) hda_nid_t nid = spec->autocfg.line_out_pins[i]; int pin_type = get_pin_type(spec->autocfg.line_out_type); if (nid) - alc883_auto_set_output_and_unmute(codec, nid, pin_type, + alc882_auto_set_output_and_unmute(codec, nid, pin_type, i); } } -static void alc883_auto_init_hp_out(struct hda_codec *codec) +static void alc882_auto_init_hp_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; hda_nid_t pin; @@ -9666,91 +9667,191 @@ static void alc883_auto_init_hp_out(struct hda_codec *codec) pin = spec->autocfg.hp_pins[0]; if (pin) /* connect to front */ /* use dac 0 */ - alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); pin = spec->autocfg.speaker_pins[0]; if (pin) - alc883_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); + alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } -#define alc883_is_input_pin(nid) alc880_is_input_pin(nid) -#define ALC883_PIN_CD_NID ALC880_PIN_CD_NID - -static void alc883_auto_init_analog_input(struct hda_codec *codec) +static void alc882_auto_init_analog_input(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int i; for (i = 0; i < AUTO_PIN_LAST; i++) { hda_nid_t nid = spec->autocfg.input_pins[i]; - if (alc883_is_input_pin(nid)) { - alc_set_input_pin(codec, nid, i); - if (nid != ALC883_PIN_CD_NID && - (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) + if (!nid) + continue; + alc_set_input_pin(codec, nid, i); + if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); + } +} + +static void alc882_auto_init_input_src(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int c; + + for (c = 0; c < spec->num_adc_nids; c++) { + hda_nid_t conn_list[HDA_MAX_NUM_INPUTS]; + hda_nid_t nid = spec->capsrc_nids[c]; + unsigned int mux_idx; + const struct hda_input_mux *imux; + int conns, mute, idx, item; + + conns = snd_hda_get_connections(codec, nid, conn_list, + ARRAY_SIZE(conn_list)); + if (conns < 0) + continue; + mux_idx = c >= spec->num_mux_defs ? 0 : c; + imux = &spec->input_mux[mux_idx]; + for (idx = 0; idx < conns; idx++) { + /* if the current connection is the selected one, + * unmute it as default - otherwise mute it + */ + mute = AMP_IN_MUTE(idx); + for (item = 0; item < imux->num_items; item++) { + if (imux->items[item].index == idx) { + if (spec->cur_mux[c] == item) + mute = AMP_IN_UNMUTE(idx); + break; + } + } + /* check if we have a selector or mixer + * we could check for the widget type instead, but + * just check for Amp-In presence (in case of mixer + * without amp-in there is something wrong, this + * function shouldn't be used or capsrc nid is wrong) + */ + if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); + mute); + else if (mute != AMP_IN_MUTE(idx)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, + idx); } } } -#define alc883_auto_init_input_src alc882_auto_init_input_src +/* add mic boosts if needed */ +static int alc_auto_add_mic_boost(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int err; + hda_nid_t nid; + + nid = spec->autocfg.input_pins[AUTO_PIN_MIC]; + 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)); + if (err < 0) + return err; + } + nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC]; + 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)); + if (err < 0) + return err; + } + return 0; +} /* almost identical with ALC880 parser... */ -static int alc883_parse_auto_config(struct hda_codec *codec) +static int alc882_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - int err = alc880_parse_auto_config(codec); - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; + static hda_nid_t alc882_ignore[] = { 0x1d, 0 }; + int i, err; + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, + alc882_ignore); if (err < 0) return err; - else if (!err) - return 0; /* no config found */ + if (!spec->autocfg.line_outs) + return 0; /* can't find valid BIOS pin config */ - err = alc_auto_add_mic_boost(codec); + err = alc880_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = alc880_auto_create_extra_out(spec, + spec->autocfg.speaker_pins[0], + "Speaker"); + if (err < 0) + return err; + err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0], + "Headphone"); + if (err < 0) + return err; + err = alc882_auto_create_input_ctls(codec, &spec->autocfg); if (err < 0) return err; - /* hack - override the init verbs */ - spec->init_verbs[0] = alc883_auto_init_verbs; + spec->multiout.max_channels = spec->multiout.num_dacs * 2; - /* setup input_mux for ALC889 */ - if (codec->vendor_id == 0x10ec0889) { - /* digital-mic input pin is excluded in alc880_auto_create..() - * because it's under 0x18 - */ - if (cfg->input_pins[AUTO_PIN_MIC] == 0x12 || - cfg->input_pins[AUTO_PIN_FRONT_MIC] == 0x12) { - struct hda_input_mux *imux = &spec->private_imux[0]; - for (i = 1; i < 3; i++) - memcpy(&spec->private_imux[i], - &spec->private_imux[0], - sizeof(spec->private_imux[0])); - imux->items[imux->num_items].label = "Int DMic"; - imux->items[imux->num_items].index = 0x0b; - imux->num_items++; - spec->num_mux_defs = 3; - spec->input_mux = spec->private_imux; + /* check multiple SPDIF-out (for recent codecs) */ + for (i = 0; i < spec->autocfg.dig_outs; i++) { + hda_nid_t dig_nid; + err = snd_hda_get_connections(codec, + spec->autocfg.dig_out_pins[i], + &dig_nid, 1); + if (err < 0) + continue; + if (!i) + spec->multiout.dig_out_nid = dig_nid; + else { + spec->multiout.slave_dig_outs = spec->slave_dig_outs; + spec->slave_dig_outs[i - 1] = dig_nid; + if (i == ARRAY_SIZE(spec->slave_dig_outs) - 1) + break; } } + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = ALC880_DIGIN_NID; + + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); + + add_verb(spec, alc883_auto_init_verbs); + /* if ADC 0x07 is available, initialize it, too */ + if (get_wcaps_type(get_wcaps(codec, 0x07)) == AC_WID_AUD_IN) + add_verb(spec, alc882_adc1_init_verbs); + + spec->num_mux_defs = 1; + spec->input_mux = &spec->private_imux[0]; + + alc_ssid_check(codec, 0x15, 0x1b, 0x14); + + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; return 1; /* config found */ } /* additional initialization for auto-configuration model */ -static void alc883_auto_init(struct hda_codec *codec) +static void alc882_auto_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - alc883_auto_init_multi_out(codec); - alc883_auto_init_hp_out(codec); - alc883_auto_init_analog_input(codec); - alc883_auto_init_input_src(codec); + alc882_auto_init_multi_out(codec); + alc882_auto_init_hp_out(codec); + alc882_auto_init_analog_input(codec); + alc882_auto_init_input_src(codec); if (spec->unsol_event) alc_inithook(codec); } -static int patch_alc883(struct hda_codec *codec) +static int patch_alc882(struct hda_codec *codec) { struct alc_spec *spec; int err, board_config; @@ -9761,28 +9862,35 @@ static int patch_alc883(struct hda_codec *codec) codec->spec = spec; - alc_fix_pll_init(codec, 0x20, 0x0a, 10); + switch (codec->vendor_id) { + case 0x10ec0882: + case 0x10ec0885: + break; + default: + /* ALC883 and variants */ + alc_fix_pll_init(codec, 0x20, 0x0a, 10); + break; + } - board_config = snd_hda_check_board_config(codec, ALC883_MODEL_LAST, - alc883_models, - alc883_cfg_tbl); - if (board_config < 0 || board_config >= ALC883_MODEL_LAST) { - /* Pick up systems that don't supply PCI SSID */ - switch (codec->subsystem_id) { - case 0x106b3600: /* Macbook 3.1 */ - board_config = ALC889A_MB31; - break; - default: - printk(KERN_INFO - "hda_codec: Unknown model for %s, trying " - "auto-probe from BIOS...\n", codec->chip_name); - board_config = ALC883_AUTO; - } + board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST, + alc882_models, + alc882_cfg_tbl); + + if (board_config < 0 || board_config >= ALC882_MODEL_LAST) + board_config = snd_hda_check_board_codec_sid_config(codec, + ALC882_MODEL_LAST, alc882_models, alc882_ssid_cfg_tbl); + + if (board_config < 0 || board_config >= ALC882_MODEL_LAST) { + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + board_config = ALC882_AUTO; } - if (board_config == ALC883_AUTO) { + alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups); + + if (board_config == ALC882_AUTO) { /* automatic parse from the BIOS config */ - err = alc883_parse_auto_config(codec); + err = alc882_parse_auto_config(codec); if (err < 0) { alc_free(codec); return err; @@ -9790,7 +9898,7 @@ static int patch_alc883(struct hda_codec *codec) printk(KERN_INFO "hda_codec: Cannot set up configuration " "from BIOS. Using base mode...\n"); - board_config = ALC883_3ST_2ch_DIG; + board_config = ALC882_3ST_DIG; } } @@ -9800,63 +9908,61 @@ static int patch_alc883(struct hda_codec *codec) return err; } - if (board_config != ALC883_AUTO) - setup_preset(spec, &alc883_presets[board_config]); + if (board_config != ALC882_AUTO) + setup_preset(codec, &alc882_presets[board_config]); - switch (codec->vendor_id) { - case 0x10ec0888: - if (!spec->num_adc_nids) { - spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); - spec->adc_nids = alc883_adc_nids; - } - if (!spec->capsrc_nids) - spec->capsrc_nids = alc883_capsrc_nids; + 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_digital_playback = &alc882_pcm_digital_playback; + spec->stream_digital_capture = &alc882_pcm_digital_capture; + + if (codec->vendor_id == 0x10ec0888) spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */ - break; - case 0x10ec0889: - if (!spec->num_adc_nids) { - spec->num_adc_nids = ARRAY_SIZE(alc889_adc_nids); - spec->adc_nids = alc889_adc_nids; - } - if (!spec->capsrc_nids) - spec->capsrc_nids = alc889_capsrc_nids; - break; - default: - if (!spec->num_adc_nids) { - spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); - spec->adc_nids = alc883_adc_nids; + + if (!spec->adc_nids && spec->input_mux) { + int i; + spec->num_adc_nids = 0; + for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) { + hda_nid_t cap; + hda_nid_t nid = alc882_adc_nids[i]; + unsigned int wcap = get_wcaps(codec, nid); + /* get type */ + wcap = get_wcaps_type(wcap); + if (wcap != AC_WID_AUD_IN) + continue; + spec->private_adc_nids[spec->num_adc_nids] = nid; + err = snd_hda_get_connections(codec, nid, &cap, 1); + if (err < 0) + continue; + spec->private_capsrc_nids[spec->num_adc_nids] = cap; + spec->num_adc_nids++; } - if (!spec->capsrc_nids) - spec->capsrc_nids = alc883_capsrc_nids; - break; + spec->adc_nids = spec->private_adc_nids; + spec->capsrc_nids = spec->private_capsrc_nids; } - 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_digital_playback = &alc883_pcm_digital_playback; - spec->stream_digital_capture = &alc883_pcm_digital_capture; - - if (!spec->cap_mixer) - set_capture_mixer(spec); + set_capture_mixer(codec); set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); spec->vmaster_nid = 0x0c; codec->patch_ops = alc_patch_ops; - if (board_config == ALC883_AUTO) - spec->init_hook = alc883_auto_init; - + if (board_config == ALC882_AUTO) + spec->init_hook = alc882_auto_init; #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) - spec->loopback.amplist = alc883_loopbacks; + spec->loopback.amplist = alc882_loopbacks; #endif codec->proc_widget_hook = print_realtek_coef; return 0; } + /* * ALC262 support */ @@ -10032,13 +10138,12 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = { }; /* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc262_hp_t5735_init_hook(struct hda_codec *codec) +static void alc262_hp_t5735_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x0c; /* HACK: not actually a pin */ - alc_automute_amp(codec); } static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = { @@ -10195,22 +10300,20 @@ static void alc262_hippo_unsol_event(struct hda_codec *codec, unsigned int res) alc262_hippo_automute(codec); } -static void alc262_hippo_init_hook(struct hda_codec *codec) +static void alc262_hippo_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; - alc262_hippo_automute(codec); } -static void alc262_hippo1_init_hook(struct hda_codec *codec) +static void alc262_hippo1_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x1b; spec->autocfg.speaker_pins[0] = 0x14; - alc262_hippo_automute(codec); } @@ -10267,13 +10370,12 @@ static struct hda_verb alc262_tyan_verbs[] = { }; /* unsolicited event for HP jack sensing */ -static void alc262_tyan_init_hook(struct hda_codec *codec) +static void alc262_tyan_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x1b; spec->autocfg.speaker_pins[0] = 0x15; - alc_automute_amp(codec); } @@ -10365,12 +10467,6 @@ static struct hda_verb alc262_eapd_verbs[] = { { } }; -static struct hda_verb alc262_hippo_unsol_verbs[] = { - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {} -}; - static struct hda_verb alc262_hippo1_unsol_verbs[] = { {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, @@ -10391,14 +10487,6 @@ static struct hda_verb alc262_sony_unsol_verbs[] = { {} }; -static struct hda_input_mux alc262_dmic_capture_source = { - .num_items = 2, - .items = { - { "Int DMic", 0x9 }, - { "Mic", 0x0 }, - }, -}; - static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = { HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), @@ -10420,35 +10508,17 @@ static struct hda_verb alc262_toshiba_s06_verbs[] = { {} }; -static void alc262_dmic_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_write(codec, 0x22, 0, - AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x09); -} - - -/* unsolicited event for HP jack sensing */ -static void alc262_toshiba_s06_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) == ALC880_MIC_EVENT) - alc262_dmic_automute(codec); - else - alc_sku_unsol_event(codec, res); -} - -static void alc262_toshiba_s06_init_hook(struct hda_codec *codec) +static void alc262_toshiba_s06_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; - alc_automute_pin(codec); - alc262_dmic_automute(codec); + spec->ext_mic.pin = 0x18; + spec->ext_mic.mux_idx = 0; + spec->int_mic.pin = 0x12; + spec->int_mic.mux_idx = 9; + spec->auto_mic = 1; } /* @@ -10866,104 +10936,111 @@ static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = { { } /* end */ }; +/* We use two mixers depending on the output pin; 0x16 is a mono output + * and thus it's bound with a different mixer. + * This function returns which mixer amp should be used. + */ +static int alc262_check_volbit(hda_nid_t nid) +{ + if (!nid) + return 0; + else if (nid == 0x16) + return 2; + else + return 1; +} + +static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid, + const char *pfx, int *vbits) +{ + char name[32]; + unsigned long val; + int vbit; + + vbit = alc262_check_volbit(nid); + if (!vbit) + return 0; + if (*vbits & vbit) /* a volume control for this mixer already there */ + return 0; + *vbits |= vbit; + snprintf(name, sizeof(name), "%s Playback Volume", pfx); + if (vbit == 2) + val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT); + return add_control(spec, ALC_CTL_WIDGET_VOL, name, val); +} + +static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid, + const char *pfx) +{ + char name[32]; + unsigned long val; + + if (!nid) + return 0; + snprintf(name, sizeof(name), "%s Playback Switch", pfx); + if (nid == 0x16) + val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + return add_control(spec, ALC_CTL_WIDGET_MUTE, name, val); +} + /* 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) { - hda_nid_t nid; + const char *pfx; + int vbits; 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(0x0c, 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 (nid == 0x16) { - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "Speaker Playback Volume", - HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - 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 (nid == 0x16) { - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "Headphone Playback Volume", - HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, - HDA_OUTPUT)); - if (err < 0) - return err; - 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; -} - -static int alc262_auto_create_analog_input_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg) -{ - int err; + if (!cfg->speaker_pins[0] && !cfg->hp_pins[0]) + pfx = "Master"; + else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + pfx = "Speaker"; + else + pfx = "Front"; + err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[0], pfx); + if (err < 0) + return err; + err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[0], "Speaker"); + if (err < 0) + return err; + err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[0], "Headphone"); + if (err < 0) + return err; - err = alc880_auto_create_analog_input_ctls(spec, cfg); + vbits = alc262_check_volbit(cfg->line_out_pins[0]) | + alc262_check_volbit(cfg->speaker_pins[0]) | + alc262_check_volbit(cfg->hp_pins[0]); + if (vbits == 1 || vbits == 2) + pfx = "Master"; /* only one mixer is used */ + else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + pfx = "Speaker"; + else + pfx = "Front"; + vbits = 0; + err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[0], pfx, &vbits); + if (err < 0) + return err; + err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[0], "Speaker", + &vbits); + if (err < 0) + return err; + err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[0], "Headphone", + &vbits); if (err < 0) return err; - /* digital-mic input pin is excluded in alc880_auto_create..() - * because it's under 0x18 - */ - if (cfg->input_pins[AUTO_PIN_MIC] == 0x12 || - cfg->input_pins[AUTO_PIN_FRONT_MIC] == 0x12) { - struct hda_input_mux *imux = &spec->private_imux[0]; - imux->items[imux->num_items].label = "Int Mic"; - imux->items[imux->num_items].index = 0x09; - imux->num_items++; - } return 0; } +#define alc262_auto_create_input_ctls \ + alc880_auto_create_input_ctls /* * generic initialization of ADC, input mixers and output mixers @@ -11281,7 +11358,7 @@ static int alc262_parse_auto_config(struct hda_codec *codec) err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg); if (err < 0) return err; - err = alc262_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = alc262_auto_create_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -11412,7 +11489,7 @@ static struct alc_config_preset alc262_presets[] = { }, [ALC262_HIPPO] = { .mixers = { alc262_hippo_mixer }, - .init_verbs = { alc262_init_verbs, alc262_hippo_unsol_verbs}, + .init_verbs = { alc262_init_verbs, alc_hp15_unsol_verbs}, .num_dacs = ARRAY_SIZE(alc262_dac_nids), .dac_nids = alc262_dac_nids, .hp_nid = 0x03, @@ -11421,7 +11498,8 @@ static struct alc_config_preset alc262_presets[] = { .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, .unsol_event = alc262_hippo_unsol_event, - .init_hook = alc262_hippo_init_hook, + .setup = alc262_hippo_setup, + .init_hook = alc262_hippo_automute, }, [ALC262_HIPPO_1] = { .mixers = { alc262_hippo1_mixer }, @@ -11434,7 +11512,8 @@ static struct alc_config_preset alc262_presets[] = { .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, .unsol_event = alc262_hippo_unsol_event, - .init_hook = alc262_hippo1_init_hook, + .setup = alc262_hippo1_setup, + .init_hook = alc262_hippo_automute, }, [ALC262_FUJITSU] = { .mixers = { alc262_fujitsu_mixer }, @@ -11497,7 +11576,8 @@ static struct alc_config_preset alc262_presets[] = { .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc262_hp_t5735_init_hook, + .setup = alc262_hp_t5735_setup, + .init_hook = alc_automute_amp, }, [ALC262_HP_RP5700] = { .mixers = { alc262_hp_rp5700_mixer }, @@ -11528,11 +11608,13 @@ static struct alc_config_preset alc262_presets[] = { .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, .unsol_event = alc262_hippo_unsol_event, - .init_hook = alc262_hippo_init_hook, + .setup = alc262_hippo_setup, + .init_hook = alc262_hippo_automute, }, [ALC262_BENQ_T31] = { .mixers = { alc262_benq_t31_mixer }, - .init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs, alc262_hippo_unsol_verbs }, + .init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs, + alc_hp15_unsol_verbs }, .num_dacs = ARRAY_SIZE(alc262_dac_nids), .dac_nids = alc262_dac_nids, .hp_nid = 0x03, @@ -11540,7 +11622,8 @@ static struct alc_config_preset alc262_presets[] = { .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, .unsol_event = alc262_hippo_unsol_event, - .init_hook = alc262_hippo_init_hook, + .setup = alc262_hippo_setup, + .init_hook = alc262_hippo_automute, }, [ALC262_ULTRA] = { .mixers = { alc262_ultra_mixer }, @@ -11592,9 +11675,9 @@ static struct alc_config_preset alc262_presets[] = { .dig_out_nid = ALC262_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc262_modes), .channel_mode = alc262_modes, - .input_mux = &alc262_dmic_capture_source, - .unsol_event = alc262_toshiba_s06_unsol_event, - .init_hook = alc262_toshiba_s06_init_hook, + .unsol_event = alc_sku_unsol_event, + .setup = alc262_toshiba_s06_setup, + .init_hook = alc_inithook, }, [ALC262_TOSHIBA_RX1] = { .mixers = { alc262_toshiba_rx1_mixer }, @@ -11606,7 +11689,8 @@ static struct alc_config_preset alc262_presets[] = { .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, .unsol_event = alc262_hippo_unsol_event, - .init_hook = alc262_hippo_init_hook, + .setup = alc262_hippo_setup, + .init_hook = alc262_hippo_automute, }, [ALC262_TYAN] = { .mixers = { alc262_tyan_mixer }, @@ -11619,7 +11703,8 @@ static struct alc_config_preset alc262_presets[] = { .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc262_tyan_init_hook, + .setup = alc262_tyan_setup, + .init_hook = alc_automute_amp, }, }; @@ -11654,8 +11739,8 @@ static int patch_alc262(struct hda_codec *codec) alc262_cfg_tbl); if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for %s, " - "trying auto-probe from BIOS...\n", codec->chip_name); + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); board_config = ALC262_AUTO; } @@ -11682,7 +11767,7 @@ static int patch_alc262(struct hda_codec *codec) } if (board_config != ALC262_AUTO) - setup_preset(spec, &alc262_presets[board_config]); + setup_preset(codec, &alc262_presets[board_config]); spec->stream_analog_playback = &alc262_pcm_analog_playback; spec->stream_analog_capture = &alc262_pcm_analog_capture; @@ -11708,7 +11793,7 @@ static int patch_alc262(struct hda_codec *codec) unsigned int wcap = get_wcaps(codec, 0x07); /* get type */ - wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + wcap = get_wcaps_type(wcap); if (wcap != AC_WID_AUD_IN) { spec->adc_nids = alc262_adc_nids_alt; spec->num_adc_nids = @@ -11723,7 +11808,7 @@ static int patch_alc262(struct hda_codec *codec) } } if (!spec->cap_mixer && !spec->no_analog) - set_capture_mixer(spec); + set_capture_mixer(codec); if (!spec->no_analog) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); @@ -11815,14 +11900,6 @@ static struct hda_verb alc268_toshiba_verbs[] = { { } /* end */ }; -static struct hda_input_mux alc268_acer_lc_capture_source = { - .num_items = 2, - .items = { - { "i-Mic", 0x6 }, - { "E-Mic", 0x0 }, - }, -}; - /* Acer specific */ /* bind volumes of both NID 0x02 and 0x03 */ static struct hda_bind_ctls alc268_acer_bind_master_vol = { @@ -11941,7 +12018,8 @@ static struct hda_verb alc268_acer_verbs[] = { /* unsolicited event for HP jack sensing */ #define alc268_toshiba_unsol_event alc262_hippo_unsol_event -#define alc268_toshiba_init_hook alc262_hippo_init_hook +#define alc268_toshiba_setup alc262_hippo_setup +#define alc268_toshiba_automute alc262_hippo_automute static void alc268_acer_unsol_event(struct hda_codec *codec, unsigned int res) @@ -11971,30 +12049,33 @@ static void alc268_aspire_one_speaker_automute(struct hda_codec *codec) AMP_IN_MUTE(0), bits); } - -static void alc268_acer_mic_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CONNECT_SEL, - present ? 0x0 : 0x6); -} - static void alc268_acer_lc_unsol_event(struct hda_codec *codec, unsigned int res) { - if ((res >> 26) == ALC880_HP_EVENT) + switch (res >> 26) { + case ALC880_HP_EVENT: alc268_aspire_one_speaker_automute(codec); - if ((res >> 26) == ALC880_MIC_EVENT) - alc268_acer_mic_automute(codec); + break; + case ALC880_MIC_EVENT: + alc_mic_automute(codec); + break; + } +} + +static void alc268_acer_lc_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->ext_mic.pin = 0x18; + spec->ext_mic.mux_idx = 0; + spec->int_mic.pin = 0x12; + spec->int_mic.mux_idx = 6; + spec->auto_mic = 1; } static void alc268_acer_lc_init_hook(struct hda_codec *codec) { alc268_aspire_one_speaker_automute(codec); - alc268_acer_mic_automute(codec); + alc_mic_automute(codec); } static struct snd_kcontrol_new alc268_dell_mixer[] = { @@ -12012,17 +12093,22 @@ 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}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, { } }; /* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc268_dell_init_hook(struct hda_codec *codec) +static void alc268_dell_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; - alc_automute_pin(codec); + spec->ext_mic.pin = 0x18; + spec->ext_mic.mux_idx = 0; + spec->int_mic.pin = 0x19; + spec->int_mic.mux_idx = 1; + spec->auto_mic = 1; } static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = { @@ -12043,38 +12129,16 @@ static struct hda_verb alc267_quanta_il1_verbs[] = { { } }; -static void alc267_quanta_il1_mic_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_write(codec, 0x23, 0, - AC_VERB_SET_CONNECT_SEL, - present ? 0x00 : 0x01); -} - -static void alc267_quanta_il1_init_hook(struct hda_codec *codec) +static void alc267_quanta_il1_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; - alc_automute_pin(codec); - alc267_quanta_il1_mic_automute(codec); -} - -static void alc267_quanta_il1_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case ALC880_MIC_EVENT: - alc267_quanta_il1_mic_automute(codec); - break; - default: - alc_sku_unsol_event(codec, res); - break; - } + spec->ext_mic.pin = 0x18; + spec->ext_mic.mux_idx = 0; + spec->int_mic.pin = 0x19; + spec->int_mic.mux_idx = 1; + spec->auto_mic = 1; } /* @@ -12154,21 +12218,16 @@ static struct hda_verb alc268_volume_init_verbs[] = { { } }; +static struct snd_kcontrol_new alc268_capture_nosrc_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), + { } /* end */ +}; + static struct snd_kcontrol_new alc268_capture_alt_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Capture Switch", 0x23, 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 = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, + _DEFINE_CAPSRC(1), { } /* end */ }; @@ -12177,18 +12236,7 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = { HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 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 = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, + _DEFINE_CAPSRC(2), { } /* end */ }; @@ -12275,26 +12323,38 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, const char *ctlname, int idx) { char name[32]; + hda_nid_t dac; int err; sprintf(name, "%s Playback Volume", ctlname); - if (nid == 0x14) { - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(0x02, 3, idx, - HDA_OUTPUT)); - if (err < 0) - return err; - } else if (nid == 0x15) { + switch (nid) { + case 0x14: + case 0x16: + dac = 0x02; + break; + case 0x15: + dac = 0x03; + break; + default: + return 0; + } + if (spec->multiout.dac_nids[0] != dac && + spec->multiout.dac_nids[1] != dac) { err = add_control(spec, ALC_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(0x03, 3, idx, + HDA_COMPOSE_AMP_VAL(dac, 3, idx, HDA_OUTPUT)); if (err < 0) return err; - } else - return -1; + spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac; + } + sprintf(name, "%s Playback Switch", ctlname); - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + if (nid != 0x16) + err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT)); + else /* mono */ + err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT)); if (err < 0) return err; return 0; @@ -12307,14 +12367,19 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, hda_nid_t nid; int err; - spec->multiout.num_dacs = 2; /* only use one dac */ spec->multiout.dac_nids = spec->private_dac_nids; - spec->multiout.dac_nids[0] = 2; - spec->multiout.dac_nids[1] = 3; nid = cfg->line_out_pins[0]; - if (nid) - alc268_new_analog_output(spec, nid, "Front", 0); + if (nid) { + const char *name; + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + name = "Speaker"; + else + name = "Front"; + err = alc268_new_analog_output(spec, nid, name, 0); + if (err < 0) + return err; + } nid = cfg->speaker_pins[0]; if (nid == 0x1d) { @@ -12323,16 +12388,23 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); if (err < 0) return err; + } else { + err = alc268_new_analog_output(spec, nid, "Speaker", 0); + if (err < 0) + return err; } nid = cfg->hp_pins[0]; - if (nid) - alc268_new_analog_output(spec, nid, "Headphone", 0); + if (nid) { + err = alc268_new_analog_output(spec, nid, "Headphone", 0); + if (err < 0) + return err; + } nid = cfg->line_out_pins[1] | cfg->line_out_pins[2]; if (nid == 0x16) { err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Mono Playback Switch", - HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_INPUT)); + HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT)); if (err < 0) return err; } @@ -12340,38 +12412,46 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, } /* create playback/capture controls for input pins */ -static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec, +static int alc268_auto_create_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, idx1; + return alc_auto_create_input_ctls(codec, cfg, 0, 0x23, 0x24); +} - for (i = 0; i < AUTO_PIN_LAST; i++) { - switch(cfg->input_pins[i]) { - case 0x18: - idx1 = 0; /* Mic 1 */ - break; - case 0x19: - idx1 = 1; /* Mic 2 */ - break; - case 0x1a: - idx1 = 2; /* Line In */ - break; - case 0x1c: - idx1 = 3; /* CD */ - break; - case 0x12: - case 0x13: - idx1 = 6; /* digital mics */ - break; - default: - continue; - } - imux->items[imux->num_items].label = auto_pin_cfg_labels[i]; - imux->items[imux->num_items].index = idx1; - imux->num_items++; +static void alc268_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, int pin_type) +{ + int idx; + + alc_set_pin_output(codec, nid, pin_type); + if (nid == 0x14 || nid == 0x16) + idx = 0; + else + idx = 1; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); +} + +static void alc268_auto_init_multi_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid = spec->autocfg.line_out_pins[0]; + if (nid) { + int pin_type = get_pin_type(spec->autocfg.line_out_type); + alc268_auto_set_output_and_unmute(codec, nid, pin_type); } - return 0; +} + +static void alc268_auto_init_hp_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t pin; + + pin = spec->autocfg.hp_pins[0]; + if (pin) + alc268_auto_set_output_and_unmute(codec, pin, PIN_HP); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT); } static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec) @@ -12382,9 +12462,10 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec) hda_nid_t line_nid = spec->autocfg.line_out_pins[0]; unsigned int dac_vol1, dac_vol2; - if (speaker_nid) { + if (line_nid == 0x1d || speaker_nid == 0x1d) { snd_hda_codec_write(codec, speaker_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + /* mute mixer inputs from 0x1d */ snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)); @@ -12392,6 +12473,7 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec) AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)); } else { + /* unmute mixer inputs from 0x1d */ snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)); snd_hda_codec_write(codec, 0x10, 0, @@ -12448,7 +12530,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec) err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg); if (err < 0) return err; - err = alc268_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = alc268_auto_create_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -12467,7 +12549,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec) add_mixer(spec, alc268_beep_mixer); add_verb(spec, alc268_volume_init_verbs); - spec->num_mux_defs = 1; + spec->num_mux_defs = 2; spec->input_mux = &spec->private_imux[0]; err = alc_auto_add_mic_boost(codec); @@ -12479,8 +12561,6 @@ static int alc268_parse_auto_config(struct hda_codec *codec) return 1; } -#define alc268_auto_init_multi_out alc882_auto_init_multi_out -#define alc268_auto_init_hp_out alc882_auto_init_hp_out #define alc268_auto_init_analog_input alc882_auto_init_analog_input /* init callback for auto-configuration model -- overriding the default init */ @@ -12523,8 +12603,11 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = { ALC268_ACER_ASPIRE_ONE), SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL), SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron Mini9", ALC268_DELL), + /* almost compatible with toshiba but with optional digital outs; + * auto-probing seems working fine + */ SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP TX25xx series", - ALC268_TOSHIBA), + ALC268_AUTO), SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST), SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO), SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA), @@ -12545,7 +12628,8 @@ static struct snd_pci_quirk alc268_ssid_cfg_tbl[] = { static struct alc_config_preset alc268_presets[] = { [ALC267_QUANTA_IL1] = { - .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer }, + .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer, + alc268_capture_nosrc_mixer }, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc267_quanta_il1_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), @@ -12555,9 +12639,9 @@ static struct alc_config_preset alc268_presets[] = { .hp_nid = 0x03, .num_channel_mode = ARRAY_SIZE(alc268_modes), .channel_mode = alc268_modes, - .input_mux = &alc268_capture_source, - .unsol_event = alc267_quanta_il1_unsol_event, - .init_hook = alc267_quanta_il1_init_hook, + .unsol_event = alc_sku_unsol_event, + .setup = alc267_quanta_il1_setup, + .init_hook = alc_inithook, }, [ALC268_3ST] = { .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, @@ -12589,7 +12673,8 @@ static struct alc_config_preset alc268_presets[] = { .channel_mode = alc268_modes, .input_mux = &alc268_capture_source, .unsol_event = alc268_toshiba_unsol_event, - .init_hook = alc268_toshiba_init_hook, + .setup = alc268_toshiba_setup, + .init_hook = alc268_toshiba_automute, }, [ALC268_ACER] = { .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer, @@ -12628,7 +12713,7 @@ static struct alc_config_preset alc268_presets[] = { [ALC268_ACER_ASPIRE_ONE] = { .mixers = { alc268_acer_aspire_one_mixer, alc268_beep_mixer, - alc268_capture_alt_mixer }, + alc268_capture_nosrc_mixer }, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_acer_aspire_one_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), @@ -12639,22 +12724,26 @@ static struct alc_config_preset alc268_presets[] = { .hp_nid = 0x03, .num_channel_mode = ARRAY_SIZE(alc268_modes), .channel_mode = alc268_modes, - .input_mux = &alc268_acer_lc_capture_source, .unsol_event = alc268_acer_lc_unsol_event, + .setup = alc268_acer_lc_setup, .init_hook = alc268_acer_lc_init_hook, }, [ALC268_DELL] = { - .mixers = { alc268_dell_mixer, alc268_beep_mixer }, + .mixers = { alc268_dell_mixer, alc268_beep_mixer, + alc268_capture_nosrc_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, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, .hp_nid = 0x02, .num_channel_mode = ARRAY_SIZE(alc268_modes), .channel_mode = alc268_modes, .unsol_event = alc_sku_unsol_event, - .init_hook = alc268_dell_init_hook, - .input_mux = &alc268_capture_source, + .setup = alc268_dell_setup, + .init_hook = alc_inithook, }, [ALC268_ZEPTO] = { .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, @@ -12671,8 +12760,8 @@ static struct alc_config_preset alc268_presets[] = { .num_channel_mode = ARRAY_SIZE(alc268_modes), .channel_mode = alc268_modes, .input_mux = &alc268_capture_source, - .unsol_event = alc268_toshiba_unsol_event, - .init_hook = alc268_toshiba_init_hook + .setup = alc268_toshiba_setup, + .init_hook = alc268_toshiba_automute, }, #ifdef CONFIG_SND_DEBUG [ALC268_TEST] = { @@ -12714,8 +12803,8 @@ static int patch_alc268(struct hda_codec *codec) ALC882_MODEL_LAST, alc268_models, alc268_ssid_cfg_tbl); if (board_config < 0 || board_config >= ALC268_MODEL_LAST) { - printk(KERN_INFO "hda_codec: Unknown model for %s, " - "trying auto-probe from BIOS...\n", codec->chip_name); + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); board_config = ALC268_AUTO; } @@ -12734,7 +12823,7 @@ static int patch_alc268(struct hda_codec *codec) } if (board_config != ALC268_AUTO) - setup_preset(spec, &alc268_presets[board_config]); + setup_preset(codec, &alc268_presets[board_config]); spec->stream_analog_playback = &alc268_pcm_analog_playback; spec->stream_analog_capture = &alc268_pcm_analog_capture; @@ -12770,22 +12859,30 @@ static int patch_alc268(struct hda_codec *codec) unsigned int wcap = get_wcaps(codec, 0x07); int i; + spec->capsrc_nids = alc268_capsrc_nids; /* get type */ - wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { + wcap = get_wcaps_type(wcap); + if (spec->auto_mic || + wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { spec->adc_nids = alc268_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt); - add_mixer(spec, alc268_capture_alt_mixer); + if (spec->auto_mic) + fixup_automic_adc(codec); + if (spec->auto_mic || spec->input_mux->num_items == 1) + add_mixer(spec, alc268_capture_nosrc_mixer); + else + add_mixer(spec, alc268_capture_alt_mixer); } else { spec->adc_nids = alc268_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids); add_mixer(spec, alc268_capture_mixer); } - spec->capsrc_nids = alc268_capsrc_nids; /* set default input source */ for (i = 0; i < spec->num_adc_nids; i++) snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i], 0, AC_VERB_SET_CONNECT_SEL, + i < spec->num_mux_defs ? + spec->input_mux[i].items[0].index : spec->input_mux->items[0].index); } @@ -12820,22 +12917,6 @@ static hda_nid_t alc269_capsrc_nids[1] = { * not a mux! */ -static struct hda_input_mux alc269_eeepc_dmic_capture_source = { - .num_items = 2, - .items = { - { "i-Mic", 0x5 }, - { "e-Mic", 0x0 }, - }, -}; - -static struct hda_input_mux alc269_eeepc_amic_capture_source = { - .num_items = 2, - .items = { - { "i-Mic", 0x1 }, - { "e-Mic", 0x0 }, - }, -}; - #define alc269_modes alc260_modes #define alc269_capture_source alc880_lg_lw_capture_source @@ -12997,16 +13078,6 @@ static void alc269_lifebook_speaker_automute(struct hda_codec *codec) AC_VERB_SET_PROC_COEF, 0x480); } -static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_write(codec, 0x23, 0, - AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x1); -} - static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec) { unsigned int present_laptop; @@ -13033,10 +13104,14 @@ static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec) static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec, unsigned int res) { - if ((res >> 26) == ALC880_HP_EVENT) + switch (res >> 26) { + case ALC880_HP_EVENT: alc269_quanta_fl1_speaker_automute(codec); - if ((res >> 26) == ALC880_MIC_EVENT) - alc269_quanta_fl1_mic_automute(codec); + break; + case ALC880_MIC_EVENT: + alc_mic_automute(codec); + break; + } } static void alc269_lifebook_unsol_event(struct hda_codec *codec, @@ -13048,10 +13123,20 @@ static void alc269_lifebook_unsol_event(struct hda_codec *codec, alc269_lifebook_mic_autoswitch(codec); } +static void alc269_quanta_fl1_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->ext_mic.pin = 0x18; + spec->ext_mic.mux_idx = 0; + spec->int_mic.pin = 0x19; + spec->int_mic.mux_idx = 1; + spec->auto_mic = 1; +} + static void alc269_quanta_fl1_init_hook(struct hda_codec *codec) { alc269_quanta_fl1_speaker_automute(codec); - alc269_quanta_fl1_mic_automute(codec); + alc_mic_automute(codec); } static void alc269_lifebook_init_hook(struct hda_codec *codec) @@ -13096,60 +13181,44 @@ static void alc269_speaker_automute(struct hda_codec *codec) AMP_IN_MUTE(0), bits); } -static void alc269_eeepc_dmic_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_write(codec, 0x23, 0, - AC_VERB_SET_CONNECT_SEL, (present ? 0 : 5)); -} - -static void alc269_eeepc_amic_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE, - 0x7000 | (0x00 << 8) | (present ? 0 : 0x80)); - snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE, - 0x7000 | (0x01 << 8) | (present ? 0x80 : 0)); -} - /* unsolicited event for HP jack sensing */ -static void alc269_eeepc_dmic_unsol_event(struct hda_codec *codec, +static void alc269_eeepc_unsol_event(struct hda_codec *codec, unsigned int res) { - if ((res >> 26) == ALC880_HP_EVENT) + switch (res >> 26) { + case ALC880_HP_EVENT: alc269_speaker_automute(codec); - - if ((res >> 26) == ALC880_MIC_EVENT) - alc269_eeepc_dmic_automute(codec); + break; + case ALC880_MIC_EVENT: + alc_mic_automute(codec); + break; + } } -static void alc269_eeepc_dmic_inithook(struct hda_codec *codec) +static void alc269_eeepc_dmic_setup(struct hda_codec *codec) { - alc269_speaker_automute(codec); - alc269_eeepc_dmic_automute(codec); + struct alc_spec *spec = codec->spec; + spec->ext_mic.pin = 0x18; + spec->ext_mic.mux_idx = 0; + spec->int_mic.pin = 0x12; + spec->int_mic.mux_idx = 5; + spec->auto_mic = 1; } -/* unsolicited event for HP jack sensing */ -static void alc269_eeepc_amic_unsol_event(struct hda_codec *codec, - unsigned int res) +static void alc269_eeepc_amic_setup(struct hda_codec *codec) { - if ((res >> 26) == ALC880_HP_EVENT) - alc269_speaker_automute(codec); - - if ((res >> 26) == ALC880_MIC_EVENT) - alc269_eeepc_amic_automute(codec); + struct alc_spec *spec = codec->spec; + spec->ext_mic.pin = 0x18; + spec->ext_mic.mux_idx = 0; + spec->int_mic.pin = 0x19; + spec->int_mic.mux_idx = 1; + spec->auto_mic = 1; } -static void alc269_eeepc_amic_inithook(struct hda_codec *codec) +static void alc269_eeepc_inithook(struct hda_codec *codec) { alc269_speaker_automute(codec); - alc269_eeepc_amic_automute(codec); + alc_mic_automute(codec); } /* @@ -13222,89 +13291,10 @@ static struct hda_verb alc269_init_verbs[] = { { } }; -/* 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 \ - alc262_auto_create_analog_input_ctls +#define alc269_auto_create_multi_out_ctls \ + alc268_auto_create_multi_out_ctls +#define alc269_auto_create_input_ctls \ + alc268_auto_create_input_ctls #ifdef CONFIG_SND_HDA_POWER_SAVE #define alc269_loopbacks alc880_loopbacks @@ -13354,7 +13344,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) 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); + err = alc269_auto_create_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -13379,15 +13369,15 @@ static int alc269_parse_auto_config(struct hda_codec *codec) return err; if (!spec->cap_mixer && !spec->no_analog) - set_capture_mixer(spec); + set_capture_mixer(codec); alc_ssid_check(codec, 0x15, 0x1b, 0x14); 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_multi_out alc268_auto_init_multi_out +#define alc269_auto_init_hp_out alc268_auto_init_hp_out #define alc269_auto_init_analog_input alc882_auto_init_analog_input @@ -13411,7 +13401,8 @@ static const char *alc269_models[ALC269_MODEL_LAST] = { [ALC269_ASUS_EEEPC_P703] = "eeepc-p703", [ALC269_ASUS_EEEPC_P901] = "eeepc-p901", [ALC269_FUJITSU] = "fujitsu", - [ALC269_LIFEBOOK] = "lifebook" + [ALC269_LIFEBOOK] = "lifebook", + [ALC269_AUTO] = "auto", }; static struct snd_pci_quirk alc269_cfg_tbl[] = { @@ -13455,6 +13446,7 @@ static struct alc_config_preset alc269_presets[] = { .channel_mode = alc269_modes, .input_mux = &alc269_capture_source, .unsol_event = alc269_quanta_fl1_unsol_event, + .setup = alc269_quanta_fl1_setup, .init_hook = alc269_quanta_fl1_init_hook, }, [ALC269_ASUS_EEEPC_P703] = { @@ -13467,9 +13459,9 @@ static struct alc_config_preset alc269_presets[] = { .hp_nid = 0x03, .num_channel_mode = ARRAY_SIZE(alc269_modes), .channel_mode = alc269_modes, - .input_mux = &alc269_eeepc_amic_capture_source, - .unsol_event = alc269_eeepc_amic_unsol_event, - .init_hook = alc269_eeepc_amic_inithook, + .unsol_event = alc269_eeepc_unsol_event, + .setup = alc269_eeepc_amic_setup, + .init_hook = alc269_eeepc_inithook, }, [ALC269_ASUS_EEEPC_P901] = { .mixers = { alc269_eeepc_mixer }, @@ -13481,9 +13473,9 @@ static struct alc_config_preset alc269_presets[] = { .hp_nid = 0x03, .num_channel_mode = ARRAY_SIZE(alc269_modes), .channel_mode = alc269_modes, - .input_mux = &alc269_eeepc_dmic_capture_source, - .unsol_event = alc269_eeepc_dmic_unsol_event, - .init_hook = alc269_eeepc_dmic_inithook, + .unsol_event = alc269_eeepc_unsol_event, + .setup = alc269_eeepc_dmic_setup, + .init_hook = alc269_eeepc_inithook, }, [ALC269_FUJITSU] = { .mixers = { alc269_fujitsu_mixer }, @@ -13495,9 +13487,9 @@ static struct alc_config_preset alc269_presets[] = { .hp_nid = 0x03, .num_channel_mode = ARRAY_SIZE(alc269_modes), .channel_mode = alc269_modes, - .input_mux = &alc269_eeepc_dmic_capture_source, - .unsol_event = alc269_eeepc_dmic_unsol_event, - .init_hook = alc269_eeepc_dmic_inithook, + .unsol_event = alc269_eeepc_unsol_event, + .setup = alc269_eeepc_dmic_setup, + .init_hook = alc269_eeepc_inithook, }, [ALC269_LIFEBOOK] = { .mixers = { alc269_lifebook_mixer }, @@ -13532,8 +13524,8 @@ static int patch_alc269(struct hda_codec *codec) alc269_cfg_tbl); if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for %s, " - "trying auto-probe from BIOS...\n", codec->chip_name); + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); board_config = ALC269_AUTO; } @@ -13558,7 +13550,7 @@ static int patch_alc269(struct hda_codec *codec) } if (board_config != ALC269_AUTO) - setup_preset(spec, &alc269_presets[board_config]); + setup_preset(codec, &alc269_presets[board_config]); if (codec->subsystem_id == 0x17aa3bf8) { /* Due to a hardware problem on Lenovo Ideadpad, we need to @@ -13577,7 +13569,7 @@ static int patch_alc269(struct hda_codec *codec) spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); spec->capsrc_nids = alc269_capsrc_nids; if (!spec->cap_mixer) - set_capture_mixer(spec); + set_capture_mixer(codec); set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); spec->vmaster_nid = 0x02; @@ -14127,23 +14119,23 @@ static struct hda_verb alc861_auto_init_verbs[] = { {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, /* set Mic 1 */ @@ -14215,64 +14207,96 @@ static struct hda_input_mux alc861_capture_source = { }, }; +static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t mix, srcs[5]; + int i, j, num; + + if (snd_hda_get_connections(codec, pin, &mix, 1) != 1) + return 0; + num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs)); + if (num < 0) + return 0; + for (i = 0; i < num; i++) { + unsigned int type; + type = get_wcaps_type(get_wcaps(codec, srcs[i])); + if (type != AC_WID_AUD_OUT) + continue; + for (j = 0; j < spec->multiout.num_dacs; j++) + if (spec->multiout.dac_nids[j] == srcs[i]) + break; + if (j >= spec->multiout.num_dacs) + return srcs[i]; + } + return 0; +} + /* fill in the dac_nids table from the parsed pin configuration */ -static int alc861_auto_fill_dac_nids(struct alc_spec *spec, +static int alc861_auto_fill_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { + struct alc_spec *spec = codec->spec; int i; - hda_nid_t nid; + hda_nid_t nid, dac; spec->multiout.dac_nids = spec->private_dac_nids; for (i = 0; i < cfg->line_outs; i++) { nid = cfg->line_out_pins[i]; - if (nid) { - if (i >= ARRAY_SIZE(alc861_dac_nids)) - continue; - spec->multiout.dac_nids[i] = alc861_dac_nids[i]; - } + dac = alc861_look_for_dac(codec, nid); + if (!dac) + continue; + spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac; } - spec->multiout.num_dacs = cfg->line_outs; return 0; } +static int alc861_create_out_sw(struct hda_codec *codec, const char *pfx, + hda_nid_t nid, unsigned int chs) +{ + char name[32]; + snprintf(name, sizeof(name), "%s Playback Switch", pfx); + return add_control(codec->spec, ALC_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); +} + /* add playback controls from the parsed DAC table */ -static int alc861_auto_create_multi_out_ctls(struct alc_spec *spec, +static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - char name[32]; + struct alc_spec *spec = codec->spec; static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" }; hda_nid_t nid; - int i, idx, err; + int i, err; + + if (cfg->line_outs == 1) { + const char *pfx = NULL; + if (!cfg->hp_outs) + pfx = "Master"; + else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + pfx = "Speaker"; + if (pfx) { + nid = spec->multiout.dac_nids[0]; + return alc861_create_out_sw(codec, pfx, nid, 3); + } + } for (i = 0; i < cfg->line_outs; i++) { nid = spec->multiout.dac_nids[i]; if (!nid) continue; - if (nid == 0x05) { + if (i == 2) { /* Center/LFE */ - err = add_control(spec, ALC_CTL_BIND_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid, 1, 0, - HDA_OUTPUT)); + err = alc861_create_out_sw(codec, "Center", nid, 1); if (err < 0) return err; - err = add_control(spec, ALC_CTL_BIND_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid, 2, 0, - HDA_OUTPUT)); + err = alc861_create_out_sw(codec, "LFE", nid, 2); if (err < 0) return err; } else { - for (idx = 0; idx < ARRAY_SIZE(alc861_dac_nids) - 1; - idx++) - if (nid == alc861_dac_nids[idx]) - break; - sprintf(name, "%s Playback Switch", chname[idx]); - err = add_control(spec, ALC_CTL_BIND_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); + err = alc861_create_out_sw(codec, chname[i], nid, 3); if (err < 0) return err; } @@ -14280,8 +14304,9 @@ static int alc861_auto_create_multi_out_ctls(struct alc_spec *spec, return 0; } -static int alc861_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin) +static int alc861_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) { + struct alc_spec *spec = codec->spec; int err; hda_nid_t nid; @@ -14289,70 +14314,49 @@ static int alc861_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin) return 0; if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) { - nid = 0x03; - 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; - spec->multiout.hp_nid = nid; + nid = alc861_look_for_dac(codec, pin); + if (nid) { + err = alc861_create_out_sw(codec, "Headphone", nid, 3); + if (err < 0) + return err; + spec->multiout.hp_nid = nid; + } } return 0; } /* create playback/capture controls for input pins */ -static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec, +static int alc861_auto_create_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx, idx1; - - for (i = 0; i < AUTO_PIN_LAST; i++) { - switch (cfg->input_pins[i]) { - case 0x0c: - idx1 = 1; - idx = 2; /* Line In */ - break; - case 0x0f: - idx1 = 2; - idx = 2; /* Line In */ - break; - case 0x0d: - idx1 = 0; - idx = 1; /* Mic In */ - break; - case 0x10: - idx1 = 3; - idx = 1; /* Mic In */ - break; - case 0x11: - idx1 = 4; - idx = 0; /* CD */ - break; - default: - continue; - } - - err = new_analog_input(spec, cfg->input_pins[i], - auto_pin_cfg_labels[i], idx, 0x15); - if (err < 0) - return err; - - imux->items[imux->num_items].label = auto_pin_cfg_labels[i]; - imux->items[imux->num_items].index = idx1; - imux->num_items++; - } - return 0; + return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x08, 0); } static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, - int pin_type, int dac_idx) + int pin_type, hda_nid_t dac) { + hda_nid_t mix, srcs[5]; + int i, num; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); - snd_hda_codec_write(codec, dac_idx, 0, AC_VERB_SET_AMP_GAIN_MUTE, + snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + if (snd_hda_get_connections(codec, nid, &mix, 1) != 1) + return; + num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs)); + if (num < 0) + return; + for (i = 0; i < num; i++) { + unsigned int mute; + if (srcs[i] == dac || srcs[i] == 0x15) + mute = AMP_IN_UNMUTE(i); + else + mute = AMP_IN_MUTE(i); + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + mute); + } } static void alc861_auto_init_multi_out(struct hda_codec *codec) @@ -14372,15 +14376,17 @@ static void alc861_auto_init_multi_out(struct hda_codec *codec) static void alc861_auto_init_hp_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t pin; - pin = spec->autocfg.hp_pins[0]; - if (pin) /* connect to front */ - alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, + if (spec->autocfg.hp_outs) + alc861_auto_set_output_and_unmute(codec, + spec->autocfg.hp_pins[0], + PIN_HP, + spec->multiout.hp_nid); + if (spec->autocfg.speaker_outs) + alc861_auto_set_output_and_unmute(codec, + spec->autocfg.speaker_pins[0], + PIN_OUT, spec->multiout.dac_nids[0]); - pin = spec->autocfg.speaker_pins[0]; - if (pin) - alc861_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } static void alc861_auto_init_analog_input(struct hda_codec *codec) @@ -14412,16 +14418,16 @@ static int alc861_parse_auto_config(struct hda_codec *codec) if (!spec->autocfg.line_outs) return 0; /* can't find valid BIOS pin config */ - err = alc861_auto_fill_dac_nids(spec, &spec->autocfg); + err = alc861_auto_fill_dac_nids(codec, &spec->autocfg); if (err < 0) return err; - err = alc861_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg); if (err < 0) return err; - err = alc861_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + err = alc861_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); if (err < 0) return err; - err = alc861_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = alc861_auto_create_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -14440,7 +14446,7 @@ static int alc861_parse_auto_config(struct hda_codec *codec) spec->adc_nids = alc861_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids); - set_capture_mixer(spec); + set_capture_mixer(codec); alc_ssid_check(codec, 0x0e, 0x0f, 0x0b); @@ -14633,8 +14639,8 @@ static int patch_alc861(struct hda_codec *codec) alc861_cfg_tbl); if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for %s, " - "trying auto-probe from BIOS...\n", codec->chip_name); + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); board_config = ALC861_AUTO; } @@ -14659,7 +14665,7 @@ static int patch_alc861(struct hda_codec *codec) } if (board_config != ALC861_AUTO) - setup_preset(spec, &alc861_presets[board_config]); + setup_preset(codec, &alc861_presets[board_config]); spec->stream_analog_playback = &alc861_pcm_analog_playback; spec->stream_analog_capture = &alc861_pcm_analog_capture; @@ -15062,12 +15068,15 @@ static void alc861vd_lenovo_mic_automute(struct hda_codec *codec) HDA_AMP_MUTE, bits); } -static void alc861vd_lenovo_init_hook(struct hda_codec *codec) +static void alc861vd_lenovo_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x1b; spec->autocfg.speaker_pins[0] = 0x14; +} + +static void alc861vd_lenovo_init_hook(struct hda_codec *codec) +{ alc_automute_amp(codec); alc861vd_lenovo_mic_automute(codec); } @@ -15131,13 +15140,12 @@ static struct hda_verb alc861vd_dallas_verbs[] = { }; /* toggle speaker-output according to the hp-jack state */ -static void alc861vd_dallas_init_hook(struct hda_codec *codec) +static void alc861vd_dallas_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; - alc_automute_amp(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -15170,7 +15178,7 @@ 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, 0x1339, "Asus G1", ALC660VD_3ST),*/ /* auto */ SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S), SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG), SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST), @@ -15251,6 +15259,7 @@ static struct alc_config_preset alc861vd_presets[] = { .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_capture_source, .unsol_event = alc861vd_lenovo_unsol_event, + .setup = alc861vd_lenovo_setup, .init_hook = alc861vd_lenovo_init_hook, }, [ALC861VD_DALLAS] = { @@ -15262,7 +15271,8 @@ static struct alc_config_preset alc861vd_presets[] = { .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_dallas_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc861vd_dallas_init_hook, + .setup = alc861vd_dallas_setup, + .init_hook = alc_automute_amp, }, [ALC861VD_HP] = { .mixers = { alc861vd_hp_mixer }, @@ -15274,7 +15284,8 @@ static struct alc_config_preset alc861vd_presets[] = { .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_hp_capture_source, .unsol_event = alc_automute_amp_unsol_event, - .init_hook = alc861vd_dallas_init_hook, + .setup = alc861vd_dallas_setup, + .init_hook = alc_automute_amp, }, [ALC660VD_ASUS_V1S] = { .mixers = { alc861vd_lenovo_mixer }, @@ -15289,6 +15300,7 @@ static struct alc_config_preset alc861vd_presets[] = { .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_capture_source, .unsol_event = alc861vd_lenovo_unsol_event, + .setup = alc861vd_lenovo_setup, .init_hook = alc861vd_lenovo_init_hook, }, }; @@ -15296,6 +15308,13 @@ static struct alc_config_preset alc861vd_presets[] = { /* * BIOS auto configuration */ +static int alc861vd_auto_create_input_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x09, 0); +} + + static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int dac_idx) { @@ -15330,7 +15349,6 @@ static void alc861vd_auto_init_hp_out(struct hda_codec *codec) alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } -#define alc861vd_is_input_pin(nid) alc880_is_input_pin(nid) #define ALC861VD_PIN_CD_NID ALC880_PIN_CD_NID static void alc861vd_auto_init_analog_input(struct hda_codec *codec) @@ -15340,7 +15358,7 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec) for (i = 0; i < AUTO_PIN_LAST; i++) { hda_nid_t nid = spec->autocfg.input_pins[i]; - if (alc861vd_is_input_pin(nid)) { + if (alc_is_input_pin(codec, nid)) { alc_set_input_pin(codec, nid, i); if (nid != ALC861VD_PIN_CD_NID && (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) @@ -15404,13 +15422,25 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, if (err < 0) return err; } else { - sprintf(name, "%s Playback Volume", chname[i]); + const char *pfx; + if (cfg->line_outs == 1 && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + if (!cfg->hp_pins) + pfx = "Speaker"; + else + pfx = "PCM"; + } else + pfx = chname[i]; + sprintf(name, "%s Playback Volume", pfx); err = add_control(spec, ALC_CTL_WIDGET_VOL, name, HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT)); if (err < 0) return err; - sprintf(name, "%s Playback Switch", chname[i]); + if (cfg->line_outs == 1 && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + pfx = "Speaker"; + sprintf(name, "%s Playback Switch", pfx); err = add_control(spec, ALC_CTL_BIND_MUTE, name, HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT)); @@ -15503,7 +15533,7 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) "Headphone"); if (err < 0) return err; - err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg); + err = alc861vd_auto_create_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -15541,6 +15571,29 @@ static void alc861vd_auto_init(struct hda_codec *codec) alc_inithook(codec); } +enum { + ALC660VD_FIX_ASUS_GPIO1 +}; + +/* reset GPIO1 */ +static const struct hda_verb alc660vd_fix_asus_gpio1_verbs[] = { + {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, + { } +}; + +static const struct alc_fixup alc861vd_fixups[] = { + [ALC660VD_FIX_ASUS_GPIO1] = { + .verbs = alc660vd_fix_asus_gpio1_verbs, + }, +}; + +static struct snd_pci_quirk alc861vd_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1), + {} +}; + static int patch_alc861vd(struct hda_codec *codec) { struct alc_spec *spec; @@ -15557,11 +15610,13 @@ static int patch_alc861vd(struct hda_codec *codec) alc861vd_cfg_tbl); if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) { - printk(KERN_INFO "hda_codec: Unknown model for %s, " - "trying auto-probe from BIOS...\n", codec->chip_name); + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); board_config = ALC861VD_AUTO; } + alc_pick_fixup(codec, alc861vd_fixup_tbl, alc861vd_fixups); + if (board_config == ALC861VD_AUTO) { /* automatic parse from the BIOS config */ err = alc861vd_parse_auto_config(codec); @@ -15583,7 +15638,7 @@ static int patch_alc861vd(struct hda_codec *codec) } if (board_config != ALC861VD_AUTO) - setup_preset(spec, &alc861vd_presets[board_config]); + setup_preset(codec, &alc861vd_presets[board_config]); if (codec->vendor_id == 0x10ec0660) { /* always turn on EAPD */ @@ -15603,7 +15658,7 @@ static int patch_alc861vd(struct hda_codec *codec) if (!spec->capsrc_nids) spec->capsrc_nids = alc861vd_capsrc_nids; - set_capture_mixer(spec); + set_capture_mixer(codec); set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); spec->vmaster_nid = 0x02; @@ -15644,9 +15699,9 @@ static hda_nid_t alc272_dac_nids[2] = { 0x02, 0x03 }; -static hda_nid_t alc662_adc_nids[1] = { +static hda_nid_t alc662_adc_nids[2] = { /* ADC1-2 */ - 0x09, + 0x09, 0x08 }; static hda_nid_t alc272_adc_nids[1] = { @@ -15654,7 +15709,7 @@ static hda_nid_t alc272_adc_nids[1] = { 0x08, }; -static hda_nid_t alc662_capsrc_nids[1] = { 0x22 }; +static hda_nid_t alc662_capsrc_nids[2] = { 0x22, 0x23 }; static hda_nid_t alc272_capsrc_nids[1] = { 0x23 }; @@ -15678,14 +15733,6 @@ static struct hda_input_mux alc662_lenovo_101e_capture_source = { }, }; -static struct hda_input_mux alc662_eeepc_capture_source = { - .num_items = 2, - .items = { - { "i-Mic", 0x1 }, - { "e-Mic", 0x0 }, - }, -}; - static struct hda_input_mux alc663_capture_source = { .num_items = 3, .items = { @@ -15695,23 +15742,7 @@ static struct hda_input_mux alc663_capture_source = { }, }; -static struct hda_input_mux alc663_m51va_capture_source = { - .num_items = 2, - .items = { - { "Ext-Mic", 0x0 }, - { "D-Mic", 0x9 }, - }, -}; - -#if 1 /* set to 0 for testing other input sources below */ -static struct hda_input_mux alc272_nc10_capture_source = { - .num_items = 2, - .items = { - { "Autoselect Mic", 0x0 }, - { "Internal Mic", 0x1 }, - }, -}; -#else +#if 0 /* set to 1 for testing other input sources below */ static struct hda_input_mux alc272_nc10_capture_source = { .num_items = 16, .items = { @@ -16380,47 +16411,44 @@ static void alc662_lenovo_101e_unsol_event(struct hda_codec *codec, alc662_lenovo_101e_ispeaker_automute(codec); } -static void alc662_eeepc_mic_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, - 0x7000 | (0x00 << 8) | (present ? 0 : 0x80)); - snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, - 0x7000 | (0x00 << 8) | (present ? 0 : 0x80)); - snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, - 0x7000 | (0x01 << 8) | (present ? 0x80 : 0)); - snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, - 0x7000 | (0x01 << 8) | (present ? 0x80 : 0)); -} - /* unsolicited event for HP jack sensing */ static void alc662_eeepc_unsol_event(struct hda_codec *codec, unsigned int res) { if ((res >> 26) == ALC880_MIC_EVENT) - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); else alc262_hippo_unsol_event(codec, res); } +static void alc662_eeepc_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + alc262_hippo1_setup(codec); + spec->ext_mic.pin = 0x18; + spec->ext_mic.mux_idx = 0; + spec->int_mic.pin = 0x19; + spec->int_mic.mux_idx = 1; + spec->auto_mic = 1; +} + static void alc662_eeepc_inithook(struct hda_codec *codec) { - alc262_hippo1_init_hook(codec); - alc662_eeepc_mic_automute(codec); + alc262_hippo_automute(codec); + alc_mic_automute(codec); } -static void alc662_eeepc_ep20_inithook(struct hda_codec *codec) +static void alc662_eeepc_ep20_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x14; spec->autocfg.speaker_pins[0] = 0x1b; - alc262_hippo_master_update(codec); } +#define alc662_eeepc_ep20_inithook alc262_hippo_master_update + static void alc663_m51va_speaker_automute(struct hda_codec *codec) { unsigned int present; @@ -16531,23 +16559,6 @@ static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec) } } -static void alc663_m51va_mic_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; - snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, - 0x7000 | (0x00 << 8) | (present ? 0 : 0x80)); - snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, - 0x7000 | (0x00 << 8) | (present ? 0 : 0x80)); - snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, - 0x7000 | (0x09 << 8) | (present ? 0x80 : 0)); - snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, - 0x7000 | (0x09 << 8) | (present ? 0x80 : 0)); -} - static void alc663_m51va_unsol_event(struct hda_codec *codec, unsigned int res) { @@ -16556,36 +16567,32 @@ static void alc663_m51va_unsol_event(struct hda_codec *codec, alc663_m51va_speaker_automute(codec); break; case ALC880_MIC_EVENT: - alc663_m51va_mic_automute(codec); + alc_mic_automute(codec); break; } } +static void alc663_m51va_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->ext_mic.pin = 0x18; + spec->ext_mic.mux_idx = 0; + spec->int_mic.pin = 0x12; + spec->int_mic.mux_idx = 1; + spec->auto_mic = 1; +} + static void alc663_m51va_inithook(struct hda_codec *codec) { alc663_m51va_speaker_automute(codec); - alc663_m51va_mic_automute(codec); + alc_mic_automute(codec); } /* ***************** Mode1 ******************************/ -static void alc663_mode1_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case ALC880_HP_EVENT: - alc663_m51va_speaker_automute(codec); - break; - case ALC880_MIC_EVENT: - alc662_eeepc_mic_automute(codec); - break; - } -} +#define alc663_mode1_unsol_event alc663_m51va_unsol_event +#define alc663_mode1_setup alc663_m51va_setup +#define alc663_mode1_inithook alc663_m51va_inithook -static void alc663_mode1_inithook(struct hda_codec *codec) -{ - alc663_m51va_speaker_automute(codec); - alc662_eeepc_mic_automute(codec); -} /* ***************** Mode2 ******************************/ static void alc662_mode2_unsol_event(struct hda_codec *codec, unsigned int res) @@ -16595,15 +16602,17 @@ static void alc662_mode2_unsol_event(struct hda_codec *codec, alc662_f5z_speaker_automute(codec); break; case ALC880_MIC_EVENT: - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); break; } } +#define alc662_mode2_setup alc663_m51va_setup + static void alc662_mode2_inithook(struct hda_codec *codec) { alc662_f5z_speaker_automute(codec); - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); } /* ***************** Mode3 ******************************/ static void alc663_mode3_unsol_event(struct hda_codec *codec, @@ -16614,15 +16623,17 @@ static void alc663_mode3_unsol_event(struct hda_codec *codec, alc663_two_hp_m1_speaker_automute(codec); break; case ALC880_MIC_EVENT: - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); break; } } +#define alc663_mode3_setup alc663_m51va_setup + static void alc663_mode3_inithook(struct hda_codec *codec) { alc663_two_hp_m1_speaker_automute(codec); - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); } /* ***************** Mode4 ******************************/ static void alc663_mode4_unsol_event(struct hda_codec *codec, @@ -16633,15 +16644,17 @@ static void alc663_mode4_unsol_event(struct hda_codec *codec, alc663_21jd_two_speaker_automute(codec); break; case ALC880_MIC_EVENT: - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); break; } } +#define alc663_mode4_setup alc663_m51va_setup + static void alc663_mode4_inithook(struct hda_codec *codec) { alc663_21jd_two_speaker_automute(codec); - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); } /* ***************** Mode5 ******************************/ static void alc663_mode5_unsol_event(struct hda_codec *codec, @@ -16652,15 +16665,17 @@ static void alc663_mode5_unsol_event(struct hda_codec *codec, alc663_15jd_two_speaker_automute(codec); break; case ALC880_MIC_EVENT: - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); break; } } +#define alc663_mode5_setup alc663_m51va_setup + static void alc663_mode5_inithook(struct hda_codec *codec) { alc663_15jd_two_speaker_automute(codec); - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); } /* ***************** Mode6 ******************************/ static void alc663_mode6_unsol_event(struct hda_codec *codec, @@ -16671,15 +16686,17 @@ static void alc663_mode6_unsol_event(struct hda_codec *codec, alc663_two_hp_m2_speaker_automute(codec); break; case ALC880_MIC_EVENT: - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); break; } } +#define alc663_mode6_setup alc663_m51va_setup + static void alc663_mode6_inithook(struct hda_codec *codec) { alc663_two_hp_m2_speaker_automute(codec); - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); } static void alc663_g71v_hp_automute(struct hda_codec *codec) @@ -16721,16 +16738,18 @@ static void alc663_g71v_unsol_event(struct hda_codec *codec, alc663_g71v_front_automute(codec); break; case ALC880_MIC_EVENT: - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); break; } } +#define alc663_g71v_setup alc663_m51va_setup + static void alc663_g71v_inithook(struct hda_codec *codec) { alc663_g71v_front_automute(codec); alc663_g71v_hp_automute(codec); - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); } static void alc663_g50v_unsol_event(struct hda_codec *codec, @@ -16741,15 +16760,17 @@ static void alc663_g50v_unsol_event(struct hda_codec *codec, alc663_m51va_speaker_automute(codec); break; case ALC880_MIC_EVENT: - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); break; } } +#define alc663_g50v_setup alc663_m51va_setup + static void alc663_g50v_inithook(struct hda_codec *codec) { alc663_m51va_speaker_automute(codec); - alc662_eeepc_mic_automute(codec); + alc_mic_automute(codec); } static struct snd_kcontrol_new alc662_ecs_mixer[] = { @@ -16876,6 +16897,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS), SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K", ALC662_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB200", ALC663_ASUS_MODE4), SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", ALC662_3ST_6ch_DIG), @@ -16953,8 +16975,8 @@ static struct alc_config_preset alc662_presets[] = { .dac_nids = alc662_dac_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc662_eeepc_capture_source, .unsol_event = alc662_eeepc_unsol_event, + .setup = alc662_eeepc_setup, .init_hook = alc662_eeepc_inithook, }, [ALC662_ASUS_EEEPC_EP20] = { @@ -16968,6 +16990,7 @@ static struct alc_config_preset alc662_presets[] = { .channel_mode = alc662_3ST_6ch_modes, .input_mux = &alc662_lenovo_101e_capture_source, .unsol_event = alc662_eeepc_unsol_event, + .setup = alc662_eeepc_ep20_setup, .init_hook = alc662_eeepc_ep20_inithook, }, [ALC662_ECS] = { @@ -16978,8 +17001,8 @@ static struct alc_config_preset alc662_presets[] = { .dac_nids = alc662_dac_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc662_eeepc_capture_source, .unsol_event = alc662_eeepc_unsol_event, + .setup = alc662_eeepc_setup, .init_hook = alc662_eeepc_inithook, }, [ALC663_ASUS_M51VA] = { @@ -16990,8 +17013,8 @@ static struct alc_config_preset alc662_presets[] = { .dig_out_nid = ALC662_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc663_m51va_capture_source, .unsol_event = alc663_m51va_unsol_event, + .setup = alc663_m51va_setup, .init_hook = alc663_m51va_inithook, }, [ALC663_ASUS_G71V] = { @@ -17002,8 +17025,8 @@ static struct alc_config_preset alc662_presets[] = { .dig_out_nid = ALC662_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc662_eeepc_capture_source, .unsol_event = alc663_g71v_unsol_event, + .setup = alc663_g71v_setup, .init_hook = alc663_g71v_inithook, }, [ALC663_ASUS_H13] = { @@ -17013,7 +17036,6 @@ static struct alc_config_preset alc662_presets[] = { .dac_nids = alc662_dac_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc663_m51va_capture_source, .unsol_event = alc663_m51va_unsol_event, .init_hook = alc663_m51va_inithook, }, @@ -17027,6 +17049,7 @@ static struct alc_config_preset alc662_presets[] = { .channel_mode = alc662_3ST_6ch_modes, .input_mux = &alc663_capture_source, .unsol_event = alc663_g50v_unsol_event, + .setup = alc663_g50v_setup, .init_hook = alc663_g50v_inithook, }, [ALC663_ASUS_MODE1] = { @@ -17040,8 +17063,8 @@ static struct alc_config_preset alc662_presets[] = { .dig_out_nid = ALC662_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc662_eeepc_capture_source, .unsol_event = alc663_mode1_unsol_event, + .setup = alc663_mode1_setup, .init_hook = alc663_mode1_inithook, }, [ALC662_ASUS_MODE2] = { @@ -17054,8 +17077,8 @@ static struct alc_config_preset alc662_presets[] = { .dig_out_nid = ALC662_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc662_eeepc_capture_source, .unsol_event = alc662_mode2_unsol_event, + .setup = alc662_mode2_setup, .init_hook = alc662_mode2_inithook, }, [ALC663_ASUS_MODE3] = { @@ -17069,8 +17092,8 @@ static struct alc_config_preset alc662_presets[] = { .dig_out_nid = ALC662_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc662_eeepc_capture_source, .unsol_event = alc663_mode3_unsol_event, + .setup = alc663_mode3_setup, .init_hook = alc663_mode3_inithook, }, [ALC663_ASUS_MODE4] = { @@ -17084,8 +17107,8 @@ static struct alc_config_preset alc662_presets[] = { .dig_out_nid = ALC662_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc662_eeepc_capture_source, .unsol_event = alc663_mode4_unsol_event, + .setup = alc663_mode4_setup, .init_hook = alc663_mode4_inithook, }, [ALC663_ASUS_MODE5] = { @@ -17099,8 +17122,8 @@ static struct alc_config_preset alc662_presets[] = { .dig_out_nid = ALC662_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc662_eeepc_capture_source, .unsol_event = alc663_mode5_unsol_event, + .setup = alc663_mode5_setup, .init_hook = alc663_mode5_inithook, }, [ALC663_ASUS_MODE6] = { @@ -17114,8 +17137,8 @@ static struct alc_config_preset alc662_presets[] = { .dig_out_nid = ALC662_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc662_eeepc_capture_source, .unsol_event = alc663_mode6_unsol_event, + .setup = alc663_mode6_setup, .init_hook = alc663_mode6_inithook, }, [ALC272_DELL] = { @@ -17129,8 +17152,8 @@ static struct alc_config_preset alc662_presets[] = { .num_adc_nids = ARRAY_SIZE(alc272_adc_nids), .capsrc_nids = alc272_capsrc_nids, .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc663_m51va_capture_source, .unsol_event = alc663_m51va_unsol_event, + .setup = alc663_m51va_setup, .init_hook = alc663_m51va_inithook, }, [ALC272_DELL_ZM1] = { @@ -17141,11 +17164,11 @@ static struct alc_config_preset alc662_presets[] = { .dac_nids = alc662_dac_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .adc_nids = alc662_adc_nids, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), + .num_adc_nids = 1, .capsrc_nids = alc662_capsrc_nids, .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc663_m51va_capture_source, .unsol_event = alc663_m51va_unsol_event, + .setup = alc663_m51va_setup, .init_hook = alc663_m51va_inithook, }, [ALC272_SAMSUNG_NC10] = { @@ -17156,8 +17179,9 @@ static struct alc_config_preset alc662_presets[] = { .dac_nids = alc272_dac_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, - .input_mux = &alc272_nc10_capture_source, + /*.input_mux = &alc272_nc10_capture_source,*/ .unsol_event = alc663_mode4_unsol_event, + .setup = alc663_mode4_setup, .init_hook = alc663_mode4_inithook, }, }; @@ -17167,58 +17191,145 @@ static struct alc_config_preset alc662_presets[] = { * BIOS auto configuration */ +/* convert from MIX nid to DAC */ +static inline hda_nid_t alc662_mix_to_dac(hda_nid_t nid) +{ + if (nid == 0x0f) + return 0x02; + else if (nid >= 0x0c && nid <= 0x0e) + return nid - 0x0c + 0x02; + else + return 0; +} + +/* get MIX nid connected to the given pin targeted to DAC */ +static hda_nid_t alc662_dac_to_mix(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) +{ + hda_nid_t mix[4]; + int i, num; + + num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); + for (i = 0; i < num; i++) { + if (alc662_mix_to_dac(mix[i]) == dac) + return mix[i]; + } + return 0; +} + +/* look for an empty DAC slot */ +static hda_nid_t alc662_look_for_dac(struct hda_codec *codec, hda_nid_t pin) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t srcs[5]; + int i, j, num; + + num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs)); + if (num < 0) + return 0; + for (i = 0; i < num; i++) { + hda_nid_t nid = alc662_mix_to_dac(srcs[i]); + if (!nid) + continue; + for (j = 0; j < spec->multiout.num_dacs; j++) + if (spec->multiout.dac_nids[j] == nid) + break; + if (j >= spec->multiout.num_dacs) + return nid; + } + return 0; +} + +/* fill in the dac_nids table from the parsed pin configuration */ +static int alc662_auto_fill_dac_nids(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + struct alc_spec *spec = codec->spec; + int i; + hda_nid_t dac; + + spec->multiout.dac_nids = spec->private_dac_nids; + for (i = 0; i < cfg->line_outs; i++) { + dac = alc662_look_for_dac(codec, cfg->line_out_pins[i]); + if (!dac) + continue; + spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac; + } + return 0; +} + +static int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx, + hda_nid_t nid, unsigned int chs) +{ + char name[32]; + sprintf(name, "%s Playback Volume", pfx); + return add_control(spec, ALC_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); +} + +static int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx, + hda_nid_t nid, unsigned int chs) +{ + char name[32]; + sprintf(name, "%s Playback Switch", pfx); + return add_control(spec, ALC_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT)); +} + +#define alc662_add_stereo_vol(spec, pfx, nid) \ + alc662_add_vol_ctl(spec, pfx, nid, 3) +#define alc662_add_stereo_sw(spec, pfx, nid) \ + alc662_add_sw_ctl(spec, pfx, nid, 3) + /* add playback controls from the parsed DAC table */ -static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec, +static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - char name[32]; + struct alc_spec *spec = codec->spec; static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" }; - hda_nid_t nid; + hda_nid_t nid, mix; int i, err; for (i = 0; i < cfg->line_outs; i++) { - if (!spec->multiout.dac_nids[i]) + nid = spec->multiout.dac_nids[i]; + if (!nid) + continue; + mix = alc662_dac_to_mix(codec, cfg->line_out_pins[i], nid); + if (!mix) continue; - nid = alc880_idx_to_dac(i); if (i == 2) { /* Center/LFE */ - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid, 1, 0, - HDA_OUTPUT)); + err = alc662_add_vol_ctl(spec, "Center", nid, 1); if (err < 0) return err; - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid, 2, 0, - HDA_OUTPUT)); + err = alc662_add_vol_ctl(spec, "LFE", nid, 2); if (err < 0) return err; - err = add_control(spec, ALC_CTL_WIDGET_MUTE, - "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(0x0e, 1, 0, - HDA_INPUT)); + err = alc662_add_sw_ctl(spec, "Center", mix, 1); if (err < 0) return err; - err = add_control(spec, ALC_CTL_WIDGET_MUTE, - "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, - HDA_INPUT)); + err = alc662_add_sw_ctl(spec, "LFE", mix, 2); if (err < 0) return err; } else { - sprintf(name, "%s Playback Volume", chname[i]); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - HDA_OUTPUT)); + const char *pfx; + if (cfg->line_outs == 1 && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + if (cfg->hp_outs) + pfx = "Speaker"; + else + pfx = "PCM"; + } else + pfx = chname[i]; + err = alc662_add_vol_ctl(spec, pfx, nid, 3); if (err < 0) return err; - sprintf(name, "%s Playback Switch", chname[i]); - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(alc880_idx_to_mixer(i), - 3, 0, HDA_INPUT)); + if (cfg->line_outs == 1 && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + pfx = "Speaker"; + err = alc662_add_sw_ctl(spec, pfx, mix, 3); if (err < 0) return err; } @@ -17227,139 +17338,75 @@ static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec, } /* add playback controls for speaker and HP outputs */ -static int alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, +/* return DAC nid if any new DAC is assigned */ +static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, const char *pfx) { - hda_nid_t nid; + struct alc_spec *spec = codec->spec; + hda_nid_t nid, mix; int err; - char name[32]; if (!pin) return 0; - - if (pin == 0x17) { - /* ALC663 has a mono output pin on 0x17 */ + nid = alc662_look_for_dac(codec, pin); + if (!nid) { + char name[32]; + /* the corresponding DAC is already occupied */ + if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) + return 0; /* no way */ + /* create a switch only */ sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(pin, 2, 0, HDA_OUTPUT)); - return err; + return add_control(spec, ALC_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); } - if (alc880_is_fixed_pin(pin)) { - nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); - /* printk(KERN_DEBUG "DAC nid=%x\n",nid); */ - /* specify the DAC as the extra output */ - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = nid; - else - spec->multiout.extra_out_nid[0] = nid; - /* control HP volume/switch on the output mixer amp */ - nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_BIND_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); - if (err < 0) - return err; - } else if (alc880_is_multi_pin(pin)) { - /* set manual connection */ - /* we have only a switch on HP-out PIN */ - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; - } - return 0; -} - -/* return the index of the src widget from the connection list of the nid. - * return -1 if not found - */ -static int alc662_input_pin_idx(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t src) -{ - hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; - int i, conns; - - conns = snd_hda_get_connections(codec, nid, conn_list, - ARRAY_SIZE(conn_list)); - if (conns < 0) - return -1; - for (i = 0; i < conns; i++) - if (conn_list[i] == src) - return i; - return -1; -} - -static int alc662_is_input_pin(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int pincap = snd_hda_query_pin_caps(codec, nid); - return (pincap & AC_PINCAP_IN) != 0; + mix = alc662_dac_to_mix(codec, pin, nid); + if (!mix) + return 0; + err = alc662_add_vol_ctl(spec, pfx, nid, 3); + if (err < 0) + return err; + err = alc662_add_sw_ctl(spec, pfx, mix, 3); + if (err < 0) + return err; + return nid; } /* create playback/capture controls for input pins */ -static int alc662_auto_create_analog_input_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, err, idx; - - for (i = 0; i < AUTO_PIN_LAST; i++) { - if (alc662_is_input_pin(codec, cfg->input_pins[i])) { - idx = alc662_input_pin_idx(codec, 0x0b, - cfg->input_pins[i]); - if (idx >= 0) { - err = new_analog_input(spec, cfg->input_pins[i], - auto_pin_cfg_labels[i], - idx, 0x0b); - if (err < 0) - return err; - } - idx = alc662_input_pin_idx(codec, 0x22, - cfg->input_pins[i]); - if (idx >= 0) { - imux->items[imux->num_items].label = - auto_pin_cfg_labels[i]; - imux->items[imux->num_items].index = idx; - imux->num_items++; - } - } - } - return 0; -} +#define alc662_auto_create_input_ctls \ + alc880_auto_create_input_ctls static void alc662_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, - int dac_idx) + hda_nid_t dac) { + int i, num; + hda_nid_t srcs[4]; + alc_set_pin_output(codec, nid, pin_type); /* need the manual connection? */ - if (alc880_is_multi_pin(nid)) { - struct alc_spec *spec = codec->spec; - int idx = alc880_multi_pin_idx(nid); - snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0, - AC_VERB_SET_CONNECT_SEL, - alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx])); + num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs)); + if (num <= 1) + return; + for (i = 0; i < num; i++) { + if (alc662_mix_to_dac(srcs[i]) != dac) + continue; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i); + return; } } static void alc662_auto_init_multi_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + int pin_type = get_pin_type(spec->autocfg.line_out_type); int i; 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); if (nid) alc662_auto_set_output_and_unmute(codec, nid, pin_type, - i); + spec->multiout.dac_nids[i]); } } @@ -17369,12 +17416,13 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec) hda_nid_t pin; pin = spec->autocfg.hp_pins[0]; - if (pin) /* connect to front */ - /* use dac 0 */ - alc662_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + if (pin) + alc662_auto_set_output_and_unmute(codec, pin, PIN_HP, + spec->multiout.hp_nid); pin = spec->autocfg.speaker_pins[0]; if (pin) - alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); + alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT, + spec->multiout.extra_out_nid[0]); } #define ALC662_PIN_CD_NID ALC880_PIN_CD_NID @@ -17386,7 +17434,7 @@ static void alc662_auto_init_analog_input(struct hda_codec *codec) for (i = 0; i < AUTO_PIN_LAST; i++) { hda_nid_t nid = spec->autocfg.input_pins[i]; - if (alc662_is_input_pin(codec, nid)) { + if (alc_is_input_pin(codec, nid)) { alc_set_input_pin(codec, nid, i); if (nid != ALC662_PIN_CD_NID && (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) @@ -17412,22 +17460,26 @@ static int alc662_parse_auto_config(struct hda_codec *codec) if (!spec->autocfg.line_outs) return 0; /* can't find valid BIOS pin config */ - err = alc880_auto_fill_dac_nids(spec, &spec->autocfg); + err = alc662_auto_fill_dac_nids(codec, &spec->autocfg); if (err < 0) return err; - err = alc662_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = alc662_auto_create_multi_out_ctls(codec, &spec->autocfg); if (err < 0) return err; - err = alc662_auto_create_extra_out(spec, + err = alc662_auto_create_extra_out(codec, spec->autocfg.speaker_pins[0], "Speaker"); if (err < 0) return err; - err = alc662_auto_create_extra_out(spec, spec->autocfg.hp_pins[0], + if (err) + spec->multiout.extra_out_nid[0] = err; + err = alc662_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], "Headphone"); if (err < 0) return err; - err = alc662_auto_create_analog_input_ctls(codec, &spec->autocfg); + if (err) + spec->multiout.hp_nid = err; + err = alc662_auto_create_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -17484,8 +17536,8 @@ static int patch_alc662(struct hda_codec *codec) alc662_models, alc662_cfg_tbl); if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for %s, " - "trying auto-probe from BIOS...\n", codec->chip_name); + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); board_config = ALC662_AUTO; } @@ -17510,7 +17562,7 @@ static int patch_alc662(struct hda_codec *codec) } if (board_config != ALC662_AUTO) - setup_preset(spec, &alc662_presets[board_config]); + setup_preset(codec, &alc662_presets[board_config]); spec->stream_analog_playback = &alc662_pcm_analog_playback; spec->stream_analog_capture = &alc662_pcm_analog_capture; @@ -17526,7 +17578,7 @@ static int patch_alc662(struct hda_codec *codec) spec->capsrc_nids = alc662_capsrc_nids; if (!spec->cap_mixer) - set_capture_mixer(spec); + set_capture_mixer(codec); if (codec->vendor_id == 0x10ec0662) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); else @@ -17562,23 +17614,23 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 }, { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd }, { .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2", - .patch = patch_alc883 }, + .patch = patch_alc882 }, { .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1", .patch = patch_alc662 }, { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 }, { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, - { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 }, + { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 }, { .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A", - .patch = patch_alc882 }, /* should be patch_alc883() in future */ + .patch = patch_alc882 }, { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A", - .patch = patch_alc882 }, /* should be patch_alc883() in future */ + .patch = patch_alc882 }, { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, - { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc883 }, + { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 }, { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200", - .patch = patch_alc883 }, - { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, - { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 }, + .patch = patch_alc882 }, + { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 }, + { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 6990cfcb6a3..66c0876bf73 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -40,6 +40,8 @@ enum { STAC_INSERT_EVENT, STAC_PWR_EVENT, STAC_HP_EVENT, + STAC_LO_EVENT, + STAC_MIC_EVENT, }; enum { @@ -81,6 +83,7 @@ enum { STAC_DELL_M6_DMIC, STAC_DELL_M6_BOTH, STAC_DELL_EQ, + STAC_ALIENWARE_M17X, STAC_92HD73XX_MODELS }; @@ -155,6 +158,7 @@ enum { STAC_D965_5ST_NO_FP, STAC_DELL_3ST, STAC_DELL_BIOS, + STAC_927X_VOLKNOB, STAC_927X_MODELS }; @@ -177,6 +181,12 @@ struct sigmatel_jack { struct snd_jack *jack; }; +struct sigmatel_mic_route { + hda_nid_t pin; + signed char mux_idx; + signed char dmux_idx; +}; + struct sigmatel_spec { struct snd_kcontrol_new *mixers[4]; unsigned int num_mixers; @@ -188,6 +198,7 @@ struct sigmatel_spec { unsigned int hp_detect: 1; unsigned int spdif_mute: 1; unsigned int check_volume_offset:1; + unsigned int auto_mic:1; /* gpio lines */ unsigned int eapd_mask; @@ -219,7 +230,6 @@ struct sigmatel_spec { /* playback */ struct hda_input_mux *mono_mux; - struct hda_input_mux *amp_mux; unsigned int cur_mmux; struct hda_multi_out multiout; hda_nid_t dac_nids[5]; @@ -239,6 +249,15 @@ struct sigmatel_spec { unsigned int num_dmuxes; hda_nid_t *smux_nids; unsigned int num_smuxes; + unsigned int num_analog_muxes; + + unsigned long *capvols; /* amp-volume attr: HDA_COMPOSE_AMP_VAL() */ + unsigned long *capsws; /* amp-mute attr: HDA_COMPOSE_AMP_VAL() */ + unsigned int num_caps; /* number of capture volume/switch elements */ + + struct sigmatel_mic_route ext_mic; + struct sigmatel_mic_route int_mic; + const char **spdif_labels; hda_nid_t dig_in_nid; @@ -263,7 +282,6 @@ struct sigmatel_spec { unsigned int cur_smux[2]; unsigned int cur_amux; hda_nid_t *amp_nids; - unsigned int num_amps; unsigned int powerdown_adcs; /* i/o switches */ @@ -282,7 +300,6 @@ struct sigmatel_spec { struct hda_input_mux private_dimux; struct hda_input_mux private_imux; struct hda_input_mux private_smux; - struct hda_input_mux private_amp_mux; struct hda_input_mux private_mono_mux; }; @@ -311,11 +328,6 @@ static hda_nid_t stac92hd73xx_adc_nids[2] = { 0x1a, 0x1b }; -#define DELL_M6_AMP 2 -static hda_nid_t stac92hd73xx_amp_nids[3] = { - 0x0b, 0x0c, 0x0e -}; - #define STAC92HD73XX_NUM_DMICS 2 static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = { 0x13, 0x14, 0 @@ -323,8 +335,8 @@ static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = { #define STAC92HD73_DAC_COUNT 5 -static hda_nid_t stac92hd73xx_mux_nids[4] = { - 0x28, 0x29, 0x2a, 0x2b, +static hda_nid_t stac92hd73xx_mux_nids[2] = { + 0x20, 0x21, }; static hda_nid_t stac92hd73xx_dmux_nids[2] = { @@ -335,14 +347,16 @@ static hda_nid_t stac92hd73xx_smux_nids[2] = { 0x22, 0x23, }; -#define STAC92HD83XXX_NUM_DMICS 2 -static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = { - 0x11, 0x12, 0 +#define STAC92HD73XX_NUM_CAPS 2 +static unsigned long stac92hd73xx_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x20, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), }; +#define stac92hd73xx_capsws stac92hd73xx_capvols #define STAC92HD83_DAC_COUNT 3 -static hda_nid_t stac92hd83xxx_dmux_nids[2] = { +static hda_nid_t stac92hd83xxx_mux_nids[2] = { 0x17, 0x18, }; @@ -362,9 +376,12 @@ static unsigned int stac92hd83xxx_pwr_mapping[4] = { 0x03, 0x0c, 0x20, 0x40, }; -static hda_nid_t stac92hd83xxx_amp_nids[1] = { - 0xc, +#define STAC92HD83XXX_NUM_CAPS 2 +static unsigned long stac92hd83xxx_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_OUTPUT), }; +#define stac92hd83xxx_capsws stac92hd83xxx_capvols static hda_nid_t stac92hd71bxx_pwr_nids[3] = { 0x0a, 0x0d, 0x0f @@ -395,6 +412,13 @@ static hda_nid_t stac92hd71bxx_slave_dig_outs[2] = { 0x22, 0 }; +#define STAC92HD71BXX_NUM_CAPS 2 +static unsigned long stac92hd71bxx_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), +}; +#define stac92hd71bxx_capsws stac92hd71bxx_capvols + static hda_nid_t stac925x_adc_nids[1] = { 0x03, }; @@ -416,6 +440,13 @@ static hda_nid_t stac925x_dmux_nids[1] = { 0x14, }; +static unsigned long stac925x_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT), +}; +static unsigned long stac925x_capsws[] = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), +}; + static hda_nid_t stac922x_adc_nids[2] = { 0x06, 0x07, }; @@ -424,6 +455,13 @@ static hda_nid_t stac922x_mux_nids[2] = { 0x12, 0x13, }; +#define STAC922X_NUM_CAPS 2 +static unsigned long stac922x_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT), +}; +#define stac922x_capsws stac922x_capvols + static hda_nid_t stac927x_slave_dig_outs[2] = { 0x1f, 0, }; @@ -453,6 +491,18 @@ static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = { 0x13, 0x14, 0 }; +#define STAC927X_NUM_CAPS 3 +static unsigned long stac927x_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x19, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_INPUT), +}; +static unsigned long stac927x_capsws[] = { + HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), +}; + static const char *stac927x_spdif_labels[5] = { "Digital Playback", "ADAT", "Analog Mux 1", "Analog Mux 2", "Analog Mux 3" @@ -479,6 +529,16 @@ static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = { 0x17, 0x18, 0 }; +#define STAC9205_NUM_CAPS 2 +static unsigned long stac9205_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_INPUT), +}; +static unsigned long stac9205_capsws[] = { + HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1e, 3, 0, HDA_OUTPUT), +}; + static hda_nid_t stac9200_pin_nids[8] = { 0x08, 0x09, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, @@ -529,34 +589,6 @@ static hda_nid_t stac9205_pin_nids[12] = { 0x21, 0x22, }; -#define stac92xx_amp_volume_info snd_hda_mixer_amp_volume_info - -static int stac92xx_amp_volume_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; - hda_nid_t nid = spec->amp_nids[spec->cur_amux]; - - kcontrol->private_value ^= get_amp_nid(kcontrol); - kcontrol->private_value |= nid; - - return snd_hda_mixer_amp_volume_get(kcontrol, ucontrol); -} - -static int stac92xx_amp_volume_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; - hda_nid_t nid = spec->amp_nids[spec->cur_amux]; - - kcontrol->private_value ^= get_amp_nid(kcontrol); - kcontrol->private_value |= nid; - - return snd_hda_mixer_amp_volume_put(kcontrol, ucontrol); -} - static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -693,9 +725,35 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, - spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]); + const struct hda_input_mux *imux = spec->input_mux; + unsigned int idx, prev_idx; + + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + prev_idx = spec->cur_mux[adc_idx]; + if (prev_idx == idx) + return 0; + if (idx < spec->num_analog_muxes) { + snd_hda_codec_write_cache(codec, spec->mux_nids[adc_idx], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[idx].index); + if (prev_idx >= spec->num_analog_muxes) { + imux = spec->dinput_mux; + /* 0 = analog */ + snd_hda_codec_write_cache(codec, + spec->dmux_nids[adc_idx], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[0].index); + } + } else { + imux = spec->dinput_mux; + snd_hda_codec_write_cache(codec, spec->dmux_nids[adc_idx], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[idx - 1].index); + } + spec->cur_mux[adc_idx] = idx; + return 1; } static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol, @@ -726,41 +784,6 @@ static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol, spec->mono_nid, &spec->cur_mmux); } -static int stac92xx_amp_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->amp_mux, uinfo); -} - -static int stac92xx_amp_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_amux; - return 0; -} - -static int stac92xx_amp_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; - struct snd_kcontrol *ctl = - snd_hda_find_mixer_ctl(codec, "Amp Capture Volume"); - if (!ctl) - return -EINVAL; - - snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE | - SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); - - return snd_hda_input_mux_put(codec, spec->amp_mux, ucontrol, - 0, &spec->cur_amux); -} - #define stac92xx_aloopback_info snd_ctl_boolean_mono_info static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol, @@ -828,92 +851,20 @@ 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 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 dell_eq_core_init[] = { /* set master volume to max value without distortion * and direct control */ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec}, - /* setup adcs to point to mixer */ - { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, - { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, - /* 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 dell_m6_core_init[] = { - { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* setup adcs to point to mixer */ - { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, - { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, - /* 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 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[] = { +static struct hda_verb stac92hd73xx_core_init[] = { /* set master volume and direct control */ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* dac3 is connected to import3 mux */ - { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f}, - /* 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 stac92hd83xxx_core_init[] = { - { 0xa, AC_VERB_SET_CONNECT_SEL, 0x1}, - { 0xb, AC_VERB_SET_CONNECT_SEL, 0x1}, - { 0xd, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* power state controls amps */ { 0x01, AC_VERB_SET_EAPD, 1 << 2}, {} @@ -925,19 +876,6 @@ static struct hda_verb stac92hd71bxx_core_init[] = { {} }; -#define HD_DISABLE_PORTF 1 -static struct hda_verb stac92hd71bxx_analog_core_init[] = { - /* start of config #1 */ - - /* connect port 0f to audio mixer */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2}, - /* start of config #2 */ - - /* set master volume and direct control */ - { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - {} -}; - static struct hda_verb stac92hd71bxx_unmute_core_init[] = { /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */ { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, @@ -970,6 +908,16 @@ static struct hda_verb d965_core_init[] = { {} }; +static struct hda_verb dell_3st_core_init[] = { + /* don't set delta bit */ + {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, + /* unmute node 0x1b */ + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* select node 0x03 as DAC */ + {0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, + {} +}; + static struct hda_verb stac927x_core_init[] = { /* set master volume and direct control */ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, @@ -978,6 +926,14 @@ static struct hda_verb stac927x_core_init[] = { {} }; +static struct hda_verb stac927x_volknob_core_init[] = { + /* don't set delta bit */ + {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, + /* enable analog pc beep path */ + {0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, + {} +}; + static struct hda_verb stac9205_core_init[] = { /* set master volume and direct control */ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, @@ -996,31 +952,6 @@ static struct hda_verb stac9205_core_init[] = { .put = stac92xx_mono_mux_enum_put, \ } -#define STAC_AMP_MUX \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = "Amp Selector Capture Switch", \ - .count = 1, \ - .info = stac92xx_amp_mux_enum_info, \ - .get = stac92xx_amp_mux_enum_get, \ - .put = stac92xx_amp_mux_enum_put, \ - } - -#define STAC_AMP_VOL(xname, nid, chs, idx, dir) \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = 0, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ - SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ - .info = stac92xx_amp_volume_info, \ - .get = stac92xx_amp_volume_get, \ - .put = stac92xx_amp_volume_put, \ - .tlv = { .c = snd_hda_mixer_amp_tlv }, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, chs, idx, dir) \ - } - #define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ @@ -1051,34 +982,6 @@ static struct snd_kcontrol_new stac9200_mixer[] = { { } /* end */ }; -#define DELL_M6_MIXER 6 -static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = { - /* start of config #1 */ - 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("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), - - /* start of config #2 */ - 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("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), - - 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), - - { } /* end */ -}; - static struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = { STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3), {} @@ -1094,134 +997,14 @@ static struct snd_kcontrol_new stac92hd73xx_10ch_loopback[] = { {} }; -static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = { - 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[] = { - 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 stac92hd83xxx_mixer[] = { - HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0x3, HDA_INPUT), - - HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x4, HDA_INPUT), - - HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x0, HDA_INPUT), - - HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x2, HDA_INPUT), - - /* - HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x1, HDA_INPUT), - */ - { } /* end */ -}; - -static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = { - 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 Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT), - /* analog pc-beep replaced with digital beep support */ - /* - HDA_CODEC_VOLUME("PC Beep Volume", 0x17, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("PC Beep Switch", 0x17, 0x2, HDA_INPUT), - */ - - HDA_CODEC_MUTE("Import0 Mux Capture Switch", 0x17, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Import0 Mux Capture Volume", 0x17, 0x0, HDA_INPUT), - - HDA_CODEC_MUTE("Import1 Mux Capture Switch", 0x17, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Import1 Mux Capture Volume", 0x17, 0x1, HDA_INPUT), - - HDA_CODEC_MUTE("DAC0 Capture Switch", 0x17, 0x3, HDA_INPUT), - HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x17, 0x3, HDA_INPUT), - - HDA_CODEC_MUTE("DAC1 Capture Switch", 0x17, 0x4, HDA_INPUT), - HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x17, 0x4, HDA_INPUT), - { } /* end */ -}; static struct snd_kcontrol_new stac92hd71bxx_loopback[] = { STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2) }; -static struct snd_kcontrol_new stac92hd71bxx_mixer[] = { - 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 Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT), - { } /* end */ -}; - static struct snd_kcontrol_new stac925x_mixer[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x14, 0, HDA_OUTPUT), - { } /* end */ -}; - -static struct snd_kcontrol_new stac9205_mixer[] = { - HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1c, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1e, 0x0, HDA_OUTPUT), { } /* end */ }; @@ -1230,29 +1013,6 @@ static struct snd_kcontrol_new stac9205_loopback[] = { {} }; -/* This needs to be generated dynamically based on sequence */ -static struct snd_kcontrol_new stac922x_mixer[] = { - HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_INPUT), - - HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_INPUT), - { } /* end */ -}; - - -static struct snd_kcontrol_new stac927x_mixer[] = { - HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x19, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1c, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME_IDX("Capture Volume", 0x2, 0x1A, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 0x2, 0x1d, 0x0, HDA_OUTPUT), - { } /* end */ -}; - static struct snd_kcontrol_new stac927x_loopback[] = { STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1), {} @@ -1310,16 +1070,19 @@ static int stac92xx_build_controls(struct hda_codec *codec) int err; int i; - err = snd_hda_add_new_ctls(codec, spec->mixer); - if (err < 0) - return err; + if (spec->mixer) { + err = snd_hda_add_new_ctls(codec, spec->mixer); + if (err < 0) + return err; + } for (i = 0; i < spec->num_mixers; i++) { err = snd_hda_add_new_ctls(codec, spec->mixers[i]); if (err < 0) return err; } - if (spec->num_dmuxes > 0) { + if (!spec->auto_mic && spec->num_dmuxes > 0 && + snd_hda_get_bool_hint(codec, "separate_dmux") == 1) { stac_dmux_mixer.count = spec->num_dmuxes; err = snd_hda_ctl_add(codec, snd_ctl_new1(&stac_dmux_mixer, codec)); @@ -1766,12 +1529,20 @@ static unsigned int dell_m6_pin_configs[13] = { 0x4f0000f0, }; +static unsigned int alienware_m17x_pin_configs[13] = { + 0x0321101f, 0x0321101f, 0x03a11020, 0x03014020, + 0x90170110, 0x4f0000f0, 0x4f0000f0, 0x4f0000f0, + 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0, + 0x904601b0, +}; + static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs, [STAC_DELL_M6_AMIC] = dell_m6_pin_configs, [STAC_DELL_M6_DMIC] = dell_m6_pin_configs, [STAC_DELL_M6_BOTH] = dell_m6_pin_configs, [STAC_DELL_EQ] = dell_m6_pin_configs, + [STAC_ALIENWARE_M17X] = alienware_m17x_pin_configs, }; static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = { @@ -1783,6 +1554,7 @@ static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = { [STAC_DELL_M6_DMIC] = "dell-m6-dmic", [STAC_DELL_M6_BOTH] = "dell-m6", [STAC_DELL_EQ] = "dell-eq", + [STAC_ALIENWARE_M17X] = "alienware", }; static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { @@ -1820,6 +1592,12 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { {} /* terminator */ }; +static struct snd_pci_quirk stac92hd73xx_codec_id_cfg_tbl[] = { + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a1, + "Alienware M17x", STAC_ALIENWARE_M17X), + {} /* terminator */ +}; + static unsigned int ref92hd83xxx_pin_configs[10] = { 0x02214030, 0x02211010, 0x02a19020, 0x02170130, 0x01014050, 0x01819040, 0x01014020, 0x90a3014e, @@ -1827,8 +1605,8 @@ static unsigned int ref92hd83xxx_pin_configs[10] = { }; static unsigned int dell_s14_pin_configs[10] = { - 0x02214030, 0x02211010, 0x02a19020, 0x01014050, - 0x40f000f0, 0x01819040, 0x40f000f0, 0x90a60160, + 0x0221403f, 0x0221101f, 0x02a19020, 0x90170110, + 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a60160, 0x40f000f0, 0x40f000f0, }; @@ -1927,6 +1705,10 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { "HP mini 1000", STAC_HP_M4), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b, "HP HDX", STAC_HP_HDX), /* HDX16 */ + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3620, + "HP dv6", STAC_HP_DV5), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010, + "HP", STAC_HP_DV5), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233, "unknown Dell", STAC_DELL_M4_1), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234, @@ -2236,6 +2018,7 @@ static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { [STAC_D965_5ST_NO_FP] = d965_5st_no_fp_pin_configs, [STAC_DELL_3ST] = dell_3st_pin_configs, [STAC_DELL_BIOS] = NULL, + [STAC_927X_VOLKNOB] = NULL, }; static const char *stac927x_models[STAC_927X_MODELS] = { @@ -2247,6 +2030,7 @@ static const char *stac927x_models[STAC_927X_MODELS] = { [STAC_D965_5ST_NO_FP] = "5stack-no-fp", [STAC_DELL_3ST] = "dell-3stack", [STAC_DELL_BIOS] = "dell-bios", + [STAC_927X_VOLKNOB] = "volknob", }; static struct snd_pci_quirk stac927x_cfg_tbl[] = { @@ -2282,6 +2066,8 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = { "Intel D965", STAC_D965_5ST), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2500, "Intel D965", STAC_D965_5ST), + /* volume-knob fixes */ + SND_PCI_QUIRK_VENDOR(0x10cf, "FSC", STAC_927X_VOLKNOB), {} /* terminator */ }; @@ -2642,8 +2428,7 @@ static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol, return 0; } -static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid, - unsigned char type); +static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid); static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2657,7 +2442,7 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol, /* check to be sure that the ports are upto date with * switch changes */ - stac_issue_unsol_event(codec, nid, STAC_HP_EVENT); + stac_issue_unsol_event(codec, nid); return 1; } @@ -2790,7 +2575,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ * appropriately according to the pin direction */ if (spec->hp_detect) - stac_issue_unsol_event(codec, nid, STAC_HP_EVENT); + stac_issue_unsol_event(codec, nid); return 1; } @@ -2859,8 +2644,6 @@ enum { STAC_CTL_WIDGET_VOL, STAC_CTL_WIDGET_MUTE, STAC_CTL_WIDGET_MONO_MUX, - STAC_CTL_WIDGET_AMP_MUX, - STAC_CTL_WIDGET_AMP_VOL, STAC_CTL_WIDGET_HP_SWITCH, STAC_CTL_WIDGET_IO_SWITCH, STAC_CTL_WIDGET_CLFE_SWITCH, @@ -2871,8 +2654,6 @@ 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_AMP_MUX, - STAC_AMP_VOL(NULL, 0, 0, 0, 0), STAC_CODEC_HP_SWITCH(NULL), STAC_CODEC_IO_SWITCH(NULL, 0), STAC_CODEC_CLFE_SWITCH(NULL, 0), @@ -2973,6 +2754,8 @@ static int stac92xx_add_input_source(struct sigmatel_spec *spec) struct snd_kcontrol_new *knew; struct hda_input_mux *imux = &spec->private_imux; + if (spec->auto_mic) + return 0; /* no need for input source */ if (!spec->num_adcs || imux->num_items <= 1) return 0; /* no need for input source control */ knew = stac_control_new(spec, &stac_input_src_temp, @@ -3066,7 +2849,7 @@ static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid) HDA_MAX_CONNECTIONS); for (j = 0; j < conn_len; j++) { wcaps = get_wcaps(codec, conn[j]); - wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + wtype = get_wcaps_type(wcaps); /* we check only analog outputs */ if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL)) continue; @@ -3325,6 +3108,21 @@ static int create_multi_out_ctls(struct hda_codec *codec, int num_outs, return 0; } +static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol, + unsigned long sw, int idx) +{ + int err; + err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, + "Capture Volume", vol); + if (err < 0) + return err; + err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_MUTE, idx, + "Capture Switch", sw); + if (err < 0) + return err; + return 0; +} + /* add playback controls from the parsed DAC table */ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) @@ -3398,7 +3196,7 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec) spec->mono_nid, con_lst, HDA_MAX_NUM_INPUTS); - if (!num_cons || num_cons > ARRAY_SIZE(stac92xx_mono_labels)) + if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels)) return -EINVAL; for (i = 0; i < num_cons; i++) { @@ -3412,37 +3210,6 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec) "Mono Mux", spec->mono_nid); } -/* labels for amp mux outputs */ -static const char *stac92xx_amp_labels[3] = { - "Front Microphone", "Microphone", "Line In", -}; - -/* create amp out controls mux on capable codecs */ -static int stac92xx_auto_create_amp_output_ctls(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct hda_input_mux *amp_mux = &spec->private_amp_mux; - int i, err; - - for (i = 0; i < spec->num_amps; i++) { - amp_mux->items[amp_mux->num_items].label = - stac92xx_amp_labels[i]; - amp_mux->items[amp_mux->num_items].index = i; - amp_mux->num_items++; - } - - if (spec->num_amps > 1) { - err = stac92xx_add_control(spec, STAC_CTL_WIDGET_AMP_MUX, - "Amp Selector Capture Switch", 0); - if (err < 0) - return err; - } - return stac92xx_add_control(spec, STAC_CTL_WIDGET_AMP_VOL, - "Amp Capture Volume", - HDA_COMPOSE_AMP_VAL(spec->amp_nids[0], 3, 0, HDA_INPUT)); -} - - /* create PC beep volume controls */ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec, hda_nid_t nid) @@ -3511,19 +3278,33 @@ static int stac92xx_beep_switch_ctl(struct hda_codec *codec) static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - int wcaps, nid, i, err = 0; + int i, j, err = 0; for (i = 0; i < spec->num_muxes; i++) { + hda_nid_t nid; + unsigned int wcaps; + unsigned long val; + nid = spec->mux_nids[i]; wcaps = get_wcaps(codec, nid); + if (!(wcaps & AC_WCAP_OUT_AMP)) + continue; - if (wcaps & AC_WCAP_OUT_AMP) { - err = stac92xx_add_control_idx(spec, - STAC_CTL_WIDGET_VOL, i, "Mux Capture Volume", - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); - if (err < 0) - return err; + /* check whether already the same control was created as + * normal Capture Volume. + */ + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + for (j = 0; j < spec->num_caps; j++) { + if (spec->capvols[j] == val) + break; } + if (j < spec->num_caps) + continue; + + err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, i, + "Mux Capture Volume", val); + if (err < 0) + return err; } return 0; }; @@ -3544,7 +3325,7 @@ static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec) spec->smux_nids[0], con_lst, HDA_MAX_NUM_INPUTS); - if (!num_cons) + if (num_cons <= 0) return -EINVAL; if (!labels) @@ -3565,101 +3346,239 @@ static const char *stac92xx_dmic_labels[5] = { "Digital Mic 3", "Digital Mic 4" }; +static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t nid) +{ + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + int i, nums; + + nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) + if (conn[i] == nid) + return i; + return -1; +} + +/* create a volume assigned to the given pin (only if supported) */ +/* return 1 if the volume control is created */ +static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid, + const char *label, int direction) +{ + unsigned int caps, nums; + char name[32]; + int err; + + if (direction == HDA_OUTPUT) + caps = AC_WCAP_OUT_AMP; + else + caps = AC_WCAP_IN_AMP; + if (!(get_wcaps(codec, nid) & caps)) + return 0; + caps = query_amp_caps(codec, nid, direction); + nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + if (!nums) + return 0; + snprintf(name, sizeof(name), "%s Capture Volume", label); + err = stac92xx_add_control(codec->spec, STAC_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction)); + if (err < 0) + return err; + return 1; +} + /* create playback/capture controls for input pins on dmic capable codecs */ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { struct sigmatel_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux; struct hda_input_mux *dimux = &spec->private_dimux; - hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; - int err, i, j; - char name[32]; + int err, i, active_mics; + unsigned int def_conf; dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0]; dimux->items[dimux->num_items].index = 0; dimux->num_items++; + active_mics = 0; + for (i = 0; i < spec->num_dmics; i++) { + /* check the validity: sometimes it's a dead vendor-spec node */ + if (get_wcaps_type(get_wcaps(codec, spec->dmic_nids[i])) + != AC_WID_PIN) + continue; + def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]); + if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) + active_mics++; + } + for (i = 0; i < spec->num_dmics; i++) { hda_nid_t nid; int index; - int num_cons; - unsigned int wcaps; - unsigned int def_conf; + const char *label; - def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]); + nid = spec->dmic_nids[i]; + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + continue; + def_conf = snd_hda_codec_get_pincfg(codec, nid); 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_nids[0], - con_lst, - HDA_MAX_NUM_INPUTS); - for (j = 0; j < num_cons; j++) - if (con_lst[j] == nid) { - index = j; - goto found; - } - continue; -found: - wcaps = get_wcaps(codec, nid) & - (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP); + index = get_connection_index(codec, spec->dmux_nids[0], nid); + if (index < 0) + continue; - if (wcaps) { - sprintf(name, "%s Capture Volume", - stac92xx_dmic_labels[dimux->num_items]); + if (active_mics == 1) + label = "Digital Mic"; + else + label = stac92xx_dmic_labels[dimux->num_items]; - err = stac92xx_add_control(spec, - STAC_CTL_WIDGET_VOL, - name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, - (wcaps & AC_WCAP_OUT_AMP) ? - HDA_OUTPUT : HDA_INPUT)); + err = create_elem_capture_vol(codec, nid, label, HDA_INPUT); + if (err < 0) + return err; + if (!err) { + err = create_elem_capture_vol(codec, nid, label, + HDA_OUTPUT); if (err < 0) return err; } - dimux->items[dimux->num_items].label = - stac92xx_dmic_labels[dimux->num_items]; + dimux->items[dimux->num_items].label = label; dimux->items[dimux->num_items].index = index; dimux->num_items++; + if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) { + imux->items[imux->num_items].label = label; + imux->items[imux->num_items].index = index; + imux->num_items++; + } + } + + return 0; +} + +static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t *fixed, hda_nid_t *ext) +{ + unsigned int cfg; + + if (!nid) + return 0; + cfg = snd_hda_codec_get_pincfg(codec, nid); + switch (get_defcfg_connect(cfg)) { + case AC_JACK_PORT_FIXED: + if (*fixed) + return 1; /* already occupied */ + *fixed = nid; + break; + case AC_JACK_PORT_COMPLEX: + if (*ext) + return 1; /* already occupied */ + *ext = nid; + break; } + return 0; +} + +static int set_mic_route(struct hda_codec *codec, + struct sigmatel_mic_route *mic, + hda_nid_t pin) +{ + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + mic->pin = pin; + for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) + if (pin == cfg->input_pins[i]) + break; + if (i <= AUTO_PIN_FRONT_MIC) { + /* analog pin */ + i = get_connection_index(codec, spec->mux_nids[0], pin); + if (i < 0) + return -1; + mic->mux_idx = i; + mic->dmux_idx = -1; + if (spec->dmux_nids) + mic->dmux_idx = get_connection_index(codec, + spec->dmux_nids[0], + spec->mux_nids[0]); + } else if (spec->dmux_nids) { + /* digital pin */ + i = get_connection_index(codec, spec->dmux_nids[0], pin); + if (i < 0) + return -1; + mic->dmux_idx = i; + mic->mux_idx = -1; + if (spec->mux_nids) + mic->mux_idx = get_connection_index(codec, + spec->mux_nids[0], + spec->dmux_nids[0]); + } return 0; } +/* return non-zero if the device is for automatic mic switch */ +static int stac_check_auto_mic(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t fixed, ext; + int i; + + for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) { + if (cfg->input_pins[i]) + return 0; /* must be exclusively mics */ + } + fixed = ext = 0; + for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) + if (check_mic_pin(codec, cfg->input_pins[i], &fixed, &ext)) + return 0; + for (i = 0; i < spec->num_dmics; i++) + if (check_mic_pin(codec, spec->dmic_nids[i], &fixed, &ext)) + return 0; + if (!fixed || !ext) + return 0; + if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP)) + return 0; /* no unsol support */ + if (set_mic_route(codec, &spec->ext_mic, ext) || + set_mic_route(codec, &spec->int_mic, fixed)) + return 0; /* something is wrong */ + return 1; +} + /* create playback/capture controls for input pins */ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { struct sigmatel_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->private_imux; - hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; - int i, j, k; + int i, j; for (i = 0; i < AUTO_PIN_LAST; i++) { - int index; + hda_nid_t nid = cfg->input_pins[i]; + int index, err; - if (!cfg->input_pins[i]) + if (!nid) continue; index = -1; for (j = 0; j < spec->num_muxes; j++) { - int num_cons; - num_cons = snd_hda_get_connections(codec, - spec->mux_nids[j], - con_lst, - HDA_MAX_NUM_INPUTS); - for (k = 0; k < num_cons; k++) - if (con_lst[k] == cfg->input_pins[i]) { - index = k; - goto found; - } + index = get_connection_index(codec, spec->mux_nids[j], + nid); + if (index >= 0) + break; } - continue; - found: + if (index < 0) + continue; + + err = create_elem_capture_vol(codec, nid, + auto_pin_cfg_labels[i], + HDA_INPUT); + if (err < 0) + return err; + imux->items[imux->num_items].label = auto_pin_cfg_labels[i]; imux->items[imux->num_items].index = index; imux->num_items++; } + spec->num_analog_muxes = imux->num_items; if (imux->num_items) { /* @@ -3711,7 +3630,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out { struct sigmatel_spec *spec = codec->spec; int hp_swap = 0; - int err; + int i, err; if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, @@ -3751,11 +3670,10 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out 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)) { + conn_list, 1) > 0) { int wcaps = get_wcaps(codec, conn_list[0]); - int wid_type = (wcaps & AC_WCAP_TYPE) - >> AC_WCAP_TYPE_SHIFT; + int wid_type = get_wcaps_type(wcaps); /* LR swap check, some stac925x have a mux that * changes the DACs output path instead of the * mono-mux path. @@ -3846,6 +3764,21 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out spec->autocfg.line_outs = 0; } + if (stac_check_auto_mic(codec)) { + spec->auto_mic = 1; + /* only one capture for auto-mic */ + spec->num_adcs = 1; + spec->num_caps = 1; + spec->num_muxes = 1; + } + + for (i = 0; i < spec->num_caps; i++) { + err = stac92xx_add_capvol_ctls(codec, spec->capvols[i], + spec->capsws[i], i); + if (err < 0) + return err; + } + err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg); if (err < 0) return err; @@ -3855,11 +3788,6 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out if (err < 0) return err; } - if (spec->num_amps > 0) { - err = stac92xx_auto_create_amp_output_ctls(codec); - if (err < 0) - return err; - } if (spec->num_dmics > 0 && !spec->dinput_mux) if ((err = stac92xx_auto_create_dmic_input_ctls(codec, &spec->autocfg)) < 0) @@ -3896,7 +3824,6 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out spec->dinput_mux = &spec->private_dimux; spec->sinput_mux = &spec->private_smux; spec->mono_mux = &spec->private_mono_mux; - spec->amp_mux = &spec->private_amp_mux; return 1; } @@ -4108,14 +4035,14 @@ static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid, } static struct sigmatel_event *stac_get_event(struct hda_codec *codec, - hda_nid_t nid, unsigned char type) + hda_nid_t nid) { struct sigmatel_spec *spec = codec->spec; struct sigmatel_event *event = spec->events.list; int i; for (i = 0; i < spec->events.used; i++, event++) { - if (event->nid == nid && event->type == type) + if (event->nid == nid) return event; } return NULL; @@ -4135,24 +4062,32 @@ static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec, return NULL; } -static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, - unsigned int type) +/* check if given nid is a valid pin and no other events are assigned + * to it. If OK, assign the event, set the unsol flag, and returns 1. + * Otherwise, returns zero. + */ +static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, + unsigned int type) { struct sigmatel_event *event; int tag; if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)) - return; - event = stac_get_event(codec, nid, type); - if (event) + return 0; + event = stac_get_event(codec, nid); + if (event) { + if (event->type != type) + return 0; tag = event->tag; - else + } else { tag = stac_add_event(codec->spec, nid, type, 0); - if (tag < 0) - return; + if (tag < 0) + return 0; + } snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | tag); + return 1; } static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) @@ -4245,20 +4180,39 @@ static int stac92xx_init(struct hda_codec *codec) hda_nid_t nid = cfg->hp_pins[i]; enable_pin_detect(codec, nid, STAC_HP_EVENT); } + if (cfg->line_out_type == AUTO_PIN_LINE_OUT && + cfg->speaker_outs > 0) { + /* enable pin-detect for line-outs as well */ + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t nid = cfg->line_out_pins[i]; + enable_pin_detect(codec, nid, STAC_LO_EVENT); + } + } + /* force to enable the first line-out; the others are set up * in unsol_event */ stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0], AC_PINCTL_OUT_EN); /* fake event to set up pins */ - stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0], - STAC_HP_EVENT); + if (cfg->hp_pins[0]) + stac_issue_unsol_event(codec, cfg->hp_pins[0]); + else if (cfg->line_out_pins[0]) + stac_issue_unsol_event(codec, cfg->line_out_pins[0]); } else { stac92xx_auto_init_multi_out(codec); stac92xx_auto_init_hp_out(codec); for (i = 0; i < cfg->hp_outs; i++) stac_toggle_power_map(codec, cfg->hp_pins[i], 1); } + if (spec->auto_mic) { + /* initialize connection to analog input */ + if (spec->dmux_nids) + snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0, + AC_VERB_SET_CONNECT_SEL, 0); + if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT)) + stac_issue_unsol_event(codec, spec->ext_mic.pin); + } for (i = 0; i < AUTO_PIN_LAST; i++) { hda_nid_t nid = cfg->input_pins[i]; if (nid) { @@ -4285,10 +4239,9 @@ static int stac92xx_init(struct hda_codec *codec) } conf = snd_hda_codec_get_pincfg(codec, nid); if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) { - enable_pin_detect(codec, nid, - STAC_INSERT_EVENT); - stac_issue_unsol_event(codec, nid, - STAC_INSERT_EVENT); + if (enable_pin_detect(codec, nid, + STAC_INSERT_EVENT)) + stac_issue_unsol_event(codec, nid); } } } @@ -4333,10 +4286,8 @@ static int stac92xx_init(struct hda_codec *codec) stac_toggle_power_map(codec, nid, 1); continue; } - if (!stac_get_event(codec, nid, STAC_INSERT_EVENT)) { - enable_pin_detect(codec, nid, STAC_PWR_EVENT); - stac_issue_unsol_event(codec, nid, STAC_PWR_EVENT); - } + if (enable_pin_detect(codec, nid, STAC_PWR_EVENT)) + stac_issue_unsol_event(codec, nid); } if (spec->dac_list) stac92xx_power_down(codec); @@ -4440,6 +4391,48 @@ static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) return 0; } +static void stac92xx_line_out_detect(struct hda_codec *codec, + int presence) +{ + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < cfg->line_outs; i++) { + if (presence) + break; + presence = get_pin_presence(codec, cfg->line_out_pins[i]); + if (presence) { + unsigned int pinctl; + pinctl = snd_hda_codec_read(codec, + cfg->line_out_pins[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (pinctl & AC_PINCTL_IN_EN) + presence = 0; /* mic- or line-input */ + } + } + + if (presence) { + /* disable speakers */ + for (i = 0; i < cfg->speaker_outs; i++) + stac92xx_reset_pinctl(codec, cfg->speaker_pins[i], + AC_PINCTL_OUT_EN); + if (spec->eapd_mask && spec->eapd_switch) + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data & + ~spec->eapd_mask); + } else { + /* enable speakers */ + for (i = 0; i < cfg->speaker_outs; i++) + stac92xx_set_pinctl(codec, cfg->speaker_pins[i], + AC_PINCTL_OUT_EN); + if (spec->eapd_mask && spec->eapd_switch) + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data | + spec->eapd_mask); + } +} + /* return non-zero if the hp-pin of the given array index isn't * a jack-detection target */ @@ -4492,13 +4485,6 @@ static void stac92xx_hp_detect(struct hda_codec *codec) for (i = 0; i < cfg->line_outs; i++) stac92xx_reset_pinctl(codec, cfg->line_out_pins[i], AC_PINCTL_OUT_EN); - for (i = 0; i < cfg->speaker_outs; i++) - stac92xx_reset_pinctl(codec, cfg->speaker_pins[i], - AC_PINCTL_OUT_EN); - if (spec->eapd_mask && spec->eapd_switch) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data & - ~spec->eapd_mask); } else { /* enable lineouts */ if (spec->hp_switch) @@ -4507,14 +4493,8 @@ static void stac92xx_hp_detect(struct hda_codec *codec) for (i = 0; i < cfg->line_outs; i++) stac92xx_set_pinctl(codec, cfg->line_out_pins[i], AC_PINCTL_OUT_EN); - for (i = 0; i < cfg->speaker_outs; i++) - stac92xx_set_pinctl(codec, cfg->speaker_pins[i], - AC_PINCTL_OUT_EN); - if (spec->eapd_mask && spec->eapd_switch) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data | - spec->eapd_mask); } + stac92xx_line_out_detect(codec, presence); /* toggle hp outs */ for (i = 0; i < cfg->hp_outs; i++) { unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN; @@ -4599,10 +4579,28 @@ static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid) } } -static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid, - unsigned char type) +static void stac92xx_mic_detect(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct sigmatel_mic_route *mic; + + if (get_pin_presence(codec, spec->ext_mic.pin)) + mic = &spec->ext_mic; + else + mic = &spec->int_mic; + if (mic->dmux_idx >= 0) + snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0, + AC_VERB_SET_CONNECT_SEL, + mic->dmux_idx); + if (mic->mux_idx >= 0) + snd_hda_codec_write_cache(codec, spec->mux_nids[0], 0, + AC_VERB_SET_CONNECT_SEL, + mic->mux_idx); +} + +static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid) { - struct sigmatel_event *event = stac_get_event(codec, nid, type); + struct sigmatel_event *event = stac_get_event(codec, nid); if (!event) return; codec->patch_ops.unsol_event(codec, (unsigned)event->tag << 26); @@ -4621,8 +4619,18 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) switch (event->type) { case STAC_HP_EVENT: + case STAC_LO_EVENT: stac92xx_hp_detect(codec); - /* fallthru */ + break; + case STAC_MIC_EVENT: + stac92xx_mic_detect(codec); + break; + } + + switch (event->type) { + case STAC_HP_EVENT: + case STAC_LO_EVENT: + case STAC_MIC_EVENT: case STAC_INSERT_EVENT: case STAC_PWR_EVENT: if (spec->num_pwrs > 0) @@ -4712,9 +4720,13 @@ static int stac92xx_resume(struct hda_codec *codec) snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); /* fake event to set up pins again to override cached values */ - if (spec->hp_detect) - stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0], - STAC_HP_EVENT); + if (spec->hp_detect) { + if (spec->autocfg.hp_pins[0]) + stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0]); + else if (spec->autocfg.line_out_pins[0]) + stac_issue_unsol_event(codec, + spec->autocfg.line_out_pins[0]); + } return 0; } @@ -4754,6 +4766,19 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec, static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) { struct sigmatel_spec *spec = codec->spec; + int i; + hda_nid_t nid; + + /* reset each pin before powering down DAC/ADC to avoid click noise */ + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int wid_type = get_wcaps_type(wcaps); + if (wid_type == AC_WID_PIN) + snd_hda_codec_read(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } + if (spec->eapd_mask) stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data & @@ -4790,7 +4815,8 @@ static int patch_stac9200(struct hda_codec *codec) stac9200_models, stac9200_cfg_tbl); if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n"); + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); else stac92xx_set_config_regs(codec, stac9200_brd_tbl[spec->board_config]); @@ -4862,8 +4888,8 @@ static int patch_stac925x(struct hda_codec *codec) stac925x_cfg_tbl); again: if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x," - "using BIOS defaults\n"); + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); else stac92xx_set_config_regs(codec, stac925x_brd_tbl[spec->board_config]); @@ -4893,6 +4919,9 @@ static int patch_stac925x(struct hda_codec *codec) spec->init = stac925x_core_init; spec->mixer = stac925x_mixer; + spec->num_caps = 1; + spec->capvols = stac925x_capvols; + spec->capsws = stac925x_capsws; err = stac92xx_parse_auto_config(codec, 0x8, 0x7); if (!err) { @@ -4914,16 +4943,6 @@ static int patch_stac925x(struct hda_codec *codec) return 0; } -static struct hda_input_mux stac92hd73xx_dmux = { - .num_items = 4, - .items = { - { "Analog Inputs", 0x0b }, - { "Digital Mic 1", 0x09 }, - { "Digital Mic 2", 0x0a }, - { "CD", 0x08 }, - } -}; - static int patch_stac92hd73xx(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -4943,10 +4962,16 @@ static int patch_stac92hd73xx(struct hda_codec *codec) STAC_92HD73XX_MODELS, stac92hd73xx_models, stac92hd73xx_cfg_tbl); + /* check codec subsystem id if not found */ + if (spec->board_config < 0) + spec->board_config = + snd_hda_check_board_codec_sid_config(codec, + STAC_92HD73XX_MODELS, stac92hd73xx_models, + stac92hd73xx_codec_id_cfg_tbl); again: if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: Unknown model for" - " STAC92HD73XX, using BIOS defaults\n"); + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); else stac92xx_set_config_regs(codec, stac92hd73xx_brd_tbl[spec->board_config]); @@ -4959,20 +4984,15 @@ again: "number of channels defaulting to DAC count\n"); num_dacs = STAC92HD73_DAC_COUNT; } + spec->init = stac92hd73xx_core_init; switch (num_dacs) { case 0x3: /* 6 Channel */ - spec->mixer = stac92hd73xx_6ch_mixer; - spec->init = stac92hd73xx_6ch_core_init; spec->aloopback_ctl = stac92hd73xx_6ch_loopback; break; case 0x4: /* 8 Channel */ - spec->mixer = stac92hd73xx_8ch_mixer; - spec->init = stac92hd73xx_8ch_core_init; spec->aloopback_ctl = stac92hd73xx_8ch_loopback; break; case 0x5: /* 10 Channel */ - spec->mixer = stac92hd73xx_10ch_mixer; - spec->init = stac92hd73xx_10ch_core_init; spec->aloopback_ctl = stac92hd73xx_10ch_loopback; break; } @@ -4987,14 +5007,14 @@ again: spec->dmic_nids = stac92hd73xx_dmic_nids; spec->dmux_nids = stac92hd73xx_dmux_nids; spec->smux_nids = stac92hd73xx_smux_nids; - spec->amp_nids = stac92hd73xx_amp_nids; - spec->num_amps = ARRAY_SIZE(stac92hd73xx_amp_nids); spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids); spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids); spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids); - memcpy(&spec->private_dimux, &stac92hd73xx_dmux, - sizeof(stac92hd73xx_dmux)); + + spec->num_caps = STAC92HD73XX_NUM_CAPS; + spec->capvols = stac92hd73xx_capvols; + spec->capsws = stac92hd73xx_capsws; switch (spec->board_config) { case STAC_DELL_EQ: @@ -5004,43 +5024,40 @@ again: case STAC_DELL_M6_DMIC: case STAC_DELL_M6_BOTH: spec->num_smuxes = 0; - spec->mixer = &stac92hd73xx_6ch_mixer[DELL_M6_MIXER]; - spec->amp_nids = &stac92hd73xx_amp_nids[DELL_M6_AMP]; spec->eapd_switch = 0; - spec->num_amps = 1; - if (spec->board_config != STAC_DELL_EQ) - spec->init = dell_m6_core_init; switch (spec->board_config) { case STAC_DELL_M6_AMIC: /* Analog Mics */ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); spec->num_dmics = 0; - spec->private_dimux.num_items = 1; break; case STAC_DELL_M6_DMIC: /* Digital Mics */ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); spec->num_dmics = 1; - spec->private_dimux.num_items = 2; break; case STAC_DELL_M6_BOTH: /* Both */ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); spec->num_dmics = 1; - spec->private_dimux.num_items = 2; break; } break; + case STAC_ALIENWARE_M17X: + spec->num_dmics = STAC92HD73XX_NUM_DMICS; + spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); + spec->eapd_switch = 0; + break; default: spec->num_dmics = STAC92HD73XX_NUM_DMICS; spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); spec->eapd_switch = 1; + break; } - if (spec->board_config > STAC_92HD73XX_REF) { + if (spec->board_config != STAC_92HD73XX_REF) { /* GPIO0 High = Enable EAPD */ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; spec->gpio_data = 0x01; } - spec->dinput_mux = &spec->private_dimux; spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids); spec->pwr_nids = stac92hd73xx_pwr_nids; @@ -5072,15 +5089,6 @@ again: return 0; } -static struct hda_input_mux stac92hd83xxx_dmux = { - .num_items = 3, - .items = { - { "Analog Inputs", 0x03 }, - { "Digital Mic 1", 0x04 }, - { "Digital Mic 2", 0x05 }, - } -}; - static int patch_stac92hd83xxx(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -5095,34 +5103,31 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) codec->spec = spec; codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs; - spec->mono_nid = 0x19; spec->digbeep_nid = 0x21; - spec->dmic_nids = stac92hd83xxx_dmic_nids; - spec->dmux_nids = stac92hd83xxx_dmux_nids; + spec->mux_nids = stac92hd83xxx_mux_nids; + spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids); spec->adc_nids = stac92hd83xxx_adc_nids; + spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids); spec->pwr_nids = stac92hd83xxx_pwr_nids; - spec->amp_nids = stac92hd83xxx_amp_nids; spec->pwr_mapping = stac92hd83xxx_pwr_mapping; spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids); spec->multiout.dac_nids = spec->dac_nids; spec->init = stac92hd83xxx_core_init; - spec->mixer = stac92hd83xxx_mixer; spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids); - spec->num_dmuxes = ARRAY_SIZE(stac92hd83xxx_dmux_nids); - spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids); - spec->num_amps = ARRAY_SIZE(stac92hd83xxx_amp_nids); - spec->num_dmics = STAC92HD83XXX_NUM_DMICS; - spec->dinput_mux = &stac92hd83xxx_dmux; spec->pin_nids = stac92hd83xxx_pin_nids; + spec->num_caps = STAC92HD83XXX_NUM_CAPS; + spec->capvols = stac92hd83xxx_capvols; + spec->capsws = stac92hd83xxx_capsws; + spec->board_config = snd_hda_check_board_config(codec, STAC_92HD83XXX_MODELS, stac92hd83xxx_models, stac92hd83xxx_cfg_tbl); again: if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: Unknown model for" - " STAC92HD83XXX, using BIOS defaults\n"); + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); else stac92xx_set_config_regs(codec, stac92hd83xxx_brd_tbl[spec->board_config]); @@ -5164,6 +5169,8 @@ again: num_dacs = snd_hda_get_connections(codec, nid, conn, STAC92HD83_DAC_COUNT + 1) - 1; + if (num_dacs < 0) + num_dacs = STAC92HD83_DAC_COUNT; /* set port X to select the last DAC */ @@ -5177,25 +5184,6 @@ again: return 0; } -static struct hda_input_mux stac92hd71bxx_dmux_nomixer = { - .num_items = 3, - .items = { - { "Analog Inputs", 0x00 }, - { "Digital Mic 1", 0x02 }, - { "Digital Mic 2", 0x03 }, - } -}; - -static struct hda_input_mux stac92hd71bxx_dmux_amixer = { - .num_items = 4, - .items = { - { "Analog Inputs", 0x00 }, - { "Mixer", 0x01 }, - { "Digital Mic 1", 0x02 }, - { "Digital Mic 2", 0x03 }, - } -}; - /* get the pin connection (fixed, none, etc) */ static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx) { @@ -5256,7 +5244,6 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) struct sigmatel_spec *spec; struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init; int err = 0; - unsigned int ndmic_nids = 0; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -5285,13 +5272,13 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) stac92hd71bxx_cfg_tbl); again: if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: Unknown model for" - " STAC92HD71BXX, using BIOS defaults\n"); + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); else stac92xx_set_config_regs(codec, stac92hd71bxx_brd_tbl[spec->board_config]); - if (spec->board_config > STAC_92HD71BXX_REF) { + if (spec->board_config != STAC_92HD71BXX_REF) { /* GPIO0 = EAPD */ spec->gpio_mask = 0x01; spec->gpio_dir = 0x01; @@ -5301,6 +5288,10 @@ again: spec->dmic_nids = stac92hd71bxx_dmic_nids; spec->dmux_nids = stac92hd71bxx_dmux_nids; + spec->num_caps = STAC92HD71BXX_NUM_CAPS; + spec->capvols = stac92hd71bxx_capvols; + spec->capsws = stac92hd71bxx_capsws; + switch (codec->vendor_id) { case 0x111d76b6: /* 4 Port without Analog Mixer */ case 0x111d76b7: @@ -5308,24 +5299,13 @@ again: /* fallthru */ case 0x111d76b4: /* 6 Port without Analog Mixer */ case 0x111d76b5: - memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_nomixer, - sizeof(stac92hd71bxx_dmux_nomixer)); - spec->mixer = stac92hd71bxx_mixer; spec->init = stac92hd71bxx_core_init; codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; spec->num_dmics = stac92hd71bxx_connected_ports(codec, stac92hd71bxx_dmic_nids, STAC92HD71BXX_NUM_DMICS); - if (spec->num_dmics) { - spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); - spec->dinput_mux = &spec->private_dimux; - ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1; - } break; case 0x111d7608: /* 5 Port with Analog Mixer */ - memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_amixer, - sizeof(stac92hd71bxx_dmux_amixer)); - spec->private_dimux.num_items--; switch (spec->board_config) { case STAC_HP_M4: /* Enable VREF power saving on GPIO1 detect */ @@ -5347,11 +5327,8 @@ again: /* no output amps */ spec->num_pwrs = 0; - spec->mixer = stac92hd71bxx_analog_mixer; - spec->dinput_mux = &spec->private_dimux; - /* disable VSW */ - spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF]; + spec->init = stac92hd71bxx_core_init; unmute_init++; snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0); snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3); @@ -5359,8 +5336,6 @@ again: spec->num_dmics = stac92hd71bxx_connected_ports(codec, stac92hd71bxx_dmic_nids, STAC92HD71BXX_NUM_DMICS - 1); - spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); - ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 2; break; case 0x111d7603: /* 6 Port with Analog Mixer */ if ((codec->revision_id & 0xf) == 1) @@ -5370,17 +5345,12 @@ again: spec->num_pwrs = 0; /* fallthru */ default: - memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_amixer, - sizeof(stac92hd71bxx_dmux_amixer)); - spec->dinput_mux = &spec->private_dimux; - spec->mixer = stac92hd71bxx_analog_mixer; - spec->init = stac92hd71bxx_analog_core_init; + spec->init = stac92hd71bxx_core_init; codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; spec->num_dmics = stac92hd71bxx_connected_ports(codec, stac92hd71bxx_dmic_nids, STAC92HD71BXX_NUM_DMICS); - spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); - ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1; + break; } if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) @@ -5408,6 +5378,7 @@ again: spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids); spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids); + spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e); switch (spec->board_config) { @@ -5440,6 +5411,11 @@ again: case STAC_HP_DV5: snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN); + /* HP dv6 gives the headphone pin as a line-out. Thus we + * need to set hp_detect flag here to force to enable HP + * detection. + */ + spec->hp_detect = 1; break; case STAC_HP_HDX: spec->num_dmics = 1; @@ -5462,8 +5438,6 @@ again: #endif spec->multiout.dac_nids = spec->dac_nids; - if (spec->dinput_mux) - spec->private_dimux.num_items += spec->num_dmics - ndmic_nids; err = stac92xx_parse_auto_config(codec, 0x21, 0); if (!err) { @@ -5541,8 +5515,8 @@ static int patch_stac922x(struct hda_codec *codec) again: if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, " - "using BIOS defaults\n"); + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); else stac92xx_set_config_regs(codec, stac922x_brd_tbl[spec->board_config]); @@ -5555,7 +5529,10 @@ static int patch_stac922x(struct hda_codec *codec) spec->num_pwrs = 0; spec->init = stac922x_core_init; - spec->mixer = stac922x_mixer; + + spec->num_caps = STAC922X_NUM_CAPS; + spec->capvols = stac922x_capvols; + spec->capsws = stac922x_capsws; spec->multiout.dac_nids = spec->dac_nids; @@ -5604,8 +5581,8 @@ static int patch_stac927x(struct hda_codec *codec) stac927x_cfg_tbl); again: if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: Unknown model for" - "STAC927x, using BIOS defaults\n"); + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); else stac92xx_set_config_regs(codec, stac927x_brd_tbl[spec->board_config]); @@ -5621,16 +5598,18 @@ static int patch_stac927x(struct hda_codec *codec) spec->dac_list = stac927x_dac_nids; spec->multiout.dac_nids = spec->dac_nids; + if (spec->board_config != STAC_D965_REF) { + /* GPIO0 High = Enable EAPD */ + spec->eapd_mask = spec->gpio_mask = 0x01; + spec->gpio_dir = spec->gpio_data = 0x01; + } + switch (spec->board_config) { case STAC_D965_3ST: case STAC_D965_5ST: /* GPIO0 High = Enable EAPD */ - spec->eapd_mask = 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_DELL_BIOS: switch (codec->subsystem_id) { @@ -5648,36 +5627,32 @@ static int patch_stac927x(struct hda_codec *codec) snd_hda_codec_set_pincfg(codec, 0x0e, 0x02a79130); /* fallthru */ case STAC_DELL_3ST: - /* GPIO2 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x04; - spec->gpio_data = 0x04; - switch (codec->subsystem_id) { - case 0x1028022f: - /* correct EAPD to be GPIO0 */ - spec->eapd_mask = spec->gpio_mask = 0x01; - spec->gpio_dir = spec->gpio_data = 0x01; - break; - }; + if (codec->subsystem_id != 0x1028022f) { + /* GPIO2 High = Enable EAPD */ + spec->eapd_mask = spec->gpio_mask = 0x04; + spec->gpio_dir = 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->init = dell_3st_core_init; spec->dmux_nids = stac927x_dmux_nids; spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids); break; + case STAC_927X_VOLKNOB: + spec->num_dmics = 0; + spec->init = stac927x_volknob_core_init; + break; default: - if (spec->board_config > STAC_D965_REF) { - /* GPIO0 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = 0x01; - spec->gpio_dir = spec->gpio_data = 0x01; - } spec->num_dmics = 0; - spec->init = stac927x_core_init; - spec->mixer = stac927x_mixer; + break; } + spec->num_caps = STAC927X_NUM_CAPS; + spec->capvols = stac927x_capvols; + spec->capsws = stac927x_capsws; + spec->num_pwrs = 0; spec->aloopback_ctl = stac927x_loopback; spec->aloopback_mask = 0x40; @@ -5739,7 +5714,8 @@ static int patch_stac9205(struct hda_codec *codec) stac9205_cfg_tbl); again: if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n"); + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); else stac92xx_set_config_regs(codec, stac9205_brd_tbl[spec->board_config]); @@ -5758,9 +5734,12 @@ static int patch_stac9205(struct hda_codec *codec) spec->num_pwrs = 0; spec->init = stac9205_core_init; - spec->mixer = stac9205_mixer; spec->aloopback_ctl = stac9205_loopback; + spec->num_caps = STAC9205_NUM_CAPS; + spec->capvols = stac9205_capvols; + spec->capsws = stac9205_capsws; + spec->aloopback_mask = 0x40; spec->aloopback_shift = 0; /* Turn on/off EAPD per HP plugging */ @@ -5835,12 +5814,6 @@ static struct hda_verb stac9872_core_init[] = { {} }; -static struct snd_kcontrol_new stac9872_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT), - { } /* end */ -}; - static hda_nid_t stac9872_pin_nids[] = { 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x13, 0x14, @@ -5854,6 +5827,11 @@ static hda_nid_t stac9872_mux_nids[] = { 0x15 }; +static unsigned long stac9872_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), +}; +#define stac9872_capsws stac9872_capvols + static unsigned int stac9872_vaio_pin_configs[9] = { 0x03211020, 0x411111f0, 0x411111f0, 0x03a15030, 0x411111f0, 0x90170110, 0x411111f0, 0x411111f0, @@ -5891,8 +5869,8 @@ static int patch_stac9872(struct hda_codec *codec) stac9872_models, stac9872_cfg_tbl); if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9872, " - "using BIOS defaults\n"); + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); else stac92xx_set_config_regs(codec, stac9872_brd_tbl[spec->board_config]); @@ -5902,8 +5880,10 @@ static int patch_stac9872(struct hda_codec *codec) spec->adc_nids = stac9872_adc_nids; spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids); spec->mux_nids = stac9872_mux_nids; - spec->mixer = stac9872_mixer; spec->init = stac9872_core_init; + spec->num_caps = 1; + spec->capvols = stac9872_capvols; + spec->capsws = stac9872_capsws; err = stac92xx_parse_auto_config(codec, 0x10, 0x12); if (err < 0) { diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index e8f10b10cce..ee89db90c9b 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1339,8 +1339,7 @@ static int get_mux_nids(struct hda_codec *codec) for (i = 0; i < spec->num_adc_nids; i++) { nid = spec->adc_nids[i]; while (nid) { - type = (get_wcaps(codec, nid) & AC_WCAP_TYPE) - >> AC_WCAP_TYPE_SHIFT; + type = get_wcaps_type(get_wcaps(codec, nid)); if (type == AC_WID_PIN) break; n = snd_hda_get_connections(codec, nid, conn, diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c index 37564300b50..6da21a2bcad 100644 --- a/sound/pci/ice1712/amp.c +++ b/sound/pci/ice1712/amp.c @@ -52,11 +52,13 @@ static int __devinit snd_vt1724_amp_init(struct snd_ice1712 *ice) /* only use basic functionality for now */ - ice->num_total_dacs = 2; /* only PSDOUT0 is connected */ + /* VT1616 6ch codec connected to PSDOUT0 using packed mode */ + ice->num_total_dacs = 6; ice->num_total_adcs = 2; - /* Chaintech AV-710 has another codecs, which need initialization */ - /* initialize WM8728 codec */ + /* Chaintech AV-710 has another WM8728 codec connected to PSDOUT4 + (shared with the SPDIF output). Mixer control for this codec + is not yet supported. */ if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AV710) { for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2) wm_put(ice, wm_inits[i], wm_inits[i+1]); diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index cecf1ffeeaa..d74033a2cfb 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2259,7 +2259,7 @@ static int snd_ice1712_pro_peak_get(struct snd_kcontrol *kcontrol, } static struct snd_kcontrol_new snd_ice1712_mixer_pro_peak __devinitdata = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "Multi Track Peak", .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_ice1712_pro_peak_info, diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index adc909ec125..9da2dae64c5 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -379,6 +379,15 @@ struct snd_ice1712 { unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate); void (*set_spdif_clock)(struct snd_ice1712 *ice); +#ifdef CONFIG_PM + int (*pm_suspend)(struct snd_ice1712 *); + int (*pm_resume)(struct snd_ice1712 *); + int pm_suspend_enabled:1; + int pm_saved_is_spdif_master:1; + unsigned int pm_saved_spdif_ctrl; + unsigned char pm_saved_spdif_cfg; + unsigned int pm_saved_route; +#endif }; diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index cc84a831eb2..10fc92c0557 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -560,6 +560,7 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: spin_lock(&ice->reg_lock); old = inb(ICEMT1724(ice, DMA_CONTROL)); if (cmd == SNDRV_PCM_TRIGGER_START) @@ -570,6 +571,10 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd) spin_unlock(&ice->reg_lock); break; + case SNDRV_PCM_TRIGGER_RESUME: + /* apps will have to restart stream */ + break; + default: return -EINVAL; } @@ -643,7 +648,7 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) { /* running? we cannot change the rate now... */ spin_unlock_irqrestore(&ice->reg_lock, flags); - return -EBUSY; + return ((rate == ice->cur_rate) && !force) ? 0 : -EBUSY; } if (!force && is_pro_rate_locked(ice)) { spin_unlock_irqrestore(&ice->reg_lock, flags); @@ -1289,7 +1294,7 @@ static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device) snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(ice->pci), - 64*1024, 64*1024); + 256*1024, 256*1024); ice->pcm = pcm; @@ -1403,7 +1408,7 @@ static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device) snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(ice->pci), - 64*1024, 64*1024); + 256*1024, 256*1024); ice->pcm_ds = pcm; @@ -2105,7 +2110,7 @@ static int snd_vt1724_pro_peak_get(struct snd_kcontrol *kcontrol, } static struct snd_kcontrol_new snd_vt1724_mixer_pro_peak __devinitdata = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "Multi Track Peak", .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_vt1724_pro_peak_info, @@ -2262,7 +2267,7 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice, -static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice) +static void snd_vt1724_chip_reset(struct snd_ice1712 *ice) { outb(VT1724_RESET , ICEREG1724(ice, CONTROL)); inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */ @@ -2272,7 +2277,7 @@ static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice) msleep(10); } -static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice) +static int snd_vt1724_chip_init(struct snd_ice1712 *ice) { outb(ice->eeprom.data[ICE_EEP2_SYSCONF], ICEREG1724(ice, SYS_CFG)); outb(ice->eeprom.data[ICE_EEP2_ACLINK], ICEREG1724(ice, AC97_CFG)); @@ -2287,6 +2292,14 @@ static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice) outb(0, ICEREG1724(ice, POWERDOWN)); + /* MPU_RX and TX irq masks are cleared later dynamically */ + outb(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX , ICEREG1724(ice, IRQMASK)); + + /* don't handle FIFO overrun/underruns (just yet), + * since they cause machine lockups + */ + outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK)); + return 0; } @@ -2431,6 +2444,8 @@ static int __devinit snd_vt1724_create(struct snd_card *card, snd_vt1724_proc_init(ice); synchronize_irq(pci->irq); + card->private_data = ice; + err = pci_request_regions(pci, "ICE1724"); if (err < 0) { kfree(ice); @@ -2459,14 +2474,6 @@ static int __devinit snd_vt1724_create(struct snd_card *card, return -EIO; } - /* MPU_RX and TX irq masks are cleared later dynamically */ - outb(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX , ICEREG1724(ice, IRQMASK)); - - /* don't handle FIFO overrun/underruns (just yet), - * since they cause machine lockups - */ - outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK)); - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops); if (err < 0) { snd_vt1724_free(ice); @@ -2650,11 +2657,96 @@ static void __devexit snd_vt1724_remove(struct pci_dev *pci) pci_set_drvdata(pci, NULL); } +#ifdef CONFIG_PM +static int snd_vt1724_suspend(struct pci_dev *pci, pm_message_t state) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct snd_ice1712 *ice = card->private_data; + + if (!ice->pm_suspend_enabled) + return 0; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + + snd_pcm_suspend_all(ice->pcm); + snd_pcm_suspend_all(ice->pcm_pro); + snd_pcm_suspend_all(ice->pcm_ds); + snd_ac97_suspend(ice->ac97); + + spin_lock_irq(&ice->reg_lock); + ice->pm_saved_is_spdif_master = ice->is_spdif_master(ice); + ice->pm_saved_spdif_ctrl = inw(ICEMT1724(ice, SPDIF_CTRL)); + ice->pm_saved_spdif_cfg = inb(ICEREG1724(ice, SPDIF_CFG)); + ice->pm_saved_route = inl(ICEMT1724(ice, ROUTE_PLAYBACK)); + spin_unlock_irq(&ice->reg_lock); + + if (ice->pm_suspend) + ice->pm_suspend(ice); + + pci_disable_device(pci); + pci_save_state(pci); + pci_set_power_state(pci, pci_choose_state(pci, state)); + return 0; +} + +static int snd_vt1724_resume(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct snd_ice1712 *ice = card->private_data; + + if (!ice->pm_suspend_enabled) + return 0; + + pci_set_power_state(pci, PCI_D0); + pci_restore_state(pci); + + if (pci_enable_device(pci) < 0) { + snd_card_disconnect(card); + return -EIO; + } + + pci_set_master(pci); + + snd_vt1724_chip_reset(ice); + + if (snd_vt1724_chip_init(ice) < 0) { + snd_card_disconnect(card); + return -EIO; + } + + if (ice->pm_resume) + ice->pm_resume(ice); + + if (ice->pm_saved_is_spdif_master) { + /* switching to external clock via SPDIF */ + ice->set_spdif_clock(ice); + } else { + /* internal on-card clock */ + snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1); + } + + update_spdif_bits(ice, ice->pm_saved_spdif_ctrl); + + outb(ice->pm_saved_spdif_cfg, ICEREG1724(ice, SPDIF_CFG)); + outl(ice->pm_saved_route, ICEMT1724(ice, ROUTE_PLAYBACK)); + + if (ice->ac97) + snd_ac97_resume(ice->ac97); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} +#endif + static struct pci_driver driver = { .name = "ICE1724", .id_table = snd_vt1724_ids, .probe = snd_vt1724_probe, .remove = __devexit_p(snd_vt1724_remove), +#ifdef CONFIG_PM + .suspend = snd_vt1724_suspend, + .resume = snd_vt1724_resume, +#endif }; static int __init alsa_card_ice1724_init(void) diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c index 043a93879bd..c75515f5be6 100644 --- a/sound/pci/ice1712/prodigy_hifi.c +++ b/sound/pci/ice1712/prodigy_hifi.c @@ -1077,7 +1077,7 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice) /* * initialize the chip */ -static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice) +static void ak4396_init(struct snd_ice1712 *ice) { static unsigned short ak4396_inits[] = { AK4396_CTRL1, 0x87, /* I2S Normal Mode, 24 bit */ @@ -1087,9 +1087,37 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice) AK4396_RCH_ATT, 0x00, }; - struct prodigy_hifi_spec *spec; unsigned int i; + /* 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]); +} + +#ifdef CONFIG_PM +static int __devinit prodigy_hd2_resume(struct snd_ice1712 *ice) +{ + /* initialize ak4396 codec and restore previous mixer volumes */ + struct prodigy_hifi_spec *spec = ice->spec; + int i; + mutex_lock(&ice->gpio_mutex); + ak4396_init(ice); + for (i = 0; i < 2; i++) + ak4396_write(ice, AK4396_LCH_ATT + i, spec->vol[i] & 0xff); + mutex_unlock(&ice->gpio_mutex); + return 0; +} +#endif + +static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice) +{ + struct prodigy_hifi_spec *spec; + ice->vt1720 = 0; ice->vt1724 = 1; @@ -1112,14 +1140,12 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice) 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]); +#ifdef CONFIG_PM + ice->pm_resume = &prodigy_hd2_resume; + ice->pm_suspend_enabled = 1; +#endif + + ak4396_init(ice); return 0; } diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 171ada53520..754867ed478 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1954,6 +1954,18 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = { .name = "Sony S1XP", .type = AC97_TUNE_INV_EAPD }, + { + .subvendor = 0x104d, + .subdevice = 0x81c0, + .name = "Sony VAIO VGN-T350P", /*AD1981B*/ + .type = AC97_TUNE_INV_EAPD + }, + { + .subvendor = 0x104d, + .subdevice = 0x81c5, + .name = "Sony VAIO VGN-B1VP", /*AD1981B*/ + .type = AC97_TUNE_INV_EAPD + }, { .subvendor = 0x1043, .subdevice = 0x80f3, diff --git a/sound/pci/lx6464es/lx6464es.h b/sound/pci/lx6464es/lx6464es.h index 012c010c8c8..51afc048961 100644 --- a/sound/pci/lx6464es/lx6464es.h +++ b/sound/pci/lx6464es/lx6464es.h @@ -86,7 +86,6 @@ struct lx6464es { /* messaging */ spinlock_t msg_lock; /* message spinlock */ - atomic_t send_message_locked; struct lx_rmh rmh; /* configuration */ @@ -95,7 +94,6 @@ struct lx6464es { uint hardware_running[2]; u32 board_sample_rate; /* sample rate read from * board */ - u32 sample_rate; /* our sample rate */ u16 pcm_granularity; /* board blocksize */ /* dma */ diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c index 5812780d6e8..3086b751da4 100644 --- a/sound/pci/lx6464es/lx_core.c +++ b/sound/pci/lx6464es/lx_core.c @@ -314,98 +314,6 @@ static inline void lx_message_dump(struct lx_rmh *rmh) #define XILINX_POLL_NO_SLEEP 100 #define XILINX_POLL_ITERATIONS 150 -#if 0 /* not used now */ -static int lx_message_send(struct lx6464es *chip, struct lx_rmh *rmh) -{ - u32 reg = ED_DSP_TIMED_OUT; - int dwloop; - int answer_received; - - if (lx_dsp_reg_read(chip, eReg_CSM) & (Reg_CSM_MC | Reg_CSM_MR)) { - snd_printk(KERN_ERR LXP "PIOSendMessage eReg_CSM %x\n", reg); - return -EBUSY; - } - - /* write command */ - lx_dsp_reg_writebuf(chip, eReg_CRM1, rmh->cmd, rmh->cmd_len); - - snd_BUG_ON(atomic_read(&chip->send_message_locked) != 0); - atomic_set(&chip->send_message_locked, 1); - - /* MicoBlaze gogogo */ - lx_dsp_reg_write(chip, eReg_CSM, Reg_CSM_MC); - - /* wait for interrupt to answer */ - for (dwloop = 0; dwloop != XILINX_TIMEOUT_MS; ++dwloop) { - answer_received = atomic_read(&chip->send_message_locked); - if (answer_received == 0) - break; - msleep(1); - } - - if (answer_received == 0) { - /* in Debug mode verify Reg_CSM_MR */ - snd_BUG_ON(!(lx_dsp_reg_read(chip, eReg_CSM) & Reg_CSM_MR)); - - /* command finished, read status */ - if (rmh->dsp_stat == 0) - reg = lx_dsp_reg_read(chip, eReg_CRM1); - else - reg = 0; - } else { - int i; - snd_printk(KERN_WARNING LXP "TIMEOUT lx_message_send! " - "Interrupts disabled?\n"); - - /* attente bit Reg_CSM_MR */ - for (i = 0; i != XILINX_POLL_ITERATIONS; i++) { - if ((lx_dsp_reg_read(chip, eReg_CSM) & Reg_CSM_MR)) { - if (rmh->dsp_stat == 0) - reg = lx_dsp_reg_read(chip, eReg_CRM1); - else - reg = 0; - goto polling_successful; - } - - if (i > XILINX_POLL_NO_SLEEP) - msleep(1); - } - snd_printk(KERN_WARNING LXP "TIMEOUT lx_message_send! " - "polling failed\n"); - -polling_successful: - atomic_set(&chip->send_message_locked, 0); - } - - if ((reg & ERROR_VALUE) == 0) { - /* read response */ - if (rmh->stat_len) { - snd_BUG_ON(rmh->stat_len >= (REG_CRM_NUMBER-1)); - - lx_dsp_reg_readbuf(chip, eReg_CRM2, rmh->stat, - rmh->stat_len); - } - } else - snd_printk(KERN_WARNING LXP "lx_message_send: error_value %x\n", - reg); - - /* clear Reg_CSM_MR */ - lx_dsp_reg_write(chip, eReg_CSM, 0); - - switch (reg) { - case ED_DSP_TIMED_OUT: - snd_printk(KERN_WARNING LXP "lx_message_send: dsp timeout\n"); - return -ETIMEDOUT; - - case ED_DSP_CRASHED: - snd_printk(KERN_WARNING LXP "lx_message_send: dsp crashed\n"); - return -EAGAIN; - } - - lx_message_dump(rmh); - return 0; -} -#endif /* not used now */ static int lx_message_send_atomic(struct lx6464es *chip, struct lx_rmh *rmh) { @@ -423,7 +331,7 @@ static int lx_message_send_atomic(struct lx6464es *chip, struct lx_rmh *rmh) /* MicoBlaze gogogo */ lx_dsp_reg_write(chip, eReg_CSM, Reg_CSM_MC); - /* wait for interrupt to answer */ + /* wait for device to answer */ for (dwloop = 0; dwloop != XILINX_TIMEOUT_MS * 1000; ++dwloop) { if (lx_dsp_reg_read(chip, eReg_CSM) & Reg_CSM_MR) { if (rmh->dsp_stat == 0) @@ -1175,10 +1083,6 @@ static int lx_interrupt_ack(struct lx6464es *chip, u32 *r_irqsrc, *r_async_escmd = 1; } - if (irqsrc & MASK_SYS_STATUS_CMD_DONE) - /* xilinx command notification */ - atomic_set(&chip->send_message_locked, 0); - if (irq_async) { /* snd_printd("interrupt: async event pending\n"); */ *r_async_pending = 1; diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c index c1eb923f2ac..09b2b2a36df 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -215,17 +215,8 @@ EXPORT_SYMBOL(oxygen_write_spi); void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data) { - unsigned long timeout; - /* should not need more than about 300 us */ - timeout = jiffies + msecs_to_jiffies(1); - do { - if (!(oxygen_read16(chip, OXYGEN_2WIRE_BUS_STATUS) - & OXYGEN_2WIRE_BUSY)) - break; - udelay(1); - cond_resched(); - } while (time_after_eq(timeout, jiffies)); + msleep(1); oxygen_write8(chip, OXYGEN_2WIRE_MAP, map); oxygen_write8(chip, OXYGEN_2WIRE_DATA, data); diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 3da5c029f93..7bb827c7d80 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -3294,15 +3294,33 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) char *clock_source; int x; - if (hdsp_check_for_iobox (hdsp)) { - snd_iprintf(buffer, "No I/O box connected.\nPlease connect one and upload firmware.\n"); + status = hdsp_read(hdsp, HDSP_statusRegister); + status2 = hdsp_read(hdsp, HDSP_status2Register); + + snd_iprintf(buffer, "%s (Card #%d)\n", hdsp->card_name, + hdsp->card->number + 1); + snd_iprintf(buffer, "Buffers: capture %p playback %p\n", + hdsp->capture_buffer, hdsp->playback_buffer); + snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", + hdsp->irq, hdsp->port, (unsigned long)hdsp->iobase); + snd_iprintf(buffer, "Control register: 0x%x\n", hdsp->control_register); + snd_iprintf(buffer, "Control2 register: 0x%x\n", + hdsp->control2_register); + snd_iprintf(buffer, "Status register: 0x%x\n", status); + snd_iprintf(buffer, "Status2 register: 0x%x\n", status2); + + if (hdsp_check_for_iobox(hdsp)) { + snd_iprintf(buffer, "No I/O box connected.\n" + "Please connect one and upload firmware.\n"); return; - } + } if (hdsp_check_for_firmware(hdsp, 0)) { if (hdsp->state & HDSP_FirmwareCached) { if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) { - snd_iprintf(buffer, "Firmware loading from cache failed, please upload manually.\n"); + snd_iprintf(buffer, "Firmware loading from " + "cache failed, " + "please upload manually.\n"); return; } } else { @@ -3319,18 +3337,6 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) } } - status = hdsp_read(hdsp, HDSP_statusRegister); - status2 = hdsp_read(hdsp, HDSP_status2Register); - - snd_iprintf(buffer, "%s (Card #%d)\n", hdsp->card_name, hdsp->card->number + 1); - snd_iprintf(buffer, "Buffers: capture %p playback %p\n", - hdsp->capture_buffer, hdsp->playback_buffer); - snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", - hdsp->irq, hdsp->port, (unsigned long)hdsp->iobase); - snd_iprintf(buffer, "Control register: 0x%x\n", hdsp->control_register); - snd_iprintf(buffer, "Control2 register: 0x%x\n", hdsp->control2_register); - snd_iprintf(buffer, "Status register: 0x%x\n", status); - snd_iprintf(buffer, "Status2 register: 0x%x\n", status2); snd_iprintf(buffer, "FIFO status: %d\n", hdsp_read(hdsp, HDSP_fifoStatus) & 0xff); snd_iprintf(buffer, "MIDI1 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut0)); snd_iprintf(buffer, "MIDI1 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn0)); @@ -3351,7 +3357,6 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) snd_iprintf(buffer, "\n"); - switch (hdsp_clock_source(hdsp)) { case HDSP_CLOCK_SOURCE_AUTOSYNC: clock_source = "AutoSync"; diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index acfa4760da4..91683a34903 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1626,7 +1626,7 @@ static int snd_via8233_dxs_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct via82xx *chip = snd_kcontrol_chip(kcontrol); - unsigned int idx = snd_ctl_get_ioff(kcontrol, &ucontrol->id); + unsigned int idx = kcontrol->id.subdevice; ucontrol->value.integer.value[0] = VIA_DXS_MAX_VOLUME - chip->playback_volume[idx][0]; ucontrol->value.integer.value[1] = VIA_DXS_MAX_VOLUME - chip->playback_volume[idx][1]; @@ -1646,7 +1646,7 @@ static int snd_via8233_dxs_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct via82xx *chip = snd_kcontrol_chip(kcontrol); - unsigned int idx = snd_ctl_get_ioff(kcontrol, &ucontrol->id); + unsigned int idx = kcontrol->id.subdevice; unsigned long port = chip->port + 0x10 * idx; unsigned char val; int i, change = 0; @@ -1705,11 +1705,12 @@ static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control __devinitdata = }; static struct snd_kcontrol_new snd_via8233_dxs_volume_control __devinitdata = { - .name = "VIA DXS Playback Volume", - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .device = 0, + /* .subdevice set later */ + .name = "PCM Playback Volume", .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ), - .count = 4, .info = snd_via8233_dxs_volume_info, .get = snd_via8233_dxs_volume_get, .put = snd_via8233_dxs_volume_put, @@ -1936,10 +1937,18 @@ static int __devinit snd_via8233_init_misc(struct via82xx *chip) } else /* Using DXS when PCM emulation is enabled is really weird */ { - /* Standalone DXS controls */ - err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs_volume_control, chip)); - if (err < 0) - return err; + for (i = 0; i < 4; ++i) { + struct snd_kcontrol *kctl; + + kctl = snd_ctl_new1( + &snd_via8233_dxs_volume_control, chip); + if (!kctl) + return -ENOMEM; + kctl->id.subdevice = i; + err = snd_ctl_add(chip->card, kctl); + if (err < 0) + return err; + } } } /* select spdif data slot 10/11 */ diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 2f0925236a1..5518371db13 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -834,7 +834,7 @@ static irqreturn_t snd_ymfpci_interrupt(int irq, void *dev_id) status = snd_ymfpci_readw(chip, YDSXGR_INTFLAG); if (status & 1) { if (chip->timer) - snd_timer_interrupt(chip->timer, chip->timer->sticks); + snd_timer_interrupt(chip->timer, chip->timer_ticks); } snd_ymfpci_writew(chip, YDSXGR_INTFLAG, status); @@ -1885,8 +1885,18 @@ static int snd_ymfpci_timer_start(struct snd_timer *timer) unsigned int count; chip = snd_timer_chip(timer); - count = (timer->sticks << 1) - 1; spin_lock_irqsave(&chip->reg_lock, flags); + if (timer->sticks > 1) { + chip->timer_ticks = timer->sticks; + count = timer->sticks - 1; + } else { + /* + * Divisor 1 is not allowed; fake it by using divisor 2 and + * counting two ticks for each interrupt. + */ + chip->timer_ticks = 2; + count = 2 - 1; + } snd_ymfpci_writew(chip, YDSXGR_TIMERCOUNT, count); snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x03); spin_unlock_irqrestore(&chip->reg_lock, flags); @@ -1909,14 +1919,14 @@ static int snd_ymfpci_timer_precise_resolution(struct snd_timer *timer, unsigned long *num, unsigned long *den) { *num = 1; - *den = 48000; + *den = 96000; return 0; } static struct snd_timer_hardware snd_ymfpci_timer_hw = { .flags = SNDRV_TIMER_HW_AUTO, - .resolution = 20833, /* 1/fs = 20.8333...us */ - .ticks = 0x8000, + .resolution = 10417, /* 1 / 96 kHz = 10.41666...us */ + .ticks = 0x10000, .start = snd_ymfpci_timer_start, .stop = snd_ymfpci_timer_stop, .precise_resolution = snd_ymfpci_timer_precise_resolution, |