diff options
Diffstat (limited to 'sound/pci')
86 files changed, 5763 insertions, 1754 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 748f6b7d90b7..75c602b5b132 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. @@ -259,7 +259,6 @@ config SND_CS5530 config SND_CS5535AUDIO tristate "CS5535/CS5536 Audio" - depends on X86 && !X86_64 select SND_PCM select SND_AC97_CODEC help diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 78288dbfc17a..20cb60afb200 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -603,8 +603,8 @@ AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1) }; static const struct snd_kcontrol_new snd_ac97_controls_pc_beep[2] = { -AC97_SINGLE("PC Speaker Playback Switch", AC97_PC_BEEP, 15, 1, 1), -AC97_SINGLE("PC Speaker Playback Volume", AC97_PC_BEEP, 1, 15, 1) +AC97_SINGLE("Beep Playback Switch", AC97_PC_BEEP, 15, 1, 1), +AC97_SINGLE("Beep Playback Volume", AC97_PC_BEEP, 1, 15, 1) }; static const struct snd_kcontrol_new snd_ac97_controls_mic_boost = @@ -1393,7 +1393,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) } } - /* build PC Speaker controls */ + /* build Beep controls */ if (!(ac97->flags & AC97_HAS_NO_PC_BEEP) && ((ac97->flags & AC97_HAS_PC_BEEP) || snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) { diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 7337abdbe4e3..139cf3b2b9d7 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -800,12 +800,12 @@ AC97_SINGLE("Mono Switch", AC97_MASTER_TONE, 7, 1, 1), AC97_SINGLE("Mono ZC Switch", AC97_MASTER_TONE, 6, 1, 0), AC97_SINGLE("Mono Volume", AC97_MASTER_TONE, 0, 31, 1), -AC97_SINGLE("PC Beep to Headphone Switch", AC97_AUX, 15, 1, 1), -AC97_SINGLE("PC Beep to Headphone Volume", AC97_AUX, 12, 7, 1), -AC97_SINGLE("PC Beep to Master Switch", AC97_AUX, 11, 1, 1), -AC97_SINGLE("PC Beep to Master Volume", AC97_AUX, 8, 7, 1), -AC97_SINGLE("PC Beep to Mono Switch", AC97_AUX, 7, 1, 1), -AC97_SINGLE("PC Beep to Mono Volume", AC97_AUX, 4, 7, 1), +AC97_SINGLE("Beep to Headphone Switch", AC97_AUX, 15, 1, 1), +AC97_SINGLE("Beep to Headphone Volume", AC97_AUX, 12, 7, 1), +AC97_SINGLE("Beep to Master Switch", AC97_AUX, 11, 1, 1), +AC97_SINGLE("Beep to Master Volume", AC97_AUX, 8, 7, 1), +AC97_SINGLE("Beep to Mono Switch", AC97_AUX, 7, 1, 1), +AC97_SINGLE("Beep to Mono Volume", AC97_AUX, 4, 7, 1), AC97_SINGLE("Voice to Headphone Switch", AC97_PCM, 15, 1, 1), AC97_SINGLE("Voice to Headphone Volume", AC97_PCM, 12, 7, 1), diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index c551006e2920..aaf4da68969c 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -310,12 +310,16 @@ static int snd_ali_codec_ready(struct snd_ali *codec, unsigned int res; end_time = jiffies + msecs_to_jiffies(250); - do { + + for (;;) { res = snd_ali_5451_peek(codec,port); if (!(res & 0x8000)) return 0; + if (!time_after_eq(end_time, jiffies)) + break; schedule_timeout_uninterruptible(1); - } while (time_after_eq(end_time, jiffies)); + } + snd_ali_5451_poke(codec, port, res & ~0x8000); snd_printdd("ali_codec_ready: codec is not ready.\n "); return -EIO; @@ -327,15 +331,17 @@ static int snd_ali_stimer_ready(struct snd_ali *codec) unsigned long dwChk1,dwChk2; dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER); - dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); - end_time = jiffies + msecs_to_jiffies(250); - do { + + for (;;) { dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); if (dwChk2 != dwChk1) return 0; + if (!time_after_eq(end_time, jiffies)) + break; schedule_timeout_uninterruptible(1); - } while (time_after_eq(end_time, jiffies)); + } + snd_printk(KERN_ERR "ali_stimer_read: stimer is not ready.\n"); return -EIO; } @@ -472,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 */ @@ -555,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) { @@ -671,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); @@ -1032,7 +973,7 @@ static void snd_ali_free_voice(struct snd_ali * codec, void *private_data; snd_ali_printk("free_voice: channel=%d\n",pvoice->number); - if (pvoice == NULL || !pvoice->use) + if (!pvoice->use) return; snd_ali_clear_voices(codec, pvoice->number, pvoice->number); spin_lock_irq(&codec->voice_alloc); diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 71515ddb4593..d6752dff2a44 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -287,10 +287,10 @@ struct atiixp { /* */ static struct pci_device_id snd_atiixp_ids[] = { - { 0x1002, 0x4341, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */ - { 0x1002, 0x4361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB300 */ - { 0x1002, 0x4370, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB400 */ - { 0x1002, 0x4382, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB600 */ + { PCI_VDEVICE(ATI, 0x4341), 0 }, /* SB200 */ + { PCI_VDEVICE(ATI, 0x4361), 0 }, /* SB300 */ + { PCI_VDEVICE(ATI, 0x4370), 0 }, /* SB400 */ + { PCI_VDEVICE(ATI, 0x4382), 0 }, /* SB600 */ { 0, } }; diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index c3136cccc559..e7e147bf8eb2 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -262,8 +262,8 @@ struct atiixp_modem { /* */ static struct pci_device_id snd_atiixp_ids[] = { - { 0x1002, 0x434d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */ - { 0x1002, 0x4378, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB400 */ + { PCI_VDEVICE(ATI, 0x434d), 0 }, /* SB200 */ + { PCI_VDEVICE(ATI, 0x4378), 0 }, /* SB400 */ { 0, } }; diff --git a/sound/pci/au88x0/au8810.c b/sound/pci/au88x0/au8810.c index fce22c7af0ea..c0e8c6b295cb 100644 --- a/sound/pci/au88x0/au8810.c +++ b/sound/pci/au88x0/au8810.c @@ -1,8 +1,7 @@ #include "au8810.h" #include "au88x0.h" static struct pci_device_id snd_vortex_ids[] = { - {PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_ADVANTAGE, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1,}, + {PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_ADVANTAGE), 1,}, {0,} }; diff --git a/sound/pci/au88x0/au8820.c b/sound/pci/au88x0/au8820.c index d1fbcce07257..a6527330df58 100644 --- a/sound/pci/au88x0/au8820.c +++ b/sound/pci/au88x0/au8820.c @@ -1,8 +1,7 @@ #include "au8820.h" #include "au88x0.h" static struct pci_device_id snd_vortex_ids[] = { - {PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_1, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,}, + {PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_1), 0,}, {0,} }; diff --git a/sound/pci/au88x0/au8830.c b/sound/pci/au88x0/au8830.c index d4f2717c14fb..6c702ad4352a 100644 --- a/sound/pci/au88x0/au8830.c +++ b/sound/pci/au88x0/au8830.c @@ -1,8 +1,7 @@ #include "au8830.h" #include "au88x0.h" static struct pci_device_id snd_vortex_ids[] = { - {PCI_VENDOR_ID_AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,}, + {PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_2), 0,}, {0,} }; diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index f290bc56178f..69867ace7860 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); @@ -753,8 +830,8 @@ static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = { AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0), AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1), AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1), - AZF3328_MIXER_SWITCH("PC Speaker Playback Switch", IDX_MIXER_PCBEEP, 15, 1), - AZF3328_MIXER_VOL_SPECIAL("PC Speaker Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1), + AZF3328_MIXER_SWITCH("Beep Playback Switch", IDX_MIXER_PCBEEP, 15, 1), + AZF3328_MIXER_VOL_SPECIAL("Beep Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1), AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1), AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1), AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1), @@ -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 974e05122f00..6f46b97650cc 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 24585c6c6d01..4e2b925a94cc 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/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 57b992a5c057..15e4138bce17 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -325,9 +325,9 @@ static struct snd_pcm_hardware snd_ca0106_capture_hw = { .rate_max = 192000, .channels_min = 2, .channels_max = 2, - .buffer_bytes_max = ((65536 - 64) * 8), + .buffer_bytes_max = 65536 - 128, .period_bytes_min = 64, - .period_bytes_max = (65536 - 64), + .period_bytes_max = 32768 - 64, .periods_min = 2, .periods_max = 2, .fifo_size = 0, @@ -1876,7 +1876,7 @@ static int snd_ca0106_resume(struct pci_dev *pci) // PCI IDs static struct pci_device_id snd_ca0106_ids[] = { - { 0x1102, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Audigy LS or Live 24bit */ + { PCI_VDEVICE(CREATIVE, 0x0007), 0 }, /* Audigy LS or Live 24bit */ { 0, } }; MODULE_DEVICE_TABLE(pci, snd_ca0106_ids); diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index c8c6f437f5b3..8f443a9d61ec 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -792,8 +792,8 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) "Phone Playback Volume", "Video Playback Switch", "Video Playback Volume", - "PC Speaker Playback Switch", - "PC Speaker Playback Volume", + "Beep Playback Switch", + "Beep Playback Volume", "Mono Output Select", "Capture Source", "Capture Switch", diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c index c62b7d10ec61..15523e60351c 100644 --- a/sound/pci/ca0106/ca0106_proc.c +++ b/sound/pci/ca0106/ca0106_proc.c @@ -304,7 +304,7 @@ static void snd_ca0106_proc_reg_write32(struct snd_info_entry *entry, while (!snd_info_get_line(buffer, line, sizeof(line))) { if (sscanf(line, "%x %x", ®, &val) != 2) continue; - if ((reg < 0x40) && (reg >=0) && (val <= 0xffffffff) ) { + if (reg < 0x40 && val <= 0xffffffff) { spin_lock_irqsave(&emu->emu_lock, flags); outl(val, emu->port + (reg & 0xfffffffc)); spin_unlock_irqrestore(&emu->emu_lock, flags); @@ -405,7 +405,7 @@ static void snd_ca0106_proc_reg_write(struct snd_info_entry *entry, while (!snd_info_get_line(buffer, line, sizeof(line))) { if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3) continue; - if ((reg < 0x80) && (reg >=0) && (val <= 0xffffffff) && (channel_id >=0) && (channel_id <= 3) ) + if (reg < 0x80 && val <= 0xffffffff && channel_id <= 3) snd_ca0106_ptr_write(emu, reg, channel_id, val); } } diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 449fe02f666e..a312bae08f52 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2302,7 +2302,7 @@ static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = { CMIPCI_SB_VOL_MONO("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31), CMIPCI_SB_SW_MONO("Mic Playback Switch", 0), CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0), - CMIPCI_SB_VOL_MONO("PC Speaker Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3), + CMIPCI_SB_VOL_MONO("Beep Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3), CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15), CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0), CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0), @@ -2310,7 +2310,7 @@ static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = { CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7), CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7), CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0), - CMIPCI_DOUBLE("PC Speaker Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0), + CMIPCI_DOUBLE("Beep Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0), CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0), }; @@ -2797,11 +2797,11 @@ static inline void snd_cmipci_proc_init(struct cmipci *cm) {} static struct pci_device_id snd_cmipci_ids[] = { - {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_AL, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A), 0}, + {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B), 0}, + {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738), 0}, + {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738B), 0}, + {PCI_VDEVICE(AL, PCI_DEVICE_ID_CMEDIA_CM8738), 0}, {0,}, }; diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index f6286f84a221..e2e0359bb056 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -495,7 +495,7 @@ struct cs4281 { static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id); static struct pci_device_id snd_cs4281_ids[] = { - { 0x1013, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4281 */ + { PCI_VDEVICE(CIRRUS, 0x6005), 0, }, /* CS4281 */ { 0, } }; diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index c9b3e3d48cbc..033aec430117 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -65,9 +65,9 @@ module_param_array(mmap_valid, bool, NULL, 0444); MODULE_PARM_DESC(mmap_valid, "Support OSS mmap."); static struct pci_device_id snd_cs46xx_ids[] = { - { 0x1013, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4280 */ - { 0x1013, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4612 */ - { 0x1013, 0x6004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4615 */ + { PCI_VDEVICE(CIRRUS, 0x6001), 0, }, /* CS4280 */ + { PCI_VDEVICE(CIRRUS, 0x6003), 0, }, /* CS4612 */ + { PCI_VDEVICE(CIRRUS, 0x6004), 0, }, /* CS4615 */ { 0, } }; diff --git a/sound/pci/cs46xx/cs46xx_lib.h b/sound/pci/cs46xx/cs46xx_lib.h index 4eb55aa33612..b5189495d58a 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 2d07986f57cc..e0394e3996e8 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 a1db51b3ead8..fee35cfc0c7f 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; @@ -242,13 +242,12 @@ static int get_amixer_rsc(struct amixer_mgr *mgr, /* Allocate mem for amixer resource */ amixer = kzalloc(sizeof(*amixer), GFP_KERNEL); - if (NULL == amixer) { - err = -ENOMEM; - return err; - } + if (!amixer) + return -ENOMEM; /* Check whether there are sufficient * amixer resources to meet request. */ + err = 0; spin_lock_irqsave(&mgr->mgr_lock, flags); for (i = 0; i < desc->msr; i++) { err = mgr_get_resource(&mgr->mgr, 1, &idx); @@ -304,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); @@ -397,12 +396,11 @@ static int get_sum_rsc(struct sum_mgr *mgr, /* Allocate mem for sum resource */ sum = kzalloc(sizeof(*sum), GFP_KERNEL); - if (NULL == sum) { - err = -ENOMEM; - return err; - } + if (!sum) + return -ENOMEM; /* Check whether there are sufficient sum resources to meet request. */ + err = 0; spin_lock_irqsave(&mgr->mgr_lock, flags); for (i = 0; i < desc->msr; i++) { err = mgr_get_resource(&mgr->mgr, 1, &idx); @@ -458,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 a49c76647307..cb65bd0dd35b 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 <= 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 082e35c08c02..af56eb949bde 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -57,9 +57,9 @@ struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = { struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = { [LINEO1] = {.left = 0x40, .right = 0x41}, - [LINEO2] = {.left = 0x70, .right = 0x71}, + [LINEO2] = {.left = 0x60, .right = 0x61}, [LINEO3] = {.left = 0x50, .right = 0x51}, - [LINEO4] = {.left = 0x60, .right = 0x61}, + [LINEO4] = {.left = 0x70, .right = 0x71}, [LINEIM] = {.left = 0x45, .right = 0xc5}, [SPDIFOO] = {.left = 0x00, .right = 0x01}, [SPDIFIO] = {.left = 0x05, .right = 0x85}, @@ -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 ad3e1d144464..0cf400f879f9 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 dec46d04b041..b6b11bfe7574 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 f26d7cd9db9f..15c1e7271ea8 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 60ea23180acb..d0dc227fbdd3 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 889c495bb7d1..7dfaf67344d4 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 e1c145d8b702..c749fa720889 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; } @@ -724,12 +724,11 @@ static int get_srcimp_rsc(struct srcimp_mgr *mgr, /* Allocate mem for SRCIMP resource */ srcimp = kzalloc(sizeof(*srcimp), GFP_KERNEL); - if (NULL == srcimp) { - err = -ENOMEM; - return err; - } + if (!srcimp) + return -ENOMEM; /* Check whether there are sufficient SRCIMP resources. */ + err = 0; spin_lock_irqsave(&mgr->mgr_lock, flags); for (i = 0; i < desc->msr; i++) { err = mgr_get_resource(&mgr->mgr, 1, &idx); @@ -834,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); @@ -845,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 67665a7e43c6..6b78752e9503 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 da2065cd2c0d..1305f7ca02c3 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 f3b9b45c9c1b..f05c8c097aa8 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/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index c7f3b994101c..168af67d938e 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -77,9 +77,9 @@ MODULE_PARM_DESC(subsystem, "Force card subsystem model."); * Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value Model:SB0400 */ static struct pci_device_id snd_emu10k1_ids[] = { - { 0x1102, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* EMU10K1 */ - { 0x1102, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, /* Audigy */ - { 0x1102, 0x0008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, /* Audigy 2 Value SB0400 */ + { PCI_VDEVICE(CREATIVE, 0x0002), 0 }, /* EMU10K1 */ + { PCI_VDEVICE(CREATIVE, 0x0004), 1 }, /* Audigy */ + { PCI_VDEVICE(CREATIVE, 0x0008), 1 }, /* Audigy 2 Value SB0400 */ { 0, } }; diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 4d3ad793e98f..6b8ae7b5cd54 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -1040,8 +1040,7 @@ static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry, if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3) continue; - if ((reg < 0x49) && (reg >= 0) && (val <= 0xffffffff) - && (channel_id >= 0) && (channel_id <= 2) ) + if (reg < 0x49 && val <= 0xffffffff && channel_id <= 2) snd_emu10k1x_ptr_write(emu, reg, channel_id, val); } } @@ -1607,7 +1606,7 @@ static void __devexit snd_emu10k1x_remove(struct pci_dev *pci) // PCI IDs static struct pci_device_id snd_emu10k1x_ids[] = { - { 0x1102, 0x0006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Dell OEM version (EMU10K1) */ + { PCI_VDEVICE(CREATIVE, 0x0006), 0 }, /* Dell OEM version (EMU10K1) */ { 0, } }; MODULE_DEVICE_TABLE(pci, snd_emu10k1x_ids); diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index b0fb6c917c38..05afe06e353a 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -1818,8 +1818,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, "Master Playback Switch", "Master Capture Switch", "Master Playback Volume", "Master Capture Volume", "Wave Master Playback Volume", "Master Playback Volume", - "PC Speaker Playback Switch", "PC Speaker Capture Switch", - "PC Speaker Playback Volume", "PC Speaker Capture Volume", + "Beep Playback Switch", "Beep Capture Switch", + "Beep Playback Volume", "Beep Capture Volume", "Phone Playback Switch", "Phone Capture Switch", "Phone Playback Volume", "Phone Capture Volume", "Mic Playback Switch", "Mic Capture Switch", diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index 216f9748aff5..baa7cd508cd8 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -451,7 +451,7 @@ static void snd_emu_proc_io_reg_write(struct snd_info_entry *entry, while (!snd_info_get_line(buffer, line, sizeof(line))) { if (sscanf(line, "%x %x", ®, &val) != 2) continue; - if ((reg < 0x40) && (reg >= 0) && (val <= 0xffffffff) ) { + if (reg < 0x40 && val <= 0xffffffff) { spin_lock_irqsave(&emu->emu_lock, flags); outl(val, emu->port + (reg & 0xfffffffc)); spin_unlock_irqrestore(&emu->emu_lock, flags); @@ -527,7 +527,7 @@ static void snd_emu_proc_ptr_reg_write(struct snd_info_entry *entry, while (!snd_info_get_line(buffer, line, sizeof(line))) { if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3) continue; - if ((reg < 0xa0) && (reg >= 0) && (val <= 0xffffffff) && (channel_id >= 0) && (channel_id <= 3) ) + if (reg < 0xa0 && val <= 0xffffffff && channel_id <= 3) snd_ptr_write(emu, iobase, reg, channel_id, val); } } diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index c1a5aa15af8f..5ef7080e14d0 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -256,7 +256,7 @@ int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value) if (reg > 0x3f) return 1; reg += 0x40; /* 0x40 upwards are registers. */ - if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */ + if (value > 0x3f) /* 0 to 0x3f are values */ return 1; spin_lock_irqsave(&emu->emu_lock, flags); outl(reg, emu->port + A_IOCFG); diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index e617acaf10e3..61b8ab39800f 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -644,7 +644,7 @@ int __devinit snd_p16v_pcm(struct snd_emu10k1 *emu, int device, struct snd_pcm * int err; int capture=1; - /* snd_printk("KERN_DEBUG snd_p16v_pcm called. device=%d\n", device); */ + /* snd_printk(KERN_DEBUG "snd_p16v_pcm called. device=%d\n", device); */ emu->p16v_device_offset = device; if (rpcm) *rpcm = NULL; diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 18f4d1e98c46..2b82c5c723e1 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -445,12 +445,12 @@ static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id); static struct pci_device_id snd_audiopci_ids[] = { #ifdef CHIP1370 - { 0x1274, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1370 */ + { PCI_VDEVICE(ENSONIQ, 0x5000), 0, }, /* ES1370 */ #endif #ifdef CHIP1371 - { 0x1274, 0x1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1371 */ - { 0x1274, 0x5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1373 - CT5880 */ - { 0x1102, 0x8938, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Ectiva EV1938 */ + { PCI_VDEVICE(ENSONIQ, 0x1371), 0, }, /* ES1371 */ + { PCI_VDEVICE(ENSONIQ, 0x5880), 0, }, /* ES1373 - CT5880 */ + { PCI_VDEVICE(ECTIVA, 0x8938), 0, }, /* Ectiva EV1938 */ #endif { 0, } }; diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index fbd2ac09aa34..fb83e1ffa5cb 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -244,7 +244,7 @@ struct es1938 { static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id); static struct pci_device_id snd_es1938_ids[] = { - { 0x125d, 0x1969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Solo-1 */ + { PCI_VDEVICE(ESS, 0x1969), 0, }, /* Solo-1 */ { 0, } }; @@ -1387,7 +1387,7 @@ ES1938_DOUBLE_TLV("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0, db_scale_line), ES1938_DOUBLE_TLV("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0, db_scale_capture), -ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0), +ES1938_SINGLE("Beep Volume", 0, 0x3c, 0, 7, 0), ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0), ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1), { diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 55545e0818b5..25ae10e16f59 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -38,6 +38,17 @@ config SND_HDA_INPUT_BEEP Say Y here to build a digital beep interface for HD-audio driver. This interface is used to generate digital beeps. +config SND_HDA_INPUT_BEEP_MODE + int "Digital beep registration mode (0=off, 1=on, 2=mute sw on/off)" + depends on SND_HDA_INPUT_BEEP=y + default "1" + range 0 2 + help + Set 0 to disable the digital beep interface for HD-audio by default. + Set 1 to always enable the digital beep interface for HD-audio by + default. Set 2 to control the beep device registration to input + layer using a "Beep Switch" in mixer applications. + config SND_HDA_INPUT_JACK bool "Support jack plugging notification via input layer" depends on INPUT=y || INPUT=SND_HDA_INTEL diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 3f51a981e604..5fe34a8d8c81 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -113,23 +113,25 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type, return 0; } -int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) +static void snd_hda_do_detach(struct hda_beep *beep) +{ + input_unregister_device(beep->dev); + beep->dev = NULL; + cancel_work_sync(&beep->beep_work); + /* turn off beep for sure */ + snd_hda_codec_write_cache(beep->codec, beep->nid, 0, + AC_VERB_SET_BEEP_CONTROL, 0); +} + +static int snd_hda_do_attach(struct hda_beep *beep) { struct input_dev *input_dev; - struct hda_beep *beep; + struct hda_codec *codec = beep->codec; 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; - snprintf(beep->phys, sizeof(beep->phys), - "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); input_dev = input_allocate_device(); if (!input_dev) { - kfree(beep); + printk(KERN_INFO "hda_beep: unable to allocate input device\n"); return -ENOMEM; } @@ -151,21 +153,96 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) err = input_register_device(input_dev); if (err < 0) { input_free_device(input_dev); - kfree(beep); + printk(KERN_INFO "hda_beep: unable to register input device\n"); return err; } + beep->dev = input_dev; + return 0; +} + +static void snd_hda_do_register(struct work_struct *work) +{ + struct hda_beep *beep = + container_of(work, struct hda_beep, register_work); + + mutex_lock(&beep->mutex); + if (beep->enabled && !beep->dev) + snd_hda_do_attach(beep); + mutex_unlock(&beep->mutex); +} + +static void snd_hda_do_unregister(struct work_struct *work) +{ + struct hda_beep *beep = + container_of(work, struct hda_beep, unregister_work.work); + + mutex_lock(&beep->mutex); + if (!beep->enabled && beep->dev) + snd_hda_do_detach(beep); + mutex_unlock(&beep->mutex); +} +int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) +{ + struct hda_beep *beep = codec->beep; + enable = !!enable; + if (beep == NULL) + return 0; + if (beep->enabled != enable) { + beep->enabled = enable; + if (!enable) { + /* turn off beep */ + snd_hda_codec_write_cache(beep->codec, beep->nid, 0, + AC_VERB_SET_BEEP_CONTROL, 0); + } + if (beep->mode == HDA_BEEP_MODE_SWREG) { + if (enable) { + cancel_delayed_work(&beep->unregister_work); + schedule_work(&beep->register_work); + } else { + schedule_delayed_work(&beep->unregister_work, + HZ); + } + } + return 1; + } + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device); + +int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) +{ + struct hda_beep *beep; + + if (!snd_hda_get_bool_hint(codec, "beep")) + return 0; /* disabled explicitly by hints */ + if (codec->beep_mode == HDA_BEEP_MODE_OFF) + return 0; /* disabled by module option */ + + beep = kzalloc(sizeof(*beep), GFP_KERNEL); + if (beep == NULL) + return -ENOMEM; + snprintf(beep->phys, sizeof(beep->phys), + "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); /* enable linear scale */ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, 0x01); beep->nid = nid; - beep->dev = input_dev; beep->codec = codec; - beep->enabled = 1; + beep->mode = codec->beep_mode; codec->beep = beep; + INIT_WORK(&beep->register_work, &snd_hda_do_register); + INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister); INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); + mutex_init(&beep->mutex); + + if (beep->mode == HDA_BEEP_MODE_ON) { + beep->enabled = 1; + snd_hda_do_register(&beep->register_work); + } + return 0; } EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device); @@ -174,11 +251,12 @@ void snd_hda_detach_beep_device(struct hda_codec *codec) { struct hda_beep *beep = codec->beep; if (beep) { - cancel_work_sync(&beep->beep_work); - - input_unregister_device(beep->dev); - kfree(beep); + cancel_work_sync(&beep->register_work); + cancel_delayed_work(&beep->unregister_work); + if (beep->enabled) + snd_hda_do_detach(beep); codec->beep = NULL; + kfree(beep); } } EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device); diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index 0c3de787c717..f1de1bac042c 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -24,19 +24,29 @@ #include "hda_codec.h" +#define HDA_BEEP_MODE_OFF 0 +#define HDA_BEEP_MODE_ON 1 +#define HDA_BEEP_MODE_SWREG 2 + /* beep information */ struct hda_beep { struct input_dev *dev; struct hda_codec *codec; + unsigned int mode; char phys[32]; int tone; hda_nid_t nid; unsigned int enabled:1; + unsigned int request_enable:1; unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */ + struct work_struct register_work; /* registration work */ + struct delayed_work unregister_work; /* unregistration work */ struct work_struct beep_work; /* scheduled task for beep event */ + struct mutex mutex; }; #ifdef CONFIG_SND_HDA_INPUT_BEEP +int snd_hda_enable_beep_device(struct hda_codec *codec, int enable); int snd_hda_attach_beep_device(struct hda_codec *codec, int nid); void snd_hda_detach_beep_device(struct hda_codec *codec); #else diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index af989f660cca..2be61b31fb3c 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -30,6 +30,7 @@ #include <sound/tlv.h> #include <sound/initval.h> #include "hda_local.h" +#include "hda_beep.h" #include <sound/hda_hwdep.h> /* @@ -93,6 +94,13 @@ static void hda_keep_power_on(struct hda_codec *codec); static inline void hda_keep_power_on(struct hda_codec *codec) {} #endif +/** + * snd_hda_get_jack_location - Give a location string of the jack + * @cfg: pin default config value + * + * Parse the pin default config value and returns the string of the + * jack location, e.g. "Rear", "Front", etc. + */ const char *snd_hda_get_jack_location(u32 cfg) { static char *bases[7] = { @@ -120,6 +128,13 @@ const char *snd_hda_get_jack_location(u32 cfg) } EXPORT_SYMBOL_HDA(snd_hda_get_jack_location); +/** + * snd_hda_get_jack_connectivity - Give a connectivity string of the jack + * @cfg: pin default config value + * + * Parse the pin default config value and returns the string of the + * jack connectivity, i.e. external or internal connection. + */ const char *snd_hda_get_jack_connectivity(u32 cfg) { static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; @@ -128,6 +143,13 @@ const char *snd_hda_get_jack_connectivity(u32 cfg) } EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity); +/** + * snd_hda_get_jack_type - Give a type string of the jack + * @cfg: pin default config value + * + * Parse the pin default config value and returns the string of the + * jack type, i.e. the purpose of the jack, such as Line-Out or CD. + */ const char *snd_hda_get_jack_type(u32 cfg) { static char *jack_types[16] = { @@ -515,6 +537,7 @@ static int snd_hda_bus_dev_register(struct snd_device *device) struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, list) { snd_hda_hwdep_add_sysfs(codec); + snd_hda_hwdep_add_power_sysfs(codec); } return 0; } @@ -820,6 +843,16 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, return 0; } +/** + * snd_hda_codec_set_pincfg - Override a pin default configuration + * @codec: the HDA codec + * @nid: NID to set the pin config + * @cfg: the pin default config value + * + * Override a pin default configuration value in the cache. + * This value can be read by snd_hda_codec_get_pincfg() in a higher + * priority than the real hardware value. + */ int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid, unsigned int cfg) { @@ -827,7 +860,15 @@ int snd_hda_codec_set_pincfg(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg); -/* get the current pin config value of the given pin NID */ +/** + * snd_hda_codec_get_pincfg - Obtain a pin-default configuration + * @codec: the HDA codec + * @nid: NID to get the pin config + * + * Get the current pin config value of the given pin NID. + * If the pincfg value is cached or overridden via sysfs or driver, + * returns the cached value. + */ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) { struct hda_pincfg *pin; @@ -944,7 +985,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr mutex_init(&codec->control_mutex); init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); - snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32); + snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 60); snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); if (codec->bus->modelname) { @@ -1026,6 +1067,15 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr } EXPORT_SYMBOL_HDA(snd_hda_codec_new); +/** + * snd_hda_codec_configure - (Re-)configure the HD-audio codec + * @codec: the HDA codec + * + * Start parsing of the given codec tree and (re-)initialize the whole + * patch instance. + * + * Returns 0 if successful or a negative error code. + */ int snd_hda_codec_configure(struct hda_codec *codec) { int err; @@ -1088,6 +1138,11 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); +/** + * snd_hda_codec_cleanup_stream - clean up the codec for closing + * @codec: the CODEC to clean up + * @nid: the NID to clean up + */ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) { if (!nid) @@ -1163,8 +1218,17 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key) return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key); } -/* - * query AMP capabilities for the given widget and direction +/** + * query_amp_caps - query AMP capabilities + * @codec: the HD-auio codec + * @nid: the NID to query + * @direction: either #HDA_INPUT or #HDA_OUTPUT + * + * Query AMP capabilities for the given widget and direction. + * Returns the obtained capability bits. + * + * When cap bits have been already read, this doesn't read again but + * returns the cached value. */ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) { @@ -1187,6 +1251,19 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) } EXPORT_SYMBOL_HDA(query_amp_caps); +/** + * snd_hda_override_amp_caps - Override the AMP capabilities + * @codec: the CODEC to clean up + * @nid: the NID to clean up + * @direction: either #HDA_INPUT or #HDA_OUTPUT + * @caps: the capability bits to set + * + * Override the cached AMP caps bits value by the given one. + * This function is useful if the driver needs to adjust the AMP ranges, + * e.g. limit to 0dB, etc. + * + * Returns zero if successful or a negative error code. + */ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps) { @@ -1222,6 +1299,17 @@ static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid) return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); } +/** + * snd_hda_query_pin_caps - Query PIN capabilities + * @codec: the HD-auio codec + * @nid: the NID to query + * + * Query PIN capabilities for the given widget. + * Returns the obtained capability bits. + * + * When cap bits have been already read, this doesn't read again but + * returns the cached value. + */ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) { return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid), @@ -1269,8 +1357,15 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, info->vol[ch] = val; } -/* - * read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. +/** + * snd_hda_codec_amp_read - Read AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @index: the index value (only for input direction) + * + * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. */ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index) @@ -1283,8 +1378,18 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read); -/* - * update the AMP value, mask = bit mask to set, val = the value +/** + * snd_hda_codec_amp_update - update the AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the AMP value with a bit mask. + * Returns 0 if the value is unchanged, 1 if changed. */ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val) @@ -1303,8 +1408,17 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update); -/* - * update the AMP stereo with the same mask and value +/** + * snd_hda_codec_amp_stereo - update the AMP stereo values + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the AMP values like snd_hda_codec_amp_update(), but for a + * stereo widget with the same mask and value. */ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int direction, int idx, int mask, int val) @@ -1318,7 +1432,12 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); #ifdef SND_HDA_NEEDS_RESUME -/* resume the all amp commands from the cache */ +/** + * snd_hda_codec_resume_amp - Resume all AMP commands from the cache + * @codec: HD-audio codec + * + * Resume the all amp commands from the cache. + */ void snd_hda_codec_resume_amp(struct hda_codec *codec) { struct hda_amp_info *buffer = codec->amp_cache.buf.list; @@ -1344,7 +1463,12 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); #endif /* SND_HDA_NEEDS_RESUME */ -/* volume */ +/** + * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1400,6 +1524,12 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid, HDA_AMP_VOLMASK, val); } +/** + * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1419,6 +1549,12 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get); +/** + * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1443,6 +1579,12 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put); +/** + * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *_tlv) { @@ -1472,8 +1614,16 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv); -/* - * set (static) TLV for virtual master volume; recalculated as max 0dB +/** + * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control + * @codec: HD-audio codec + * @nid: NID of a reference widget + * @dir: #HDA_INPUT or #HDA_OUTPUT + * @tlv: TLV data to be stored, at least 4 elements + * + * Set (static) TLV data for a virtual master volume using the AMP caps + * obtained from the reference NID. + * The volume range is recalculated as if the max volume is 0dB. */ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int *tlv) @@ -1507,6 +1657,13 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec, return snd_ctl_find_id(codec->bus->card, &id); } +/** + * snd_hda_find_mixer_ctl - Find a mixer control element with the given name + * @codec: HD-audio codec + * @name: ctl id name string + * + * Get the control element with the given id string and IFACE_MIXER. + */ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, const char *name) { @@ -1514,30 +1671,57 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); -/* Add a control element and assign to the codec */ -int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl) +/** + * snd_hda_ctl-add - Add a control element and assign to the codec + * @codec: HD-audio codec + * @nid: corresponding NID (optional) + * @kctl: the control element to assign + * + * Add the given control element to an array inside the codec instance. + * All control elements belonging to a codec are supposed to be added + * by this function so that a proper clean-up works at the free or + * reconfiguration time. + * + * If non-zero @nid is passed, the NID is assigned to the control element. + * The assignment is shown in the codec proc file. + * + * snd_hda_ctl_add() checks the control subdev id field whether + * #HDA_SUBDEV_NID_FLAG bit is set. If set (and @nid is zero), the lower + * bits value is taken as the NID to assign. + */ +int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, + struct snd_kcontrol *kctl) { int err; - struct snd_kcontrol **knewp; + struct hda_nid_item *item; + if (kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) { + if (nid == 0) + nid = kctl->id.subdevice & 0xffff; + kctl->id.subdevice = 0; + } err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) return err; - knewp = snd_array_new(&codec->mixers); - if (!knewp) + item = snd_array_new(&codec->mixers); + if (!item) return -ENOMEM; - *knewp = kctl; + item->kctl = kctl; + item->nid = nid; return 0; } EXPORT_SYMBOL_HDA(snd_hda_ctl_add); -/* Clear all controls assigned to the given codec */ +/** + * snd_hda_ctls_clear - Clear all controls assigned to the given codec + * @codec: HD-audio codec + */ void snd_hda_ctls_clear(struct hda_codec *codec) { int i; - struct snd_kcontrol **kctls = codec->mixers.list; + struct hda_nid_item *items = codec->mixers.list; for (i = 0; i < codec->mixers.used; i++) - snd_ctl_remove(codec->bus->card, kctls[i]); + snd_ctl_remove(codec->bus->card, items[i].kctl); snd_array_free(&codec->mixers); } @@ -1563,6 +1747,16 @@ static void hda_unlock_devices(struct snd_card *card) spin_unlock(&card->files_lock); } +/** + * snd_hda_codec_reset - Clear all objects assigned to the codec + * @codec: HD-audio codec + * + * This frees the all PCM and control elements assigned to the codec, and + * clears the caches and restores the pin default configurations. + * + * When a device is being used, it returns -EBSY. If successfully freed, + * returns zero. + */ int snd_hda_codec_reset(struct hda_codec *codec) { struct snd_card *card = codec->bus->card; @@ -1626,7 +1820,22 @@ int snd_hda_codec_reset(struct hda_codec *codec) return 0; } -/* create a virtual master control and add slaves */ +/** + * snd_hda_add_vmaster - create a virtual master control and add slaves + * @codec: HD-audio codec + * @name: vmaster control name + * @tlv: TLV data (optional) + * @slaves: slave control names (optional) + * + * Create a virtual master control with the given name. The TLV data + * must be either NULL or a valid data. + * + * @slaves is a NULL-terminated array of strings, each of which is a + * slave control name. All controls with these names are assigned to + * the new virtual master control. + * + * This function returns zero if successful or a negative error code. + */ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, unsigned int *tlv, const char **slaves) { @@ -1643,7 +1852,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, kctl = snd_ctl_make_virtual_master(name, tlv); if (!kctl) return -ENOMEM; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; @@ -1668,7 +1877,12 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, } EXPORT_SYMBOL_HDA(snd_hda_add_vmaster); -/* switch */ +/** + * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1682,6 +1896,12 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info); +/** + * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1702,6 +1922,12 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get); +/** + * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1733,6 +1959,25 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put); +#ifdef CONFIG_SND_HDA_INPUT_BEEP +/** + * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch + * + * This function calls snd_hda_enable_beep_device(), which behaves differently + * depending on beep_mode option. + */ +int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + long *valp = ucontrol->value.integer.value; + + snd_hda_enable_beep_device(codec, *valp); + return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); +} +EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep); +#endif /* CONFIG_SND_HDA_INPUT_BEEP */ + /* * bound volume controls * @@ -1742,6 +1987,12 @@ EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put); #define AMP_VAL_IDX_SHIFT 19 #define AMP_VAL_IDX_MASK (0x0f<<19) +/** + * snd_hda_mixer_bind_switch_get - Get callback for a bound volume control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_MUTE*() macros. + */ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1759,6 +2010,12 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get); +/** + * snd_hda_mixer_bind_switch_put - Put callback for a bound volume control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_MUTE*() macros. + */ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1783,8 +2040,11 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put); -/* - * generic bound volume/swtich controls +/** + * snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. */ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -1803,6 +2063,12 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info); +/** + * snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. + */ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1820,6 +2086,12 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get); +/** + * snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. + */ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1843,6 +2115,12 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put); +/** + * snd_hda_mixer_bind_tlv - TLV callback for a generic bound control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_VOL() macro. + */ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv) { @@ -2126,7 +2404,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) return -ENOMEM; kctl->id.index = idx; kctl->private_value = nid; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, nid, kctl); if (err < 0) return err; } @@ -2165,14 +2443,19 @@ static struct snd_kcontrol_new spdif_share_sw = { .put = spdif_share_sw_put, }; +/** + * snd_hda_create_spdif_share_sw - create Default PCM switch + * @codec: the HDA codec + * @mout: multi-out instance + */ int snd_hda_create_spdif_share_sw(struct hda_codec *codec, struct hda_multi_out *mout) { if (!mout->dig_out_nid) return 0; /* ATTENTION: here mout is passed as private_data, instead of codec */ - return snd_hda_ctl_add(codec, - snd_ctl_new1(&spdif_share_sw, mout)); + return snd_hda_ctl_add(codec, mout->dig_out_nid, + snd_ctl_new1(&spdif_share_sw, mout)); } EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw); @@ -2276,7 +2559,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) if (!kctl) return -ENOMEM; kctl->private_value = nid; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, nid, kctl); if (err < 0) return err; } @@ -2332,7 +2615,12 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache); -/* resume the all commands from the cache */ +/** + * snd_hda_codec_resume_cache - Resume the all commands from the cache + * @codec: HD-audio codec + * + * Execute all verbs recorded in the command caches to resume. + */ void snd_hda_codec_resume_cache(struct hda_codec *codec) { struct hda_cache_head *buffer = codec->cmd_cache.buf.list; @@ -2452,9 +2740,11 @@ static void hda_call_codec_suspend(struct hda_codec *codec) codec->afg ? codec->afg : codec->mfg, AC_PWRST_D3); #ifdef CONFIG_SND_HDA_POWER_SAVE + snd_hda_update_power_acct(codec); cancel_delayed_work(&codec->power_work); codec->power_on = 0; codec->power_transition = 0; + codec->power_jiffies = jiffies; #endif } @@ -2756,8 +3046,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, } /** - * snd_hda_is_supported_format - check whether the given node supports - * the format val + * snd_hda_is_supported_format - Check the validity of the format + * @codec: HD-audio codec + * @nid: NID to check + * @format: the HD-audio format value to check + * + * Check whether the given node supports the format value. * * Returns 1 if supported, 0 if not. */ @@ -2877,51 +3171,36 @@ static int set_pcm_default_values(struct hda_codec *codec, return 0; } +/* global */ +const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = { + "Audio", "SPDIF", "HDMI", "Modem" +}; + /* * get the empty PCM device number to assign */ static int get_empty_pcm_device(struct hda_bus *bus, int type) { - static const char *dev_name[HDA_PCM_NTYPES] = { - "Audio", "SPDIF", "HDMI", "Modem" - }; - /* starting device index for each PCM type */ - static int dev_idx[HDA_PCM_NTYPES] = { - [HDA_PCM_TYPE_AUDIO] = 0, - [HDA_PCM_TYPE_SPDIF] = 1, - [HDA_PCM_TYPE_HDMI] = 3, - [HDA_PCM_TYPE_MODEM] = 6 + /* audio device indices; not linear to keep compatibility */ + static int audio_idx[HDA_PCM_NTYPES][5] = { + [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 }, + [HDA_PCM_TYPE_SPDIF] = { 1, -1 }, + [HDA_PCM_TYPE_HDMI] = { 3, 7, 8, 9, -1 }, + [HDA_PCM_TYPE_MODEM] = { 6, -1 }, }; - /* normal audio device indices; not linear to keep compatibility */ - static int audio_idx[4] = { 0, 2, 4, 5 }; - int i, dev; - - switch (type) { - case HDA_PCM_TYPE_AUDIO: - for (i = 0; i < ARRAY_SIZE(audio_idx); i++) { - dev = audio_idx[i]; - if (!test_bit(dev, bus->pcm_dev_bits)) - goto ok; - } - snd_printk(KERN_WARNING "Too many audio devices\n"); - return -EAGAIN; - case HDA_PCM_TYPE_SPDIF: - case HDA_PCM_TYPE_HDMI: - case HDA_PCM_TYPE_MODEM: - dev = dev_idx[type]; - if (test_bit(dev, bus->pcm_dev_bits)) { - snd_printk(KERN_WARNING "%s already defined\n", - dev_name[type]); - return -EAGAIN; - } - break; - default: + int i; + + if (type >= HDA_PCM_NTYPES) { snd_printk(KERN_WARNING "Invalid PCM type %d\n", type); return -EINVAL; } - ok: - set_bit(dev, bus->pcm_dev_bits); - return dev; + + for (i = 0; audio_idx[type][i] >= 0 ; i++) + if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits)) + return audio_idx[type][i]; + + snd_printk(KERN_WARNING "Too many %s devices\n", snd_hda_pcm_type_name[type]); + return -EAGAIN; } /* @@ -3159,14 +3438,14 @@ EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config); */ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) { - int err; + int err; for (; knew->name; knew++) { struct snd_kcontrol *kctl; kctl = snd_ctl_new1(knew, codec); if (!kctl) return -ENOMEM; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) { if (!codec->addr) return err; @@ -3174,7 +3453,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) if (!kctl) return -ENOMEM; kctl->id.device = codec->addr; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; } @@ -3207,8 +3486,27 @@ static void hda_keep_power_on(struct hda_codec *codec) { codec->power_count++; codec->power_on = 1; + codec->power_jiffies = jiffies; +} + +/* update the power on/off account with the current jiffies */ +void snd_hda_update_power_acct(struct hda_codec *codec) +{ + unsigned long delta = jiffies - codec->power_jiffies; + if (codec->power_on) + codec->power_on_acct += delta; + else + codec->power_off_acct += delta; + codec->power_jiffies += delta; } +/** + * snd_hda_power_up - Power-up the codec + * @codec: HD-audio codec + * + * Increment the power-up counter and power up the hardware really when + * not turned on yet. + */ void snd_hda_power_up(struct hda_codec *codec) { struct hda_bus *bus = codec->bus; @@ -3217,7 +3515,9 @@ void snd_hda_power_up(struct hda_codec *codec) if (codec->power_on || codec->power_transition) return; + snd_hda_update_power_acct(codec); codec->power_on = 1; + codec->power_jiffies = jiffies; if (bus->ops.pm_notify) bus->ops.pm_notify(bus); hda_call_codec_resume(codec); @@ -3229,9 +3529,13 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up); #define power_save(codec) \ ((codec)->bus->power_save ? *(codec)->bus->power_save : 0) -#define power_save(codec) \ - ((codec)->bus->power_save ? *(codec)->bus->power_save : 0) - +/** + * snd_hda_power_down - Power-down the codec + * @codec: HD-audio codec + * + * Decrement the power-up counter and schedules the power-off work if + * the counter rearches to zero. + */ void snd_hda_power_down(struct hda_codec *codec) { --codec->power_count; @@ -3245,6 +3549,19 @@ void snd_hda_power_down(struct hda_codec *codec) } EXPORT_SYMBOL_HDA(snd_hda_power_down); +/** + * snd_hda_check_amp_list_power - Check the amp list and update the power + * @codec: HD-audio codec + * @check: the object containing an AMP list and the status + * @nid: NID to check / update + * + * Check whether the given NID is in the amp list. If it's in the list, + * check the current AMP status, and update the the power-status according + * to the mute status. + * + * This function is supposed to be set or called from the check_power_status + * patch ops. + */ int snd_hda_check_amp_list_power(struct hda_codec *codec, struct hda_loopback_check *check, hda_nid_t nid) @@ -3286,6 +3603,10 @@ EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power); /* * Channel mode helper */ + +/** + * snd_hda_ch_mode_info - Info callback helper for the channel mode enum + */ int snd_hda_ch_mode_info(struct hda_codec *codec, struct snd_ctl_elem_info *uinfo, const struct hda_channel_mode *chmode, @@ -3302,6 +3623,9 @@ int snd_hda_ch_mode_info(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info); +/** + * snd_hda_ch_mode_get - Get callback helper for the channel mode enum + */ int snd_hda_ch_mode_get(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, const struct hda_channel_mode *chmode, @@ -3320,6 +3644,9 @@ int snd_hda_ch_mode_get(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get); +/** + * snd_hda_ch_mode_put - Put callback helper for the channel mode enum + */ int snd_hda_ch_mode_put(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, const struct hda_channel_mode *chmode, @@ -3344,6 +3671,10 @@ EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put); /* * input MUX helper */ + +/** + * snd_hda_input_mux_info_info - Info callback helper for the input-mux enum + */ int snd_hda_input_mux_info(const struct hda_input_mux *imux, struct snd_ctl_elem_info *uinfo) { @@ -3362,6 +3693,9 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux, } EXPORT_SYMBOL_HDA(snd_hda_input_mux_info); +/** + * snd_hda_input_mux_info_put - Put callback helper for the input-mux enum + */ int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux, struct snd_ctl_elem_value *ucontrol, @@ -3421,8 +3755,29 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid) } } -/* - * open the digital out in the exclusive mode +/** + * snd_hda_bus_reboot_notify - call the reboot notifier of each codec + * @bus: HD-audio bus + */ +void snd_hda_bus_reboot_notify(struct hda_bus *bus) +{ + struct hda_codec *codec; + + if (!bus) + return; + list_for_each_entry(codec, &bus->codec_list, list) { +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!codec->power_on) + continue; +#endif + if (codec->patch_ops.reboot_notify) + codec->patch_ops.reboot_notify(codec); + } +} +EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify); + +/** + * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode */ int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout) @@ -3437,6 +3792,9 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open); +/** + * snd_hda_multi_out_dig_prepare - prepare the digital out stream + */ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, struct hda_multi_out *mout, unsigned int stream_tag, @@ -3450,6 +3808,9 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare); +/** + * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream + */ int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, struct hda_multi_out *mout) { @@ -3460,8 +3821,8 @@ int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup); -/* - * release the digital out +/** + * snd_hda_multi_out_dig_close - release the digital out stream */ int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout) @@ -3473,8 +3834,12 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close); -/* - * set up more restrictions for analog out +/** + * snd_hda_multi_out_analog_open - open analog outputs + * + * Open analog outputs and set up the hw-constraints. + * If the digital outputs can be opened as slave, open the digital + * outputs, too. */ int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout, @@ -3519,9 +3884,11 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open); -/* - * set up the i/o for analog out - * when the digital out is available, copy the front out to digital out, too. +/** + * snd_hda_multi_out_analog_prepare - Preapre the analog outputs. + * + * Set up the i/o for analog out. + * When the digital out is available, copy the front out to digital out, too. */ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout, @@ -3578,8 +3945,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare); -/* - * clean up the setting for analog out +/** + * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out */ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout) @@ -3965,8 +4332,14 @@ EXPORT_SYMBOL_HDA(snd_hda_resume); * generic arrays */ -/* get a new element from the given array - * if it exceeds the pre-allocated array size, re-allocate the array +/** + * snd_array_new - get a new element from the given array + * @array: the array object + * + * Get a new element from the given array. If it exceeds the + * pre-allocated array size, re-allocate the array. + * + * Returns NULL if allocation failed. */ void *snd_array_new(struct snd_array *array) { @@ -3990,7 +4363,10 @@ void *snd_array_new(struct snd_array *array) } EXPORT_SYMBOL_HDA(snd_array_new); -/* free the given array elements */ +/** + * snd_array_free - free the given array elements + * @array: the array object + */ void snd_array_free(struct snd_array *array) { kfree(array->list); @@ -4000,7 +4376,12 @@ void snd_array_free(struct snd_array *array) } EXPORT_SYMBOL_HDA(snd_array_free); -/* +/** + * snd_print_pcm_rates - Print the supported PCM rates to the string buffer + * @pcm: PCM caps bits + * @buf: the string buffer to write + * @buflen: the max buffer length + * * used by hda_proc.c and hda_eld.c */ void snd_print_pcm_rates(int pcm, char *buf, int buflen) @@ -4019,6 +4400,14 @@ void snd_print_pcm_rates(int pcm, char *buf, int buflen) } EXPORT_SYMBOL_HDA(snd_print_pcm_rates); +/** + * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer + * @pcm: PCM caps bits + * @buf: the string buffer to write + * @buflen: the max buffer length + * + * used by hda_proc.c and hda_eld.c + */ void snd_print_pcm_bits(int pcm, char *buf, int buflen) { static unsigned int bits[] = { 8, 16, 20, 24, 32 }; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 99552fb5f756..be6c5f443cd9 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -674,6 +674,7 @@ struct hda_codec_ops { #ifdef CONFIG_SND_HDA_POWER_SAVE int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid); #endif + void (*reboot_notify)(struct hda_codec *codec); }; /* record for amp information cache */ @@ -771,6 +772,7 @@ struct hda_codec { /* beep device */ struct hda_beep *beep; + unsigned int beep_mode; /* widget capabilities cache */ unsigned int num_nodes; @@ -811,6 +813,9 @@ struct hda_codec { unsigned int power_transition :1; /* power-state in transition */ int power_count; /* current (global) power refcount */ struct delayed_work power_work; /* delayed task for powerdown */ + unsigned long power_on_acct; + unsigned long power_off_acct; + unsigned long power_jiffies; #endif /* codec-specific additional proc output */ @@ -910,6 +915,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, * Misc */ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); +void snd_hda_bus_reboot_notify(struct hda_bus *bus); /* * power management @@ -933,6 +939,7 @@ const char *snd_hda_get_jack_location(u32 cfg); void snd_hda_power_up(struct hda_codec *codec); void snd_hda_power_down(struct hda_codec *codec); #define snd_hda_codec_needs_resume(codec) codec->power_count +void snd_hda_update_power_acct(struct hda_codec *codec); #else static inline void snd_hda_power_up(struct hda_codec *codec) {} static inline void snd_hda_power_down(struct hda_codec *codec) {} diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 9446a5abea13..20fa6aee29c0 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -560,13 +560,14 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry, } -int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld) +int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, + int index) { char name[32]; struct snd_info_entry *entry; int err; - snprintf(name, sizeof(name), "eld#%d", codec->addr); + snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); err = snd_card_proc_new(codec->bus->card, name, &entry); if (err < 0) return err; diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b36f6c5a92df..092c6a7c2ff3 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -727,7 +727,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, if (is_loopback) add_input_loopback(codec, node->nid, HDA_INPUT, index); snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; created = 1; @@ -737,7 +738,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, if (is_loopback) add_input_loopback(codec, node->nid, HDA_OUTPUT, 0); snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; created = 1; @@ -751,7 +753,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) { knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT); snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; created = 1; @@ -759,7 +762,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) { knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT); snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; created = 1; @@ -857,7 +861,7 @@ static int build_input_controls(struct hda_codec *codec) } /* create input MUX if multiple sources are available */ - err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec)); + err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cap_sel, codec)); if (err < 0) return err; @@ -875,7 +879,8 @@ static int build_input_controls(struct hda_codec *codec) HDA_CODEC_VOLUME(name, adc_node->nid, spec->input_mux.items[i].index, HDA_INPUT); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, adc_node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; } diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index cc24e6721d74..d24328661c6a 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -154,6 +154,44 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec) return 0; } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static ssize_t power_on_acct_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); + struct hda_codec *codec = hwdep->private_data; + snd_hda_update_power_acct(codec); + return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct)); +} + +static ssize_t power_off_acct_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); + struct hda_codec *codec = hwdep->private_data; + snd_hda_update_power_acct(codec); + return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct)); +} + +static struct device_attribute power_attrs[] = { + __ATTR_RO(power_on_acct), + __ATTR_RO(power_off_acct), +}; + +int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec) +{ + struct snd_hwdep *hwdep = codec->hwdep; + int i; + + for (i = 0; i < ARRAY_SIZE(power_attrs); i++) + snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, + hwdep->device, &power_attrs[i]); + return 0; +} +#endif /* CONFIG_SND_HDA_POWER_SAVE */ + #ifdef CONFIG_SND_HDA_RECONFIG /* diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 6517f589d01d..91bcbdad5af5 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -60,10 +60,14 @@ static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; 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; +static int enable_msi = -1; #ifdef CONFIG_SND_HDA_PATCH_LOADER static char *patch[SNDRV_CARDS]; #endif +#ifdef CONFIG_SND_HDA_INPUT_BEEP +static int beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = + CONFIG_SND_HDA_INPUT_BEEP_MODE}; +#endif module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); @@ -91,6 +95,11 @@ MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); module_param_array(patch, charp, NULL, 0444); MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface."); #endif +#ifdef CONFIG_SND_HDA_INPUT_BEEP +module_param_array(beep_mode, int, NULL, 0444); +MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode " + "(0=off, 1=on, 2=mute switch on/off) (default=1)."); +#endif #ifdef CONFIG_SND_HDA_POWER_SAVE static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; @@ -404,6 +413,7 @@ struct azx { unsigned short codec_mask; int codec_probe_mask; /* copied from probe_mask option */ struct hda_bus *bus; + unsigned int beep_mode; /* CORB/RIRB */ struct azx_rb corb; @@ -677,6 +687,14 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, } } + if (!chip->polling_mode) { + snd_printk(KERN_WARNING SFX "azx_get_response timeout, " + "switching to polling mode: last cmd=0x%08x\n", + chip->last_cmd[addr]); + chip->polling_mode = 1; + goto again; + } + if (chip->msi) { snd_printk(KERN_WARNING SFX "No response from codec, " "disabling MSI: last cmd=0x%08x\n", @@ -692,14 +710,6 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, goto again; } - if (!chip->polling_mode) { - snd_printk(KERN_WARNING SFX "azx_get_response timeout, " - "switching to polling mode: last cmd=0x%08x\n", - chip->last_cmd[addr]); - chip->polling_mode = 1; - goto again; - } - if (chip->probing) { /* If this critical timeout happens during the codec probing * phase, this is likely an access to a non-existing codec @@ -1404,6 +1414,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model) err = snd_hda_codec_new(chip->bus, c, &codec); if (err < 0) continue; + codec->beep_mode = chip->beep_mode; codecs++; } } @@ -2154,6 +2165,7 @@ static int azx_resume(struct pci_dev *pci) static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf) { struct azx *chip = container_of(nb, struct azx, reboot_notifier); + snd_hda_bus_reboot_notify(chip->bus); azx_stop_chip(chip); return NOTIFY_OK; } @@ -2304,11 +2316,9 @@ static void __devinit check_probe_mask(struct azx *chip, int dev) } /* - * white-list for enable_msi + * white/black-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 struct snd_pci_quirk msi_black_list[] __devinitdata = { {} }; @@ -2316,10 +2326,12 @@ static void __devinit check_msi(struct azx *chip) { const struct snd_pci_quirk *q; - chip->msi = enable_msi; - if (chip->msi) + if (enable_msi >= 0) { + chip->msi = !!enable_msi; return; - q = snd_pci_quirk_lookup(chip->pci, msi_white_list); + } + chip->msi = 1; /* enable MSI as default */ + q = snd_pci_quirk_lookup(chip->pci, msi_black_list); if (q) { printk(KERN_INFO "hda_intel: msi for device %04x:%04x set to %d\n", @@ -2578,6 +2590,10 @@ static int __devinit azx_probe(struct pci_dev *pci, goto out_free; card->private_data = chip; +#ifdef CONFIG_SND_HDA_INPUT_BEEP + chip->beep_mode = beep_mode[dev]; +#endif + /* create codec instances */ err = azx_codec_create(chip, model[dev]); if (err < 0) diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 5f1dcc59002b..d4a3d0942c00 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -23,6 +23,15 @@ #ifndef __SOUND_HDA_LOCAL_H #define __SOUND_HDA_LOCAL_H +/* We abuse kcontrol_new.subdev field to pass the NID corresponding to + * the given new control. If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG, + * snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID. + * + * Note that the subdevice field is cleared again before the real registration + * in snd_hda_ctl_add(), so that this value won't appear in the outside. + */ +#define HDA_SUBDEV_NID_FLAG (1U << 31) + /* * for mixer controls */ @@ -33,6 +42,7 @@ /* mono volume with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ @@ -53,6 +63,7 @@ /* mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put, \ @@ -66,6 +77,28 @@ /* stereo mute switch */ #define HDA_CODEC_MUTE(xname, nid, xindex, direction) \ HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction) +#ifdef CONFIG_SND_HDA_INPUT_BEEP +/* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ +#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = snd_hda_mixer_amp_switch_put_beep, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } +#else +/* no digital beep - just the standard one */ +#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) \ + HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) +#endif /* CONFIG_SND_HDA_INPUT_BEEP */ +/* special beep mono mute switch */ +#define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \ + HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction) +/* special beep stereo mute switch */ +#define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \ + HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction) + +extern const char *snd_hda_pcm_type_name[]; int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); @@ -81,6 +114,10 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +#ifdef CONFIG_SND_HDA_INPUT_BEEP +int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +#endif /* lowlevel accessor with caching; use carefully */ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index); @@ -425,7 +462,13 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps); u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl); +struct hda_nid_item { + struct snd_kcontrol *kctl; + hda_nid_t nid; +}; + +int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, + struct snd_kcontrol *kctl); void snd_hda_ctls_clear(struct hda_codec *codec); /* @@ -437,6 +480,15 @@ int snd_hda_create_hwdep(struct hda_codec *codec); static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; } #endif +#if defined(CONFIG_SND_HDA_POWER_SAVE) && defined(CONFIG_SND_HDA_HWDEP) +int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec); +#else +static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec) +{ + return 0; +} +#endif + #ifdef CONFIG_SND_HDA_RECONFIG int snd_hda_hwdep_add_sysfs(struct hda_codec *codec); #else @@ -490,7 +542,8 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, * AMP control callbacks */ /* retrieve parameters from private_value */ -#define get_amp_nid(kc) ((kc)->private_value & 0xffff) +#define get_amp_nid_(pv) ((pv) & 0xffff) +#define get_amp_nid(kc) get_amp_nid_((kc)->private_value) #define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3) #define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1) #define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) @@ -541,11 +594,13 @@ int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t); void snd_hdmi_show_eld(struct hdmi_eld *eld); #ifdef CONFIG_PROC_FS -int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld); +int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, + int index); void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld); #else static inline int snd_hda_eld_proc_new(struct hda_codec *codec, - struct hdmi_eld *eld) + struct hdmi_eld *eld, + int index) { return 0; } diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 95f24e4729f8..f465cff28041 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -46,6 +46,41 @@ static const char *get_wid_type_name(unsigned int wid_value) return "UNKNOWN Widget"; } +static void print_nid_mixers(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int i; + struct hda_nid_item *items = codec->mixers.list; + struct snd_kcontrol *kctl; + for (i = 0; i < codec->mixers.used; i++) { + if (items[i].nid == nid) { + kctl = items[i].kctl; + snd_iprintf(buffer, + " Control: name=\"%s\", index=%i, device=%i\n", + kctl->id.name, kctl->id.index, kctl->id.device); + } + } +} + +static void print_nid_pcms(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int pcm, type; + struct hda_pcm *cpcm; + for (pcm = 0; pcm < codec->num_pcms; pcm++) { + cpcm = &codec->pcm_info[pcm]; + for (type = 0; type < 2; type++) { + if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL) + continue; + snd_iprintf(buffer, " Device: name=\"%s\", " + "type=\"%s\", device=%i\n", + cpcm->name, + snd_hda_pcm_type_name[cpcm->pcm_type], + cpcm->pcm->device); + } + } +} + static void print_amp_caps(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid, int dir) { @@ -457,6 +492,7 @@ static void print_gpio(struct snd_info_buffer *buffer, (data & (1<<i)) ? 1 : 0, (unsol & (1<<i)) ? 1 : 0); /* FIXME: add GPO and GPI pin information */ + print_nid_mixers(buffer, codec, nid); } static void print_codec_info(struct snd_info_entry *entry, @@ -536,6 +572,9 @@ static void print_codec_info(struct snd_info_entry *entry, snd_iprintf(buffer, " CP"); snd_iprintf(buffer, "\n"); + print_nid_mixers(buffer, codec, nid); + print_nid_pcms(buffer, codec, nid); + /* volume knob is a special widget that always have connection * list */ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 2d603f6aba63..8a1064bdf4c6 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -156,15 +156,19 @@ static const char *ad_slave_sws[] = { static void ad198x_free_kctls(struct hda_codec *codec); +#ifdef CONFIG_SND_HDA_INPUT_BEEP /* additional beep mixers; the actual parameters are overwritten at build */ static struct snd_kcontrol_new ad_beep_mixer[] = { HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT), { } /* end */ }; #define set_beep_amp(spec, nid, idx, dir) \ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */ +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#endif static int ad198x_build_controls(struct hda_codec *codec) { @@ -194,6 +198,7 @@ static int ad198x_build_controls(struct hda_codec *codec) } /* create beep controls if needed */ +#ifdef CONFIG_SND_HDA_INPUT_BEEP if (spec->beep_amp) { struct snd_kcontrol_new *knew; for (knew = ad_beep_mixer; knew->name; knew++) { @@ -202,11 +207,14 @@ static int ad198x_build_controls(struct hda_codec *codec) if (!kctl) return -ENOMEM; kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, + get_amp_nid_(spec->beep_amp), + kctl); if (err < 0) return err; } } +#endif /* if we have no master control, let's create it */ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { @@ -2569,6 +2577,8 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name, knew->name = kstrdup(name, GFP_KERNEL); if (! knew->name) return -ENOMEM; + if (get_amp_nid_(val)) + knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); knew->private_value = val; return 0; } diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index d08353d3bb7f..af478019088e 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -144,7 +144,7 @@ static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, struct snd_kcontrol_new knew = HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type); sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); } static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, @@ -155,7 +155,7 @@ static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, struct snd_kcontrol_new knew = HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); } #define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0) diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 8ba306856d38..9ac09e4568b3 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -500,7 +500,7 @@ static int add_mute(struct hda_codec *codec, const char *name, int index, 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); + return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp); } static int add_volume(struct hda_codec *codec, const char *name, @@ -513,7 +513,7 @@ static int add_volume(struct hda_codec *codec, const char *name, 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); + return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp); } static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) @@ -536,14 +536,14 @@ static int add_vmaster(struct hda_codec *codec, hda_nid_t dac) spec->vmaster_sw = snd_ctl_make_virtual_master("Master Playback Switch", NULL); - err = snd_hda_ctl_add(codec, spec->vmaster_sw); + err = snd_hda_ctl_add(codec, dac, 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); + err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol); if (err < 0) return err; return 0; @@ -756,13 +756,13 @@ static int build_input(struct hda_codec *codec) if (!kctl) return -ENOMEM; kctl->private_value = (long)spec->capture_bind[i]; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; } if (spec->num_inputs > 1 && !spec->mic_detect) { - err = snd_hda_ctl_add(codec, + err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cs_capture_source, codec)); if (err < 0) return err; diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 780e1a72114a..85c81feb10cf 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -197,8 +197,8 @@ static struct snd_kcontrol_new cmi9880_basic_mixer[] = { HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x08, 0, HDA_INPUT), HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0, HDA_INPUT), - HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x23, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x23, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x23, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x23, 0, HDA_OUTPUT), { } /* end */ }; diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 01a18ed475ac..4f25f08d332b 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -33,15 +33,43 @@ #include "hda_codec.h" #include "hda_local.h" -static hda_nid_t cvt_nid; /* audio converter */ -static hda_nid_t pin_nid; /* HDMI output pin */ +/* + * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device + * could support two independent pipes, each of them can be connected to one or + * more ports (DVI, HDMI or DisplayPort). + * + * The HDA correspondence of pipes/ports are converter/pin nodes. + */ +#define INTEL_HDMI_CVTS 2 +#define INTEL_HDMI_PINS 3 -#define INTEL_HDMI_EVENT_TAG 0x08 +static char *intel_hdmi_pcm_names[INTEL_HDMI_CVTS] = { + "INTEL HDMI 0", + "INTEL HDMI 1", +}; struct intel_hdmi_spec { - struct hda_multi_out multiout; - struct hda_pcm pcm_rec; - struct hdmi_eld sink_eld; + int num_cvts; + int num_pins; + hda_nid_t cvt[INTEL_HDMI_CVTS+1]; /* audio sources */ + hda_nid_t pin[INTEL_HDMI_PINS+1]; /* audio sinks */ + + /* + * source connection for each pin + */ + hda_nid_t pin_cvt[INTEL_HDMI_PINS+1]; + + /* + * HDMI sink attached to each pin + */ + bool sink_present[INTEL_HDMI_PINS]; + bool sink_eldv[INTEL_HDMI_PINS]; + struct hdmi_eld sink_eld[INTEL_HDMI_PINS]; + + /* + * export one pcm per pipe + */ + struct hda_pcm pcm_rec[INTEL_HDMI_CVTS]; }; struct hdmi_audio_infoframe { @@ -184,40 +212,165 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { { .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, }; + +/* + * HDA/HDMI auto parsing + */ + +static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) +{ + int i; + + for (i = 0; nids[i]; i++) + if (nids[i] == nid) + return i; + + snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid); + return -EINVAL; +} + +static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) +{ + struct intel_hdmi_spec *spec = codec->spec; + hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; + int conn_len, curr; + int index; + + if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { + snd_printk(KERN_WARNING + "HDMI: pin %d wcaps %#x " + "does not support connection list\n", + pin_nid, get_wcaps(codec, pin_nid)); + return -EINVAL; + } + + conn_len = snd_hda_get_connections(codec, pin_nid, conn_list, + HDA_MAX_CONNECTIONS); + if (conn_len > 1) + curr = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_CONNECT_SEL, 0); + else + curr = 0; + + index = hda_node_index(spec->pin, pin_nid); + if (index < 0) + return -EINVAL; + + spec->pin_cvt[index] = conn_list[curr]; + + return 0; +} + +static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) +{ + struct intel_hdmi_spec *spec = codec->spec; + + if (spec->num_pins >= INTEL_HDMI_PINS) { + snd_printk(KERN_WARNING + "HDMI: no space for pin %d \n", pin_nid); + return -EINVAL; + } + + spec->pin[spec->num_pins] = pin_nid; + spec->num_pins++; + + /* + * It is assumed that converter nodes come first in the node list and + * hence have been registered and usable now. + */ + return intel_hdmi_read_pin_conn(codec, pin_nid); +} + +static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid) +{ + struct intel_hdmi_spec *spec = codec->spec; + + if (spec->num_cvts >= INTEL_HDMI_CVTS) { + snd_printk(KERN_WARNING + "HDMI: no space for converter %d \n", nid); + return -EINVAL; + } + + spec->cvt[spec->num_cvts] = nid; + spec->num_cvts++; + + return 0; +} + +static int intel_hdmi_parse_codec(struct hda_codec *codec) +{ + hda_nid_t nid; + int i, nodes; + + nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + if (!nid || nodes < 0) { + snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n"); + return -EINVAL; + } + + for (i = 0; i < nodes; i++, nid++) { + unsigned int caps; + unsigned int type; + + caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); + type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_DIGITAL)) + continue; + + switch (type) { + case AC_WID_AUD_OUT: + if (intel_hdmi_add_cvt(codec, nid) < 0) + return -EINVAL; + break; + case AC_WID_PIN: + caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + if (!(caps & AC_PINCAP_HDMI)) + continue; + if (intel_hdmi_add_pin(codec, nid) < 0) + return -EINVAL; + break; + } + } + + return 0; +} + /* * HDMI routines */ #ifdef BE_PARANOID -static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, int *packet_index, int *byte_index) { int val; - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_INDEX, 0); + val = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_INDEX, 0); *packet_index = val >> 5; *byte_index = val & 0x1f; } #endif -static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, int packet_index, int byte_index) { int val; val = (packet_index << 5) | (byte_index & 0x1f); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); } -static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, unsigned char val) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); } -static void hdmi_enable_output(struct hda_codec *codec) +static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid) { /* Unmute */ if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) @@ -231,7 +384,8 @@ static void hdmi_enable_output(struct hda_codec *codec) /* * Enable Audio InfoFrame Transmission */ -static void hdmi_start_infoframe_trans(struct hda_codec *codec) +static void hdmi_start_infoframe_trans(struct hda_codec *codec, + hda_nid_t pin_nid) { hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, @@ -241,37 +395,42 @@ static void hdmi_start_infoframe_trans(struct hda_codec *codec) /* * Disable Audio InfoFrame Transmission */ -static void hdmi_stop_infoframe_trans(struct hda_codec *codec) +static void hdmi_stop_infoframe_trans(struct hda_codec *codec, + hda_nid_t pin_nid) { 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) +#ifdef CONFIG_SND_DEBUG_VERBOSE +static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid) { - return 1 + snd_hda_codec_read(codec, cvt_nid, 0, + return 1 + snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CVT_CHAN_COUNT, 0); } +#endif -static void hdmi_set_channel_count(struct hda_codec *codec, int chs) +static void hdmi_set_channel_count(struct hda_codec *codec, + hda_nid_t nid, int chs) { - snd_hda_codec_write(codec, cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); - if (chs != hdmi_get_channel_count(codec)) +#ifdef CONFIG_SND_DEBUG_VERBOSE + if (chs != hdmi_get_channel_count(codec, nid)) snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n", - chs, hdmi_get_channel_count(codec)); + chs, hdmi_get_channel_count(codec, nid)); +#endif } -static void hdmi_debug_channel_mapping(struct hda_codec *codec) +static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid) { #ifdef CONFIG_SND_DEBUG_VERBOSE int i; int slot; for (i = 0; i < 8; i++) { - slot = snd_hda_codec_read(codec, cvt_nid, 0, + slot = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_CHAN_SLOT, i); printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", slot >> 4, slot & 0x7); @@ -279,12 +438,12 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec) #endif } -static void hdmi_parse_eld(struct hda_codec *codec) +static void hdmi_parse_eld(struct hda_codec *codec, int index) { struct intel_hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld = &spec->sink_eld; + struct hdmi_eld *eld = &spec->sink_eld[index]; - if (!snd_hdmi_get_eld(eld, codec, pin_nid)) + if (!snd_hdmi_get_eld(eld, codec, spec->pin[index])) snd_hdmi_show_eld(eld); } @@ -293,7 +452,7 @@ static void hdmi_parse_eld(struct hda_codec *codec) * Audio InfoFrame routines */ -static void hdmi_debug_dip_size(struct hda_codec *codec) +static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid) { #ifdef CONFIG_SND_DEBUG_VERBOSE int i; @@ -310,7 +469,7 @@ static void hdmi_debug_dip_size(struct hda_codec *codec) #endif } -static void hdmi_clear_dip_buffers(struct hda_codec *codec) +static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) { #ifdef BE_PARANOID int i, j; @@ -340,14 +499,15 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec) } static void hdmi_fill_audio_infoframe(struct hda_codec *codec, - struct hdmi_audio_infoframe *ai) + hda_nid_t pin_nid, + struct hdmi_audio_infoframe *ai) { u8 *params = (u8 *)ai; u8 sum = 0; int i; - hdmi_debug_dip_size(codec); - hdmi_clear_dip_buffers(codec); /* be paranoid */ + hdmi_debug_dip_size(codec, pin_nid); + hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ for (i = 0; i < sizeof(ai); i++) sum += params[i]; @@ -386,11 +546,11 @@ static void init_channel_allocations(void) * * TODO: it could select the wrong CA from multiple candidates. */ -static int hdmi_setup_channel_allocation(struct hda_codec *codec, +static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, struct hdmi_audio_infoframe *ai) { struct intel_hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld = &spec->sink_eld; + struct hdmi_eld *eld; int i; int spk_mask = 0; int channels = 1 + (ai->CC02_CT47 & 0x7); @@ -402,6 +562,11 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, if (channels <= 2) return 0; + i = hda_node_index(spec->pin_cvt, nid); + if (i < 0) + return 0; + eld = &spec->sink_eld[i]; + /* * HDMI sink's ELD info cannot always be retrieved for now, e.g. * in console or for audio devices. Assume the highest speakers @@ -439,8 +604,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, return ai->CA; } -static void hdmi_setup_channel_mapping(struct hda_codec *codec, - struct hdmi_audio_infoframe *ai) +static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid, + struct hdmi_audio_infoframe *ai) { int i; @@ -453,17 +618,20 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, */ for (i = 0; i < 8; i++) - snd_hda_codec_write(codec, cvt_nid, 0, + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_CHAN_SLOT, (i << 4) | i); - hdmi_debug_channel_mapping(codec); + hdmi_debug_channel_mapping(codec, nid); } -static void hdmi_setup_audio_infoframe(struct hda_codec *codec, +static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, struct snd_pcm_substream *substream) { + struct intel_hdmi_spec *spec = codec->spec; + hda_nid_t pin_nid; + int i; struct hdmi_audio_infoframe ai = { .type = 0x84, .ver = 0x01, @@ -471,11 +639,19 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, .CC02_CT47 = substream->runtime->channels - 1, }; - hdmi_setup_channel_allocation(codec, &ai); - hdmi_setup_channel_mapping(codec, &ai); + hdmi_setup_channel_allocation(codec, nid, &ai); + hdmi_setup_channel_mapping(codec, nid, &ai); + + for (i = 0; i < spec->num_pins; i++) { + if (spec->pin_cvt[i] != nid) + continue; + if (spec->sink_present[i] != true) + continue; - hdmi_fill_audio_infoframe(codec, &ai); - hdmi_start_infoframe_trans(codec); + pin_nid = spec->pin[i]; + hdmi_fill_audio_infoframe(codec, pin_nid, &ai); + hdmi_start_infoframe_trans(codec, pin_nid); + } } @@ -485,27 +661,39 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) { + struct intel_hdmi_spec *spec = codec->spec; + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; int pind = !!(res & AC_UNSOL_RES_PD); int eldv = !!(res & AC_UNSOL_RES_ELDV); + int index; printk(KERN_INFO - "HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n", - pind, eldv); + "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", + tag, pind, eldv); + + index = hda_node_index(spec->pin, tag); + if (index < 0) + return; + + spec->sink_present[index] = pind; + spec->sink_eldv[index] = eldv; if (pind && eldv) { - hdmi_parse_eld(codec); + hdmi_parse_eld(codec, index); /* TODO: do real things about ELD */ } } static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) { + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; int cp_state = !!(res & AC_UNSOL_RES_CP_STATE); int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); printk(KERN_INFO - "HDMI content protection event: SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + tag, subtag, cp_state, cp_ready); @@ -520,10 +708,11 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res) { + struct intel_hdmi_spec *spec = codec->spec; int tag = res >> AC_UNSOL_RES_TAG_SHIFT; int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - if (tag != INTEL_HDMI_EVENT_TAG) { + if (hda_node_index(spec->pin, tag) < 0) { snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag); return; } @@ -538,69 +727,70 @@ static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res) * Callbacks */ -static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct intel_hdmi_spec *spec = codec->spec; - - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) +static int intel_hdmi_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 intel_hdmi_spec *spec = codec->spec; + hdmi_set_channel_count(codec, hinfo->nid, + substream->runtime->channels); - hdmi_stop_infoframe_trans(codec); + hdmi_setup_audio_infoframe(codec, hinfo->nid, substream); - return snd_hda_multi_out_dig_close(codec, &spec->multiout); + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + return 0; } -static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, +static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, struct snd_pcm_substream *substream) { struct intel_hdmi_spec *spec = codec->spec; + int i; - snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); - - hdmi_set_channel_count(codec, substream->runtime->channels); + for (i = 0; i < spec->num_pins; i++) { + if (spec->pin_cvt[i] != hinfo->nid) + continue; - hdmi_setup_audio_infoframe(codec, substream); + hdmi_stop_infoframe_trans(codec, spec->pin[i]); + } + snd_hda_codec_cleanup_stream(codec, hinfo->nid); return 0; } static struct hda_pcm_stream intel_hdmi_pcm_playback = { .substreams = 1, .channels_min = 2, - .channels_max = 8, .ops = { - .open = intel_hdmi_playback_pcm_open, - .close = intel_hdmi_playback_pcm_close, - .prepare = intel_hdmi_playback_pcm_prepare + .prepare = intel_hdmi_playback_pcm_prepare, + .cleanup = intel_hdmi_playback_pcm_cleanup, }, }; static int intel_hdmi_build_pcms(struct hda_codec *codec) { struct intel_hdmi_spec *spec = codec->spec; - struct hda_pcm *info = &spec->pcm_rec; + struct hda_pcm *info = spec->pcm_rec; + int i; - codec->num_pcms = 1; + codec->num_pcms = spec->num_cvts; codec->pcm_info = info; - /* NID to query formats and rates and setup streams */ - intel_hdmi_pcm_playback.nid = cvt_nid; + for (i = 0; i < codec->num_pcms; i++, info++) { + unsigned int chans; + + chans = get_wcaps(codec, spec->cvt[i]); + chans = get_wcaps_channels(chans); - info->name = "INTEL HDMI"; - info->pcm_type = HDA_PCM_TYPE_HDMI; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback; + info->name = intel_hdmi_pcm_names[i]; + info->pcm_type = HDA_PCM_TYPE_HDMI; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + intel_hdmi_pcm_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans; + } return 0; } @@ -609,29 +799,39 @@ static int intel_hdmi_build_controls(struct hda_codec *codec) { struct intel_hdmi_spec *spec = codec->spec; int err; + int i; - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); - if (err < 0) - return err; + for (i = 0; i < codec->num_pcms; i++) { + err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]); + if (err < 0) + return err; + } return 0; } static int intel_hdmi_init(struct hda_codec *codec) { - hdmi_enable_output(codec); + struct intel_hdmi_spec *spec = codec->spec; + int i; - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | INTEL_HDMI_EVENT_TAG); + for (i = 0; spec->pin[i]; i++) { + hdmi_enable_output(codec, spec->pin[i]); + snd_hda_codec_write(codec, spec->pin[i], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | spec->pin[i]); + } return 0; } static void intel_hdmi_free(struct hda_codec *codec) { struct intel_hdmi_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_pins; i++) + snd_hda_eld_proc_free(codec, &spec->sink_eld[i]); - snd_hda_eld_proc_free(codec, &spec->sink_eld); kfree(spec); } @@ -643,49 +843,38 @@ static struct hda_codec_ops intel_hdmi_patch_ops = { .unsol_event = intel_hdmi_unsol_event, }; -static int do_patch_intel_hdmi(struct hda_codec *codec) +static int patch_intel_hdmi(struct hda_codec *codec) { struct intel_hdmi_spec *spec; + int i; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM; - spec->multiout.num_dacs = 0; /* no analog */ - spec->multiout.max_channels = 8; - spec->multiout.dig_out_nid = cvt_nid; - codec->spec = spec; + if (intel_hdmi_parse_codec(codec) < 0) { + codec->spec = NULL; + kfree(spec); + return -EINVAL; + } codec->patch_ops = intel_hdmi_patch_ops; - snd_hda_eld_proc_new(codec, &spec->sink_eld); + for (i = 0; i < spec->num_pins; i++) + snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i); init_channel_allocations(); 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 = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi }, { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 70583719282b..ba339d745aab 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2410,12 +2410,14 @@ static const char *alc_slave_sws[] = { static void alc_free_kctls(struct hda_codec *codec); +#ifdef CONFIG_SND_HDA_INPUT_BEEP /* additional beep mixers; the actual parameters are overwritten at build */ static struct snd_kcontrol_new alc_beep_mixer[] = { HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_INPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), { } /* end */ }; +#endif static int alc_build_controls(struct hda_codec *codec) { @@ -2452,6 +2454,7 @@ static int alc_build_controls(struct hda_codec *codec) return err; } +#ifdef CONFIG_SND_HDA_INPUT_BEEP /* create beep controls if needed */ if (spec->beep_amp) { struct snd_kcontrol_new *knew; @@ -2461,11 +2464,13 @@ static int alc_build_controls(struct hda_codec *codec) if (!kctl) return -ENOMEM; kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, + get_amp_nid_(spec->beep_amp), kctl); if (err < 0) return err; } } +#endif /* if we have no master control, let's create it */ if (!spec->no_analog && @@ -4322,10 +4327,26 @@ static int add_control(struct alc_spec *spec, int type, const char *name, knew->name = kstrdup(name, GFP_KERNEL); if (!knew->name) return -ENOMEM; + if (get_amp_nid_(val)) + knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); knew->private_value = val; return 0; } +static int add_control_with_pfx(struct alc_spec *spec, int type, + const char *pfx, const char *dir, + const char *sfx, unsigned long val) +{ + char name[32]; + snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); + return add_control(spec, type, name, val); +} + +#define add_pb_vol_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", val) +#define add_pb_sw_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", val) + #define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17) #define alc880_fixed_pin_idx(nid) ((nid) - 0x14) #define alc880_is_multi_pin(nid) ((nid) >= 0x18) @@ -4379,7 +4400,6 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec, static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) { - char name[32]; static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" }; @@ -4392,26 +4412,26 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i])); if (i == 2) { /* Center/LFE */ - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "Center Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + "Center", HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "LFE Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + "LFE", HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_BIND_MUTE, - "Center Playback Switch", + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + "Center", HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_BIND_MUTE, - "LFE Playback Switch", + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + "LFE", HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT)); if (err < 0) @@ -4423,14 +4443,12 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, pfx = "Speaker"; else pfx = chname[i]; - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, 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, + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); if (err < 0) @@ -4446,7 +4464,6 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, { hda_nid_t nid; int err; - char name[32]; if (!pin) return 0; @@ -4460,21 +4477,18 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, spec->multiout.extra_out_nid[0] = nid; /* control HP volume/switch on the output mixer amp */ nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin)); - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, 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, + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, 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, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -4487,16 +4501,13 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, const char *ctlname, int idx, hda_nid_t mix_nid) { - char name[32]; int err; - sprintf(name, "%s Playback Volume", ctlname); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; - sprintf(name, "%s Playback Switch", ctlname); - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; @@ -4773,8 +4784,12 @@ static void set_capture_mixer(struct hda_codec *codec) } } +#ifdef CONFIG_SND_HDA_INPUT_BEEP #define set_beep_amp(spec, nid, idx, dir) \ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir)) +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#endif /* * OK, here we have finally the patch for ALC880 @@ -5989,7 +6004,6 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid, { hda_nid_t nid_vol; unsigned long vol_val, sw_val; - char name[32]; int err; if (nid >= 0x0f && nid < 0x11) { @@ -6009,14 +6023,12 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid, if (!(*vol_bits & (1 << nid_vol))) { /* first control for the volume widget */ - snprintf(name, sizeof(name), "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val); + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, vol_val); if (err < 0) return err; *vol_bits |= (1 << nid_vol); } - snprintf(name, sizeof(name), "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, sw_val); + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, sw_val); if (err < 0) return err; return 1; @@ -7336,8 +7348,8 @@ static struct snd_kcontrol_new alc882_macpro_mixer[] = { HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), /* FIXME: this looks suspicious... - HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x02, HDA_INPUT), */ { } /* end */ }; @@ -10956,7 +10968,6 @@ static int alc262_check_volbit(hda_nid_t nid) 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; @@ -10966,28 +10977,25 @@ static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid, 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); + return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, 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); + return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, val); } /* add playback controls from the parsed DAC table */ @@ -12327,11 +12335,9 @@ static struct snd_kcontrol_new alc268_test_mixer[] = { 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); switch (nid) { case 0x14: case 0x16: @@ -12345,7 +12351,7 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, } if (spec->multiout.dac_nids[0] != dac && spec->multiout.dac_nids[1] != dac) { - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, HDA_COMPOSE_AMP_VAL(dac, 3, idx, HDA_OUTPUT)); if (err < 0) @@ -12353,12 +12359,11 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac; } - sprintf(name, "%s Playback Switch", ctlname); if (nid != 0x16) - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT)); else /* mono */ - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT)); if (err < 0) return err; @@ -12388,8 +12393,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, nid = cfg->speaker_pins[0]; if (nid == 0x1d) { - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "Speaker Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Speaker", HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); if (err < 0) return err; @@ -12407,8 +12411,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, 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", + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, "Mono", HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -14260,9 +14263,7 @@ static int alc861_auto_fill_dac_nids(struct hda_codec *codec, 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, + return add_pb_sw_ctrl(codec->spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); } @@ -15386,7 +15387,6 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec) static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) { - char name[32]; static const char *chname[4] = {"Front", "Surround", "CLFE", "Side"}; hda_nid_t nid_v, nid_s; int i, err; @@ -15403,26 +15403,26 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, if (i == 2) { /* Center/LFE */ - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "Center Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + "Center", HDA_COMPOSE_AMP_VAL(nid_v, 1, 0, HDA_OUTPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "LFE Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + "LFE", HDA_COMPOSE_AMP_VAL(nid_v, 2, 0, HDA_OUTPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_BIND_MUTE, - "Center Playback Switch", + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + "Center", HDA_COMPOSE_AMP_VAL(nid_s, 1, 2, HDA_INPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_BIND_MUTE, - "LFE Playback Switch", + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + "LFE", HDA_COMPOSE_AMP_VAL(nid_s, 2, 2, HDA_INPUT)); if (err < 0) @@ -15437,8 +15437,7 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, pfx = "PCM"; } else pfx = chname[i]; - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT)); if (err < 0) @@ -15446,8 +15445,7 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, 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, + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT)); if (err < 0) @@ -15465,7 +15463,6 @@ static int alc861vd_auto_create_extra_out(struct alc_spec *spec, { hda_nid_t nid_v, nid_s; int err; - char name[32]; if (!pin) return 0; @@ -15483,21 +15480,18 @@ static int alc861vd_auto_create_extra_out(struct alc_spec *spec, nid_s = alc861vd_idx_to_mixer_switch( alc880_fixed_pin_idx(pin)); - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT)); if (err < 0) return err; - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_BIND_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid_s, 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, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -17264,21 +17258,17 @@ static int alc662_auto_fill_dac_nids(struct hda_codec *codec, return 0; } -static int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx, +static inline 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, + return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); } -static int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx, +static inline 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, + return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT)); } @@ -17356,13 +17346,11 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, return 0; 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); - return add_control(spec, ALC_CTL_WIDGET_MUTE, name, + return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 86de305fc9f2..7f76a97954f9 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1085,7 +1085,7 @@ static int stac92xx_build_controls(struct hda_codec *codec) 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, + err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&stac_dmux_mixer, codec)); if (err < 0) return err; @@ -1101,7 +1101,7 @@ static int stac92xx_build_controls(struct hda_codec *codec) spec->spdif_mute = 1; } stac_smux_mixer.count = spec->num_smuxes; - err = snd_hda_ctl_add(codec, + err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&stac_smux_mixer, codec)); if (err < 0) return err; @@ -2648,6 +2648,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, enum { STAC_CTL_WIDGET_VOL, STAC_CTL_WIDGET_MUTE, + STAC_CTL_WIDGET_MUTE_BEEP, STAC_CTL_WIDGET_MONO_MUX, STAC_CTL_WIDGET_HP_SWITCH, STAC_CTL_WIDGET_IO_SWITCH, @@ -2658,6 +2659,7 @@ enum { static struct snd_kcontrol_new stac92xx_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), + HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0), STAC_MONO_MUX, STAC_CODEC_HP_SWITCH(NULL), STAC_CODEC_IO_SWITCH(NULL, 0), @@ -2669,7 +2671,8 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = { static struct snd_kcontrol_new * stac_control_new(struct sigmatel_spec *spec, struct snd_kcontrol_new *ktemp, - const char *name) + const char *name, + hda_nid_t nid) { struct snd_kcontrol_new *knew; @@ -2685,6 +2688,8 @@ stac_control_new(struct sigmatel_spec *spec, spec->kctls.alloced--; return NULL; } + if (nid) + knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; return knew; } @@ -2693,7 +2698,8 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec, int idx, const char *name, unsigned long val) { - struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name); + struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name, + get_amp_nid_(val)); if (!knew) return -ENOMEM; knew->index = idx; @@ -2764,7 +2770,7 @@ static int stac92xx_add_input_source(struct sigmatel_spec *spec) 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, - stac_input_src_temp.name); + stac_input_src_temp.name, 0); if (!knew) return -ENOMEM; knew->count = spec->num_adcs; @@ -3221,12 +3227,15 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec, { struct sigmatel_spec *spec = codec->spec; u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT); - int err; + int err, type = STAC_CTL_WIDGET_MUTE_BEEP; + + if (spec->anabeep_nid == nid) + type = STAC_CTL_WIDGET_MUTE; /* check for mute support for the the amp */ if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) { - err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, - "PC Beep Playback Switch", + err = stac92xx_add_control(spec, type, + "Beep Playback Switch", HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -3235,7 +3244,7 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec, /* check to see if there is volume support for the amp */ if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) { err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, - "PC Beep Playback Volume", + "Beep Playback Volume", HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -3258,12 +3267,7 @@ static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int enabled = !!ucontrol->value.integer.value[0]; - if (codec->beep->enabled != enabled) { - codec->beep->enabled = enabled; - return 1; - } - return 0; + return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]); } static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = { @@ -3276,7 +3280,7 @@ static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = { static int stac92xx_beep_switch_ctl(struct hda_codec *codec) { return stac92xx_add_control_temp(codec->spec, &stac92xx_dig_beep_ctrl, - 0, "PC Beep Playback Switch", 0); + 0, "Beep Playback Switch", 0); } #endif @@ -4329,6 +4333,28 @@ static void stac92xx_free_kctls(struct hda_codec *codec) snd_array_free(&spec->kctls); } +static void stac92xx_shutup(struct hda_codec *codec) +{ + 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 & + ~spec->eapd_mask); +} + static void stac92xx_free(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -4336,6 +4362,7 @@ static void stac92xx_free(struct hda_codec *codec) if (! spec) return; + stac92xx_shutup(codec); stac92xx_free_jacks(codec); snd_array_free(&spec->events); @@ -4795,24 +4822,7 @@ 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 & - ~spec->eapd_mask); + stac92xx_shutup(codec); return 0; } #endif @@ -4827,6 +4837,7 @@ static struct hda_codec_ops stac92xx_patch_ops = { .suspend = stac92xx_suspend, .resume = stac92xx_resume, #endif + .reboot_notify = stac92xx_shutup, }; static int patch_stac9200(struct hda_codec *codec) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index ee89db90c9b6..0c621d74b165 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1,10 +1,10 @@ /* * Universal Interface for Intel High Definition Audio Codec * - * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec + * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec * - * Copyright (c) 2006-2008 Lydia Wang <lydiawang@viatech.com> - * Takashi Iwai <tiwai@suse.de> + * (C) 2006-2009 VIA Technology, Inc. + * (C) 2006-2008 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 @@ -22,21 +22,27 @@ */ /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */ -/* */ +/* */ /* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */ -/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ -/* 2006-08-02 Lydia Wang Add support to VT1709 codec */ +/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ +/* 2006-08-02 Lydia Wang Add support to VT1709 codec */ /* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */ -/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ -/* 2007-09-17 Lydia Wang Add VT1708B codec support */ +/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ +/* 2007-09-17 Lydia Wang Add VT1708B codec support */ /* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */ /* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */ -/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */ -/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ -/* 2008-04-09 Lydia Wang Add Independent HP feature */ +/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */ +/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ +/* 2008-04-09 Lydia Wang Add Independent HP feature */ /* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */ -/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ -/* */ +/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ +/* 2009-02-16 Logan Li Add support for VT1718S */ +/* 2009-03-13 Logan Li Add support for VT1716S */ +/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */ +/* 2009-07-08 Lydia Wang Add support for VT2002P */ +/* 2009-07-21 Lydia Wang Add support for VT1812 */ +/* 2009-09-19 Lydia Wang Add support for VT1818S */ +/* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -76,14 +82,6 @@ #define VT1702_HP_NID 0x17 #define VT1702_DIGOUT_NID 0x11 -#define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b) -#define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713) -#define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717) -#define IS_VT1708B_8CH_VENDORID(x) ((x) >= 0x1106e720 && (x) <= 0x1106e723) -#define IS_VT1708B_4CH_VENDORID(x) ((x) >= 0x1106e724 && (x) <= 0x1106e727) -#define IS_VT1708S_VENDORID(x) ((x) >= 0x11060397 && (x) <= 0x11067397) -#define IS_VT1702_VENDORID(x) ((x) >= 0x11060398 && (x) <= 0x11067398) - enum VIA_HDA_CODEC { UNKNOWN = -1, VT1708, @@ -92,12 +90,76 @@ enum VIA_HDA_CODEC { VT1708B_8CH, VT1708B_4CH, VT1708S, + VT1708BCE, VT1702, + VT1718S, + VT1716S, + VT2002P, + VT1812, CODEC_TYPES, }; -static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id) +struct via_spec { + /* codec parameterization */ + struct snd_kcontrol_new *mixers[6]; + unsigned int num_mixers; + + struct hda_verb *init_verbs[5]; + unsigned int num_iverbs; + + char *stream_name_analog; + struct hda_pcm_stream *stream_analog_playback; + struct hda_pcm_stream *stream_analog_capture; + + char *stream_name_digital; + struct hda_pcm_stream *stream_digital_playback; + struct hda_pcm_stream *stream_digital_capture; + + /* playback */ + struct hda_multi_out multiout; + hda_nid_t slave_dig_outs[2]; + + /* capture */ + unsigned int num_adc_nids; + hda_nid_t *adc_nids; + hda_nid_t mux_nids[3]; + hda_nid_t dig_in_nid; + hda_nid_t dig_in_pin; + + /* capture source */ + const struct hda_input_mux *input_mux; + unsigned int cur_mux[3]; + + /* PCM information */ + struct hda_pcm pcm_rec[3]; + + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + struct snd_array kctls; + struct hda_input_mux private_imux[2]; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + + /* HP mode source */ + const struct hda_input_mux *hp_mux; + unsigned int hp_independent_mode; + unsigned int hp_independent_mode_index; + unsigned int smart51_enabled; + unsigned int dmic_enabled; + enum VIA_HDA_CODEC codec_type; + + /* work to check hp jack state */ + struct hda_codec *codec; + struct delayed_work vt1708_hp_work; + int vt1708_jack_detectect; + int vt1708_hp_present; +#ifdef CONFIG_SND_HDA_POWER_SAVE + struct hda_loopback_check loopback; +#endif +}; + +static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) { + u32 vendor_id = codec->vendor_id; u16 ven_id = vendor_id >> 16; u16 dev_id = vendor_id & 0xffff; enum VIA_HDA_CODEC codec_type; @@ -111,9 +173,11 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id) codec_type = VT1709_10CH; else if (dev_id >= 0xe714 && dev_id <= 0xe717) codec_type = VT1709_6CH; - else if (dev_id >= 0xe720 && dev_id <= 0xe723) + else if (dev_id >= 0xe720 && dev_id <= 0xe723) { codec_type = VT1708B_8CH; - else if (dev_id >= 0xe724 && dev_id <= 0xe727) + if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) + codec_type = VT1708BCE; + } else if (dev_id >= 0xe724 && dev_id <= 0xe727) codec_type = VT1708B_4CH; else if ((dev_id & 0xfff) == 0x397 && (dev_id >> 12) < 8) @@ -121,6 +185,19 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id) else if ((dev_id & 0xfff) == 0x398 && (dev_id >> 12) < 8) codec_type = VT1702; + else if ((dev_id & 0xfff) == 0x428 + && (dev_id >> 12) < 8) + codec_type = VT1718S; + else if (dev_id == 0x0433 || dev_id == 0xa721) + codec_type = VT1716S; + else if (dev_id == 0x0441 || dev_id == 0x4441) + codec_type = VT1718S; + else if (dev_id == 0x0438 || dev_id == 0x4438) + codec_type = VT2002P; + else if (dev_id == 0x0448) + codec_type = VT1812; + else if (dev_id == 0x0440) + codec_type = VT1708S; else codec_type = UNKNOWN; return codec_type; @@ -128,10 +205,16 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id) #define VIA_HP_EVENT 0x01 #define VIA_GPIO_EVENT 0x02 +#define VIA_JACK_EVENT 0x04 +#define VIA_MONO_EVENT 0x08 +#define VIA_SPEAKER_EVENT 0x10 +#define VIA_BIND_HP_EVENT 0x20 enum { VIA_CTL_WIDGET_VOL, VIA_CTL_WIDGET_MUTE, + VIA_CTL_WIDGET_ANALOG_MUTE, + VIA_CTL_WIDGET_BIND_PIN_MUTE, }; enum { @@ -141,99 +224,162 @@ enum { AUTO_SEQ_SIDE }; -/* Some VT1708S based boards gets the micboost setting wrong, so we have - * to apply some brute-force and re-write the TLV's by software. */ -static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *_tlv) +static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); +static void set_jack_power_state(struct hda_codec *codec); +static int is_aa_path_mute(struct hda_codec *codec); + +static void vt1708_start_hp_work(struct via_spec *spec) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); + if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) + return; + snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, + !spec->vt1708_jack_detectect); + if (!delayed_work_pending(&spec->vt1708_hp_work)) + schedule_delayed_work(&spec->vt1708_hp_work, + msecs_to_jiffies(100)); +} - if (get_codec_type(codec->vendor_id) == VT1708S - && (nid == 0x1a || nid == 0x1e)) { - if (size < 4 * sizeof(unsigned int)) - return -ENOMEM; - if (put_user(1, _tlv)) /* SNDRV_CTL_TLVT_DB_SCALE */ - return -EFAULT; - if (put_user(2 * sizeof(unsigned int), _tlv + 1)) - return -EFAULT; - if (put_user(0, _tlv + 2)) /* offset = 0 */ - return -EFAULT; - if (put_user(1000, _tlv + 3)) /* step size = 10 dB */ - return -EFAULT; - } - return 0; +static void vt1708_stop_hp_work(struct via_spec *spec) +{ + if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) + return; + if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 + && !is_aa_path_mute(spec->codec)) + return; + snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, + !spec->vt1708_jack_detectect); + cancel_delayed_work(&spec->vt1708_hp_work); + flush_scheduled_work(); } -static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) + +static int analog_input_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - if (get_codec_type(codec->vendor_id) == VT1708S - && (nid == 0x1a || nid == 0x1e)) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 3; + set_jack_power_state(codec); + analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); + if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { + if (is_aa_path_mute(codec)) + vt1708_start_hp_work(codec->spec); + else + vt1708_stop_hp_work(codec->spec); } - return 0; + return change; } -static struct snd_kcontrol_new vt1708_control_templates[] = { - HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), -}; - - -struct via_spec { - /* codec parameterization */ - struct snd_kcontrol_new *mixers[3]; - unsigned int num_mixers; +/* modify .put = snd_hda_mixer_amp_switch_put */ +#define ANALOG_INPUT_MUTE \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = NULL, \ + .index = 0, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = analog_input_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } - struct hda_verb *init_verbs[5]; - unsigned int num_iverbs; +static void via_hp_bind_automute(struct hda_codec *codec); - char *stream_name_analog; - struct hda_pcm_stream *stream_analog_playback; - struct hda_pcm_stream *stream_analog_capture; - - char *stream_name_digital; - struct hda_pcm_stream *stream_digital_playback; - struct hda_pcm_stream *stream_digital_capture; - - /* playback */ - struct hda_multi_out multiout; - hda_nid_t slave_dig_outs[2]; - - /* capture */ - unsigned int num_adc_nids; - hda_nid_t *adc_nids; - hda_nid_t mux_nids[3]; - hda_nid_t dig_in_nid; - hda_nid_t dig_in_pin; +static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int i; + int change = 0; - /* capture source */ - const struct hda_input_mux *input_mux; - unsigned int cur_mux[3]; + long *valp = ucontrol->value.integer.value; + int lmute, rmute; + if (strstr(kcontrol->id.name, "Switch") == NULL) { + snd_printd("Invalid control!\n"); + return change; + } + change = snd_hda_mixer_amp_switch_put(kcontrol, + ucontrol); + /* Get mute value */ + lmute = *valp ? 0 : HDA_AMP_MUTE; + valp++; + rmute = *valp ? 0 : HDA_AMP_MUTE; + + /* Set hp pins */ + if (!spec->hp_independent_mode) { + for (i = 0; i < spec->autocfg.hp_outs; i++) { + snd_hda_codec_amp_update( + codec, spec->autocfg.hp_pins[i], + 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, + lmute); + snd_hda_codec_amp_update( + codec, spec->autocfg.hp_pins[i], + 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, + rmute); + } + } - /* PCM information */ - struct hda_pcm pcm_rec[3]; + if (!lmute && !rmute) { + /* Line Outs */ + for (i = 0; i < spec->autocfg.line_outs; i++) + snd_hda_codec_amp_stereo( + codec, spec->autocfg.line_out_pins[i], + HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); + /* Speakers */ + for (i = 0; i < spec->autocfg.speaker_outs; i++) + snd_hda_codec_amp_stereo( + codec, spec->autocfg.speaker_pins[i], + HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); + /* unmute */ + via_hp_bind_automute(codec); - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; - struct snd_array kctls; - struct hda_input_mux private_imux[2]; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + } else { + if (lmute) { + /* Mute all left channels */ + for (i = 1; i < spec->autocfg.line_outs; i++) + snd_hda_codec_amp_update( + codec, + spec->autocfg.line_out_pins[i], + 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, + lmute); + for (i = 0; i < spec->autocfg.speaker_outs; i++) + snd_hda_codec_amp_update( + codec, + spec->autocfg.speaker_pins[i], + 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, + lmute); + } + if (rmute) { + /* mute all right channels */ + for (i = 1; i < spec->autocfg.line_outs; i++) + snd_hda_codec_amp_update( + codec, + spec->autocfg.line_out_pins[i], + 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, + rmute); + for (i = 0; i < spec->autocfg.speaker_outs; i++) + snd_hda_codec_amp_update( + codec, + spec->autocfg.speaker_pins[i], + 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, + rmute); + } + } + return change; +} - /* HP mode source */ - const struct hda_input_mux *hp_mux; - unsigned int hp_independent_mode; +#define BIND_PIN_MUTE \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = NULL, \ + .index = 0, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = bind_pin_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } -#ifdef CONFIG_SND_HDA_POWER_SAVE - struct hda_loopback_check loopback; -#endif +static struct snd_kcontrol_new via_control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + HDA_CODEC_MUTE(NULL, 0, 0, 0), + ANALOG_INPUT_MUTE, + BIND_PIN_MUTE, }; static hda_nid_t vt1708_adc_nids[2] = { @@ -261,6 +407,27 @@ static hda_nid_t vt1702_adc_nids[3] = { 0x12, 0x20, 0x1F }; +static hda_nid_t vt1718S_adc_nids[2] = { + /* ADC1-2 */ + 0x10, 0x11 +}; + +static hda_nid_t vt1716S_adc_nids[2] = { + /* ADC1-2 */ + 0x13, 0x14 +}; + +static hda_nid_t vt2002P_adc_nids[2] = { + /* ADC1-2 */ + 0x10, 0x11 +}; + +static hda_nid_t vt1812_adc_nids[2] = { + /* ADC1-2 */ + 0x10, 0x11 +}; + + /* add dynamic controls */ static int via_add_control(struct via_spec *spec, int type, const char *name, unsigned long val) @@ -271,10 +438,12 @@ static int via_add_control(struct via_spec *spec, int type, const char *name, knew = snd_array_new(&spec->kctls); if (!knew) return -ENOMEM; - *knew = vt1708_control_templates[type]; + *knew = via_control_templates[type]; knew->name = kstrdup(name, GFP_KERNEL); if (!knew->name) return -ENOMEM; + if (get_amp_nid_(val)) + knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); knew->private_value = val; return 0; } @@ -293,8 +462,8 @@ static void via_free_kctls(struct hda_codec *codec) } /* create input playback/capture controls for the given pin */ -static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin, - const char *ctlname, int idx, int mix_nid) +static int via_new_analog_input(struct via_spec *spec, const char *ctlname, + int idx, int mix_nid) { char name[32]; int err; @@ -305,7 +474,7 @@ static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin, if (err < 0) return err; sprintf(name, "%s Playback Switch", ctlname); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; @@ -322,7 +491,7 @@ static void via_auto_set_output_and_unmute(struct hda_codec *codec, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, nid, 0, + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, 0x02); } @@ -343,10 +512,13 @@ static void via_auto_init_hp_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; hda_nid_t pin; + int i; - pin = spec->autocfg.hp_pins[0]; - if (pin) /* connect to front */ - via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + for (i = 0; i < spec->autocfg.hp_outs; i++) { + pin = spec->autocfg.hp_pins[i]; + if (pin) /* connect to front */ + via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + } } static void via_auto_init_analog_input(struct hda_codec *codec) @@ -364,6 +536,510 @@ static void via_auto_init_analog_input(struct hda_codec *codec) } } + +static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); + +static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, + unsigned int *affected_parm) +{ + unsigned parm; + unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); + unsigned no_presence = (def_conf & AC_DEFCFG_MISC) + >> AC_DEFCFG_MISC_SHIFT + & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ + unsigned present = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_SENSE, 0) >> 31; + struct via_spec *spec = codec->spec; + if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) + || ((no_presence || present) + && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { + *affected_parm = AC_PWRST_D0; /* if it's connected */ + parm = AC_PWRST_D0; + } else + parm = AC_PWRST_D3; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); +} + +static void set_jack_power_state(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int imux_is_smixer; + unsigned int parm; + + if (spec->codec_type == VT1702) { + imux_is_smixer = snd_hda_codec_read( + codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; + /* inputs */ + /* PW 1/2/5 (14h/15h/18h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x14, &parm); + set_pin_power_state(codec, 0x15, &parm); + set_pin_power_state(codec, 0x18, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */ + /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ + snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* outputs */ + /* PW 3/4 (16h/17h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x16, &parm); + set_pin_power_state(codec, 0x17, &parm); + /* MW0 (1ah), AOW 0/1 (10h/1dh) */ + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, + parm); + } else if (spec->codec_type == VT1708B_8CH + || spec->codec_type == VT1708B_4CH + || spec->codec_type == VT1708S) { + /* SW0 (17h) = stereo mixer */ + int is_8ch = spec->codec_type != VT1708B_4CH; + imux_is_smixer = snd_hda_codec_read( + codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) + == ((spec->codec_type == VT1708S) ? 5 : 0); + /* inputs */ + /* PW 1/2/5 (1ah/1bh/1eh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1a, &parm); + set_pin_power_state(codec, 0x1b, &parm); + set_pin_power_state(codec, 0x1e, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* SW0 (17h), AIW 0/1 (13h/14h) */ + snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* outputs */ + /* PW0 (19h), SW1 (18h), AOW1 (11h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x19, &parm); + snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW6 (22h), SW2 (26h), AOW2 (24h) */ + if (is_8ch) { + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x22, &parm); + snd_hda_codec_write(codec, 0x26, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x24, 0, + AC_VERB_SET_POWER_STATE, parm); + } + + /* PW 3/4/7 (1ch/1dh/23h) */ + parm = AC_PWRST_D3; + /* force to D0 for internal Speaker */ + set_pin_power_state(codec, 0x1c, &parm); + set_pin_power_state(codec, 0x1d, &parm); + if (is_8ch) + set_pin_power_state(codec, 0x23, &parm); + /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ + snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, + parm); + if (is_8ch) { + snd_hda_codec_write(codec, 0x25, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x27, 0, + AC_VERB_SET_POWER_STATE, parm); + } + } else if (spec->codec_type == VT1718S) { + /* MUX6 (1eh) = stereo mixer */ + imux_is_smixer = snd_hda_codec_read( + codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; + /* inputs */ + /* PW 5/6/7 (29h/2ah/2bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x29, &parm); + set_pin_power_state(codec, 0x2a, &parm); + set_pin_power_state(codec, 0x2b, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ + snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* outputs */ + /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x27, &parm); + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW2 (26h), AOW2 (ah) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x26, &parm); + snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW0/1 (24h/25h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x24, &parm); + set_pin_power_state(codec, 0x25, &parm); + if (!spec->hp_independent_mode) /* check for redirected HP */ + set_pin_power_state(codec, 0x28, &parm); + snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, + parm); + /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ + snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + if (spec->hp_independent_mode) { + /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x28, &parm); + snd_hda_codec_write(codec, 0x1b, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0xc, 0, + AC_VERB_SET_POWER_STATE, parm); + } + } else if (spec->codec_type == VT1716S) { + unsigned int mono_out, present; + /* SW0 (17h) = stereo mixer */ + imux_is_smixer = snd_hda_codec_read( + codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; + /* inputs */ + /* PW 1/2/5 (1ah/1bh/1eh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1a, &parm); + set_pin_power_state(codec, 0x1b, &parm); + set_pin_power_state(codec, 0x1e, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* SW0 (17h), AIW0(13h) */ + snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, + parm); + + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1e, &parm); + /* PW11 (22h) */ + if (spec->dmic_enabled) + set_pin_power_state(codec, 0x22, &parm); + else + snd_hda_codec_write( + codec, 0x22, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + + /* SW2(26h), AIW1(14h) */ + snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* outputs */ + /* PW0 (19h), SW1 (18h), AOW1 (11h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x19, &parm); + /* Smart 5.1 PW2(1bh) */ + if (spec->smart51_enabled) + set_pin_power_state(codec, 0x1b, &parm); + snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW7 (23h), SW3 (27h), AOW3 (25h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x23, &parm); + /* Smart 5.1 PW1(1ah) */ + if (spec->smart51_enabled) + set_pin_power_state(codec, 0x1a, &parm); + snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* Smart 5.1 PW5(1eh) */ + if (spec->smart51_enabled) + set_pin_power_state(codec, 0x1e, &parm); + snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* Mono out */ + /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ + present = snd_hda_codec_read( + codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + if (present) + mono_out = 0; + else { + present = snd_hda_codec_read( + codec, 0x1d, 0, AC_VERB_GET_PIN_SENSE, 0) + & 0x80000000; + if (!spec->hp_independent_mode && present) + mono_out = 0; + else + mono_out = 1; + } + parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; + snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW 3/4 (1ch/1dh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1c, &parm); + set_pin_power_state(codec, 0x1d, &parm); + /* HP Independent Mode, power on AOW3 */ + if (spec->hp_independent_mode) + snd_hda_codec_write(codec, 0x25, 0, + AC_VERB_SET_POWER_STATE, parm); + + /* force to D0 for internal Speaker */ + /* MW0 (16h), AOW0 (10h) */ + snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, + mono_out ? AC_PWRST_D0 : parm); + } else if (spec->codec_type == VT2002P) { + unsigned int present; + /* MUX9 (1eh) = stereo mixer */ + imux_is_smixer = snd_hda_codec_read( + codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; + /* inputs */ + /* PW 5/6/7 (29h/2ah/2bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x29, &parm); + set_pin_power_state(codec, 0x2a, &parm); + set_pin_power_state(codec, 0x2b, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ + snd_hda_codec_write(codec, 0x1e, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x1f, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x10, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x11, 0, + AC_VERB_SET_POWER_STATE, parm); + + /* outputs */ + /* AOW0 (8h)*/ + snd_hda_codec_write(codec, 0x8, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + + /* PW4 (26h), MW4 (1ch), MUX4(37h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x26, &parm); + snd_hda_codec_write(codec, 0x1c, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x37, + 0, AC_VERB_SET_POWER_STATE, parm); + + /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x25, &parm); + snd_hda_codec_write(codec, 0x19, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x35, 0, + AC_VERB_SET_POWER_STATE, parm); + if (spec->hp_independent_mode) { + snd_hda_codec_write(codec, 0x9, 0, + AC_VERB_SET_POWER_STATE, parm); + } + + /* Class-D */ + /* PW0 (24h), MW0(18h), MUX0(34h) */ + present = snd_hda_codec_read( + codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x24, &parm); + if (present) { + snd_hda_codec_write( + codec, 0x18, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + snd_hda_codec_write( + codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + } else { + snd_hda_codec_write( + codec, 0x18, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + snd_hda_codec_write( + codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + } + + /* Mono Out */ + /* PW15 (31h), MW8(17h), MUX8(3bh) */ + present = snd_hda_codec_read( + codec, 0x26, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x31, &parm); + if (present) { + snd_hda_codec_write( + codec, 0x17, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + snd_hda_codec_write( + codec, 0x3b, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + } else { + snd_hda_codec_write( + codec, 0x17, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + snd_hda_codec_write( + codec, 0x3b, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + } + + /* MW9 (21h) */ + if (imux_is_smixer || !is_aa_path_mute(codec)) + snd_hda_codec_write( + codec, 0x21, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + else + snd_hda_codec_write( + codec, 0x21, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + } else if (spec->codec_type == VT1812) { + unsigned int present; + /* MUX10 (1eh) = stereo mixer */ + imux_is_smixer = snd_hda_codec_read( + codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; + /* inputs */ + /* PW 5/6/7 (29h/2ah/2bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x29, &parm); + set_pin_power_state(codec, 0x2a, &parm); + set_pin_power_state(codec, 0x2b, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ + snd_hda_codec_write(codec, 0x1e, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x1f, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x10, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x11, 0, + AC_VERB_SET_POWER_STATE, parm); + + /* outputs */ + /* AOW0 (8h)*/ + snd_hda_codec_write(codec, 0x8, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + + /* PW4 (28h), MW4 (18h), MUX4(38h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x28, &parm); + snd_hda_codec_write(codec, 0x18, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x38, 0, + AC_VERB_SET_POWER_STATE, parm); + + /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x25, &parm); + snd_hda_codec_write(codec, 0x15, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x35, 0, + AC_VERB_SET_POWER_STATE, parm); + if (spec->hp_independent_mode) { + snd_hda_codec_write(codec, 0x9, 0, + AC_VERB_SET_POWER_STATE, parm); + } + + /* Internal Speaker */ + /* PW0 (24h), MW0(14h), MUX0(34h) */ + present = snd_hda_codec_read( + codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x24, &parm); + if (present) { + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + snd_hda_codec_write(codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + } else { + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + snd_hda_codec_write(codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + } + /* Mono Out */ + /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ + present = snd_hda_codec_read( + codec, 0x28, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x31, &parm); + if (present) { + snd_hda_codec_write(codec, 0x1c, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + snd_hda_codec_write(codec, 0x3c, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + snd_hda_codec_write(codec, 0x3e, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + } else { + snd_hda_codec_write(codec, 0x1c, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + snd_hda_codec_write(codec, 0x3c, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + snd_hda_codec_write(codec, 0x3e, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + } + + /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x33, &parm); + snd_hda_codec_write(codec, 0x1d, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x3d, 0, + AC_VERB_SET_POWER_STATE, parm); + + /* MW9 (21h) */ + if (imux_is_smixer || !is_aa_path_mute(codec)) + snd_hda_codec_write( + codec, 0x21, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + else + snd_hda_codec_write( + codec, 0x21, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + } +} + /* * input MUX handling */ @@ -395,6 +1071,14 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol, if (!spec->mux_nids[adc_idx]) return -EINVAL; + /* switch to D0 beofre change index */ + if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, + AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) + snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + /* update jack power state */ + set_jack_power_state(codec); + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]); @@ -413,16 +1097,74 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - hda_nid_t nid = spec->autocfg.hp_pins[0]; - unsigned int pinsel = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_SEL, - 0x00); - + hda_nid_t nid; + unsigned int pinsel; + + switch (spec->codec_type) { + case VT1718S: + nid = 0x34; + break; + case VT2002P: + nid = 0x35; + break; + case VT1812: + nid = 0x3d; + break; + default: + nid = spec->autocfg.hp_pins[0]; + break; + } + /* use !! to translate conn sel 2 for VT1718S */ + pinsel = !!snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONNECT_SEL, + 0x00); ucontrol->value.enumerated.item[0] = pinsel; return 0; } +static void activate_ctl(struct hda_codec *codec, const char *name, int active) +{ + struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); + if (ctl) { + ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + ctl->vd[0].access |= active + ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(codec->bus->card, + SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); + } +} + +static int update_side_mute_status(struct hda_codec *codec) +{ + /* mute side channel */ + struct via_spec *spec = codec->spec; + unsigned int parm = spec->hp_independent_mode + ? AMP_OUT_MUTE : AMP_OUT_UNMUTE; + hda_nid_t sw3; + + switch (spec->codec_type) { + case VT1708: + sw3 = 0x1b; + break; + case VT1709_10CH: + sw3 = 0x29; + break; + case VT1708B_8CH: + case VT1708S: + sw3 = 0x27; + break; + default: + sw3 = 0; + break; + } + + if (sw3) + snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE, + parm); + return 0; +} + static int via_independent_hp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -430,47 +1172,46 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, struct via_spec *spec = codec->spec; hda_nid_t nid = spec->autocfg.hp_pins[0]; unsigned int pinsel = ucontrol->value.enumerated.item[0]; - unsigned int con_nid = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_LIST, 0) & 0xff; - - if (con_nid == spec->multiout.hp_nid) { - if (pinsel == 0) { - if (!spec->hp_independent_mode) { - if (spec->multiout.num_dacs > 1) - spec->multiout.num_dacs -= 1; - spec->hp_independent_mode = 1; - } - } else if (pinsel == 1) { - if (spec->hp_independent_mode) { - if (spec->multiout.num_dacs > 1) - spec->multiout.num_dacs += 1; - spec->hp_independent_mode = 0; - } - } - } else { - if (pinsel == 0) { - if (spec->hp_independent_mode) { - if (spec->multiout.num_dacs > 1) - spec->multiout.num_dacs += 1; - spec->hp_independent_mode = 0; - } - } else if (pinsel == 1) { - if (!spec->hp_independent_mode) { - if (spec->multiout.num_dacs > 1) - spec->multiout.num_dacs -= 1; - spec->hp_independent_mode = 1; - } - } + /* Get Independent Mode index of headphone pin widget */ + spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel + ? 1 : 0; + + switch (spec->codec_type) { + case VT1718S: + nid = 0x34; + pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */ + spec->multiout.num_dacs = 4; + break; + case VT2002P: + nid = 0x35; + break; + case VT1812: + nid = 0x3d; + break; + default: + nid = spec->autocfg.hp_pins[0]; + break; + } + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); + + if (spec->multiout.hp_nid && spec->multiout.hp_nid + != spec->multiout.dac_nids[HDA_FRONT]) + snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, + 0, 0, 0); + + update_side_mute_status(codec); + /* update HP volume/swtich active state */ + if (spec->codec_type == VT1708S + || spec->codec_type == VT1702 + || spec->codec_type == VT1718S + || spec->codec_type == VT1716S + || spec->codec_type == VT2002P + || spec->codec_type == VT1812) { + activate_ctl(codec, "Headphone Playback Volume", + spec->hp_independent_mode); + activate_ctl(codec, "Headphone Playback Switch", + spec->hp_independent_mode); } - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, - pinsel); - - if (spec->multiout.hp_nid && - spec->multiout.hp_nid != spec->multiout.dac_nids[HDA_FRONT]) - snd_hda_codec_setup_stream(codec, - spec->multiout.hp_nid, - 0, 0, 0); - return 0; } @@ -486,6 +1227,175 @@ static struct snd_kcontrol_new via_hp_mixer[] = { { } /* end */ }; +static void notify_aa_path_ctls(struct hda_codec *codec) +{ + int i; + struct snd_ctl_elem_id id; + const char *labels[] = {"Mic", "Front Mic", "Line"}; + + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + for (i = 0; i < ARRAY_SIZE(labels); i++) { + sprintf(id.name, "%s Playback Volume", labels[i]); + snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, + &id); + } +} + +static void mute_aa_path(struct hda_codec *codec, int mute) +{ + struct via_spec *spec = codec->spec; + hda_nid_t nid_mixer; + int start_idx; + int end_idx; + int i; + /* get nid of MW0 and start & end index */ + switch (spec->codec_type) { + case VT1708: + nid_mixer = 0x17; + start_idx = 2; + end_idx = 4; + break; + case VT1709_10CH: + case VT1709_6CH: + nid_mixer = 0x18; + start_idx = 2; + end_idx = 4; + break; + case VT1708B_8CH: + case VT1708B_4CH: + case VT1708S: + case VT1716S: + nid_mixer = 0x16; + start_idx = 2; + end_idx = 4; + break; + default: + return; + } + /* check AA path's mute status */ + for (i = start_idx; i <= end_idx; i++) { + int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; + snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i, + HDA_AMP_MUTE, val); + } +} +static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) +{ + int res = 0; + int index; + for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) { + if (pin == spec->autocfg.input_pins[index]) { + res = 1; + break; + } + } + return res; +} + +static int via_smart51_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int via_smart51_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; + int on = 1; + int i; + + for (i = 0; i < ARRAY_SIZE(index); i++) { + hda_nid_t nid = spec->autocfg.input_pins[index[i]]; + if (nid) { + int ctl = + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, + 0); + if (i == AUTO_PIN_FRONT_MIC + && spec->hp_independent_mode + && spec->codec_type != VT1718S) + continue; /* ignore FMic for independent HP */ + if (ctl & AC_PINCTL_IN_EN + && !(ctl & AC_PINCTL_OUT_EN)) + on = 0; + } + } + *ucontrol->value.integer.value = on; + return 0; +} + +static int via_smart51_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int out_in = *ucontrol->value.integer.value + ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; + int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; + int i; + + for (i = 0; i < ARRAY_SIZE(index); i++) { + hda_nid_t nid = spec->autocfg.input_pins[index[i]]; + if (i == AUTO_PIN_FRONT_MIC + && spec->hp_independent_mode + && spec->codec_type != VT1718S) + continue; /* don't retask FMic for independent HP */ + if (nid) { + unsigned int parm = snd_hda_codec_read( + codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); + parm |= out_in; + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + parm); + if (out_in == AC_PINCTL_OUT_EN) { + mute_aa_path(codec, 1); + notify_aa_path_ctls(codec); + } + if (spec->codec_type == VT1718S) + snd_hda_codec_amp_stereo( + codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, + HDA_AMP_UNMUTE); + } + if (i == AUTO_PIN_FRONT_MIC) { + if (spec->codec_type == VT1708S + || spec->codec_type == VT1716S) { + /* input = index 1 (AOW3) */ + snd_hda_codec_write( + codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, 1); + snd_hda_codec_amp_stereo( + codec, nid, HDA_OUTPUT, + 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); + } + } + } + spec->smart51_enabled = *ucontrol->value.integer.value; + set_jack_power_state(codec); + return 1; +} + +static struct snd_kcontrol_new via_smart51_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Smart 5.1", + .count = 1, + .info = via_smart51_info, + .get = via_smart51_get, + .put = via_smart51_put, + }, + {} /* end */ +}; + /* capture mixer elements */ static struct snd_kcontrol_new vt1708_capture_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), @@ -506,6 +1416,112 @@ static struct snd_kcontrol_new vt1708_capture_mixer[] = { }, { } /* end */ }; + +/* check AA path's mute statue */ +static int is_aa_path_mute(struct hda_codec *codec) +{ + int mute = 1; + hda_nid_t nid_mixer; + int start_idx; + int end_idx; + int i; + struct via_spec *spec = codec->spec; + /* get nid of MW0 and start & end index */ + switch (spec->codec_type) { + case VT1708B_8CH: + case VT1708B_4CH: + case VT1708S: + case VT1716S: + nid_mixer = 0x16; + start_idx = 2; + end_idx = 4; + break; + case VT1702: + nid_mixer = 0x1a; + start_idx = 1; + end_idx = 3; + break; + case VT1718S: + nid_mixer = 0x21; + start_idx = 1; + end_idx = 3; + break; + case VT2002P: + case VT1812: + nid_mixer = 0x21; + start_idx = 0; + end_idx = 2; + break; + default: + return 0; + } + /* check AA path's mute status */ + for (i = start_idx; i <= end_idx; i++) { + unsigned int con_list = snd_hda_codec_read( + codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); + int shift = 8 * (i % 4); + hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; + unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); + if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { + /* check mute status while the pin is connected */ + int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, + HDA_INPUT, i) >> 7; + int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, + HDA_INPUT, i) >> 7; + if (!mute_l || !mute_r) { + mute = 0; + break; + } + } + } + return mute; +} + +/* enter/exit analog low-current mode */ +static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) +{ + struct via_spec *spec = codec->spec; + static int saved_stream_idle = 1; /* saved stream idle status */ + int enable = is_aa_path_mute(codec); + unsigned int verb = 0; + unsigned int parm = 0; + + if (stream_idle == -1) /* stream status did not change */ + enable = enable && saved_stream_idle; + else { + enable = enable && stream_idle; + saved_stream_idle = stream_idle; + } + + /* decide low current mode's verb & parameter */ + switch (spec->codec_type) { + case VT1708B_8CH: + case VT1708B_4CH: + verb = 0xf70; + parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ + break; + case VT1708S: + case VT1718S: + case VT1716S: + verb = 0xf73; + parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ + break; + case VT1702: + verb = 0xf73; + parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ + break; + case VT2002P: + case VT1812: + verb = 0xf93; + parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ + break; + default: + return; /* other codecs are not supported */ + } + /* send verb */ + snd_hda_codec_write(codec, codec->afg, 0, verb, parm); +} + /* * generic initialization of ADC, input mixers and output mixers */ @@ -534,9 +1550,9 @@ static struct hda_verb vt1708_volume_init_verbs[] = { {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* Setup default input to PW4 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, + + /* Setup default input MW0 to PW4 */ + {0x20, AC_VERB_SET_CONNECT_SEL, 0}, /* PW9 Output enable */ {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, { } @@ -547,30 +1563,13 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; + int idle = substream->pstr->substream_opened == 1 + && substream->ref_count == 0; + analog_low_current_mode(codec, idle); return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); } -static int via_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 via_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - - static void playback_multi_pcm_prep_0(struct hda_codec *codec, unsigned int stream_tag, unsigned int format, @@ -615,8 +1614,8 @@ static void playback_multi_pcm_prep_0(struct hda_codec *codec, snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format); - if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && - !spec->hp_independent_mode) + if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] + && !spec->hp_independent_mode) /* headphone out will just decode front left/right (stereo) */ snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); @@ -658,7 +1657,7 @@ static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); } - + vt1708_start_hp_work(spec); return 0; } @@ -698,7 +1697,7 @@ static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0); } - + vt1708_stop_hp_work(spec); return 0; } @@ -779,7 +1778,7 @@ static struct hda_pcm_stream vt1708_pcm_analog_playback = { }; static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { - .substreams = 1, + .substreams = 2, .channels_min = 2, .channels_max = 8, .nid = 0x10, /* NID to query formats and rates */ @@ -790,8 +1789,8 @@ static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { .formats = SNDRV_PCM_FMTBIT_S16_LE, .ops = { .open = via_playback_pcm_open, - .prepare = via_playback_pcm_prepare, - .cleanup = via_playback_pcm_cleanup + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup }, }; @@ -853,6 +1852,11 @@ static int via_build_controls(struct hda_codec *codec) if (err < 0) return err; } + + /* init power states */ + set_jack_power_state(codec); + analog_low_current_mode(codec, 1); + via_free_kctls(codec); /* no longer needed */ return 0; } @@ -866,8 +1870,10 @@ static int via_build_pcms(struct hda_codec *codec) codec->pcm_info = info; info->name = spec->stream_name_analog; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback); - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + *(spec->stream_analog_playback); + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.dac_nids[0]; info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; @@ -904,20 +1910,62 @@ static void via_free(struct hda_codec *codec) return; via_free_kctls(codec); + vt1708_stop_hp_work(spec); kfree(codec->spec); } /* mute internal speaker if HP is plugged */ static void via_hp_automute(struct hda_codec *codec) { - unsigned int present; + unsigned int present = 0; struct via_spec *spec = codec->spec; present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], - HDA_OUTPUT, 0, HDA_AMP_MUTE, - present ? HDA_AMP_MUTE : 0); + + if (!spec->hp_independent_mode) { + struct snd_ctl_elem_id id; + /* auto mute */ + snd_hda_codec_amp_stereo( + codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + /* notify change */ + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "Front Playback Switch"); + snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, + &id); + } +} + +/* mute mono out if HP or Line out is plugged */ +static void via_mono_automute(struct hda_codec *codec) +{ + unsigned int hp_present, lineout_present; + struct via_spec *spec = codec->spec; + + if (spec->codec_type != VT1716S) + return; + + lineout_present = snd_hda_codec_read( + codec, spec->autocfg.line_out_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + /* Mute Mono Out if Line Out is plugged */ + if (lineout_present) { + snd_hda_codec_amp_stereo( + codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); + return; + } + + hp_present = snd_hda_codec_read( + codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + if (!spec->hp_independent_mode) + snd_hda_codec_amp_stereo( + codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, + hp_present ? HDA_AMP_MUTE : 0); } static void via_gpio_control(struct hda_codec *codec) @@ -968,15 +2016,86 @@ static void via_gpio_control(struct hda_codec *codec) } } +/* mute Internal-Speaker if HP is plugged */ +static void via_speaker_automute(struct hda_codec *codec) +{ + unsigned int hp_present; + struct via_spec *spec = codec->spec; + + if (spec->codec_type != VT2002P && spec->codec_type != VT1812) + return; + + hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + if (!spec->hp_independent_mode) { + struct snd_ctl_elem_id id; + snd_hda_codec_amp_stereo( + codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0, + HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); + /* notify change */ + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "Speaker Playback Switch"); + snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, + &id); + } +} + +/* mute line-out and internal speaker if HP is plugged */ +static void via_hp_bind_automute(struct hda_codec *codec) +{ + /* use long instead of int below just to avoid an internal compiler + * error with gcc 4.0.x + */ + unsigned long hp_present, present = 0; + struct via_spec *spec = codec->spec; + int i; + + if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) + return; + + hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + present = snd_hda_codec_read(codec, spec->autocfg.line_out_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + if (!spec->hp_independent_mode) { + /* Mute Line-Outs */ + for (i = 0; i < spec->autocfg.line_outs; i++) + snd_hda_codec_amp_stereo( + codec, spec->autocfg.line_out_pins[i], + HDA_OUTPUT, 0, + HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); + if (hp_present) + present = hp_present; + } + /* Speakers */ + for (i = 0; i < spec->autocfg.speaker_outs; i++) + snd_hda_codec_amp_stereo( + codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + + /* unsolicited event for jack sensing */ static void via_unsol_event(struct hda_codec *codec, unsigned int res) { res >>= 26; - if (res == VIA_HP_EVENT) + if (res & VIA_HP_EVENT) via_hp_automute(codec); - else if (res == VIA_GPIO_EVENT) + if (res & VIA_GPIO_EVENT) via_gpio_control(codec); + if (res & VIA_JACK_EVENT) + set_jack_power_state(codec); + if (res & VIA_MONO_EVENT) + via_mono_automute(codec); + if (res & VIA_SPEAKER_EVENT) + via_speaker_automute(codec); + if (res & VIA_BIND_HP_EVENT) + via_hp_bind_automute(codec); } static int via_init(struct hda_codec *codec) @@ -986,6 +2105,10 @@ static int via_init(struct hda_codec *codec) for (i = 0; i < spec->num_iverbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); + spec->codec_type = get_codec_type(codec); + if (spec->codec_type == VT1708BCE) + spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost + same */ /* Lydia Add for EAPD enable */ if (!spec->dig_in_nid) { /* No Digital In connection */ if (spec->dig_in_pin) { @@ -1003,8 +2126,17 @@ static int via_init(struct hda_codec *codec) if (spec->slave_dig_outs[0]) codec->slave_dig_outs = spec->slave_dig_outs; - return 0; + return 0; +} + +#ifdef SND_HDA_NEEDS_RESUME +static int via_suspend(struct hda_codec *codec, pm_message_t state) +{ + struct via_spec *spec = codec->spec; + vt1708_stop_hp_work(spec); + return 0; } +#endif #ifdef CONFIG_SND_HDA_POWER_SAVE static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) @@ -1021,6 +2153,9 @@ static struct hda_codec_ops via_patch_ops = { .build_pcms = via_build_pcms, .init = via_init, .free = via_free, +#ifdef SND_HDA_NEEDS_RESUME + .suspend = via_suspend, +#endif #ifdef CONFIG_SND_HDA_POWER_SAVE .check_power_status = via_check_power_status, #endif @@ -1036,8 +2171,8 @@ static int vt1708_auto_fill_dac_nids(struct via_spec *spec, spec->multiout.num_dacs = cfg->line_outs; spec->multiout.dac_nids = spec->private_dac_nids; - - for(i = 0; i < 4; i++) { + + for (i = 0; i < 4; i++) { nid = cfg->line_out_pins[i]; if (nid) { /* config dac list */ @@ -1067,7 +2202,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, { char name[32]; static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; - hda_nid_t nid, nid_vol = 0; + hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b}; int i, err; for (i = 0; i <= AUTO_SEQ_SIDE; i++) { @@ -1075,9 +2210,8 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, if (!nid) continue; - - if (i != AUTO_SEQ_FRONT) - nid_vol = 0x18 + i; + + nid_vol = nid_vols[i]; if (i == AUTO_SEQ_CENLFE) { /* Center/LFE */ @@ -1105,21 +2239,21 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, HDA_OUTPUT)); if (err < 0) return err; - } else if (i == AUTO_SEQ_FRONT){ + } else if (i == AUTO_SEQ_FRONT) { /* add control to mixer index 0 */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_INPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_INPUT)); if (err < 0) return err; - + /* add control to PW3 */ sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, @@ -1178,6 +2312,7 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ + spec->hp_independent_mode_index = 1; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", @@ -1218,7 +2353,7 @@ static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec, case 0x1d: /* Mic */ idx = 2; break; - + case 0x1e: /* Line In */ idx = 3; break; @@ -1231,8 +2366,7 @@ static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec, idx = 1; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], - idx, 0x17); + err = via_new_analog_input(spec, labels[i], idx, 0x17); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; @@ -1260,16 +2394,60 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) def_conf = snd_hda_codec_get_pincfg(codec, nid); seqassoc = (unsigned char) get_defcfg_association(def_conf); seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); - if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) { - if (seqassoc == 0xff) { - def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); - snd_hda_codec_set_pincfg(codec, nid, def_conf); - } + if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE + && (seqassoc == 0xf0 || seqassoc == 0xff)) { + def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); + snd_hda_codec_set_pincfg(codec, nid, def_conf); } return; } +static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + + if (spec->codec_type != VT1708) + return 0; + spec->vt1708_jack_detectect = + !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); + ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect; + return 0; +} + +static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int change; + + if (spec->codec_type != VT1708) + return 0; + spec->vt1708_jack_detectect = ucontrol->value.integer.value[0]; + change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) + == !spec->vt1708_jack_detectect; + if (spec->vt1708_jack_detectect) { + mute_aa_path(codec, 1); + notify_aa_path_ctls(codec); + } + return change; +} + +static struct snd_kcontrol_new vt1708_jack_detectect[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Jack Detect", + .count = 1, + .info = snd_ctl_boolean_mono_info, + .get = vt1708_jack_detectect_get, + .put = vt1708_jack_detectect_put, + }, + {} /* end */ +}; + static int vt1708_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -1297,6 +2475,10 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg); if (err < 0) return err; + /* add jack detect on/off control */ + err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect); + if (err < 0) + return err; spec->multiout.max_channels = spec->multiout.num_dacs * 2; @@ -1316,19 +2498,45 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) spec->mixers[spec->num_mixers++] = via_hp_mixer; + spec->mixers[spec->num_mixers++] = via_smart51_mixer; return 1; } /* init callback for auto-configuration model -- overriding the default init */ static int via_auto_init(struct hda_codec *codec) { + struct via_spec *spec = codec->spec; + via_init(codec); via_auto_init_multi_out(codec); via_auto_init_hp_out(codec); via_auto_init_analog_input(codec); + if (spec->codec_type == VT2002P || spec->codec_type == VT1812) { + via_hp_bind_automute(codec); + } else { + via_hp_automute(codec); + via_speaker_automute(codec); + } + return 0; } +static void vt1708_update_hp_jack_state(struct work_struct *work) +{ + struct via_spec *spec = container_of(work, struct via_spec, + vt1708_hp_work.work); + if (spec->codec_type != VT1708) + return; + /* if jack state toggled */ + if (spec->vt1708_hp_present + != (snd_hda_codec_read(spec->codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) >> 31)) { + spec->vt1708_hp_present ^= 1; + via_hp_automute(spec->codec); + } + vt1708_start_hp_work(spec); +} + static int get_mux_nids(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -1378,7 +2586,7 @@ static int patch_vt1708(struct hda_codec *codec) "from BIOS. Using genenic mode...\n"); } - + spec->stream_name_analog = "VT1708 Analog"; spec->stream_analog_playback = &vt1708_pcm_analog_playback; /* disable 32bit format on VT1708 */ @@ -1390,7 +2598,7 @@ static int patch_vt1708(struct hda_codec *codec) spec->stream_digital_playback = &vt1708_pcm_digital_playback; spec->stream_digital_capture = &vt1708_pcm_digital_capture; - + if (!spec->adc_nids && spec->input_mux) { spec->adc_nids = vt1708_adc_nids; spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); @@ -1405,7 +2613,8 @@ static int patch_vt1708(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1708_loopbacks; #endif - + spec->codec = codec; + INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); return 0; } @@ -1433,7 +2642,8 @@ static struct snd_kcontrol_new vt1709_capture_mixer[] = { }; static struct hda_verb vt1709_uniwill_init_verbs[] = { - {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, + {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, { } }; @@ -1473,8 +2683,8 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = { {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Set input of PW4 as AOW4 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* Set input of PW4 as MW0 */ + {0x20, AC_VERB_SET_CONNECT_SEL, 0}, /* PW9 Output enable */ {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, { } @@ -1487,8 +2697,8 @@ static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { .nid = 0x10, /* NID to query formats and rates */ .ops = { .open = via_playback_pcm_open, - .prepare = via_playback_pcm_prepare, - .cleanup = via_playback_pcm_cleanup + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, }, }; @@ -1499,8 +2709,8 @@ static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { .nid = 0x10, /* NID to query formats and rates */ .ops = { .open = via_playback_pcm_open, - .prepare = via_playback_pcm_prepare, - .cleanup = via_playback_pcm_cleanup + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, }, }; @@ -1575,11 +2785,11 @@ static int vt1709_auto_fill_dac_nids(struct via_spec *spec, spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ } else if (cfg->line_outs == 3) { /* 6 channels */ - for(i = 0; i < cfg->line_outs; i++) { + for (i = 0; i < cfg->line_outs; i++) { nid = cfg->line_out_pins[i]; if (nid) { /* config dac list */ - switch(i) { + switch (i) { case AUTO_SEQ_FRONT: /* AOW0 */ spec->multiout.dac_nids[i] = 0x10; @@ -1608,56 +2818,58 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, { char name[32]; static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; - hda_nid_t nid = 0; + hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29}; int i, err; for (i = 0; i <= AUTO_SEQ_SIDE; i++) { nid = cfg->line_out_pins[i]; - if (!nid) + if (!nid) continue; + nid_vol = nid_vols[i]; + if (i == AUTO_SEQ_CENLFE) { /* Center/LFE */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); if (err < 0) return err; - } else if (i == AUTO_SEQ_FRONT){ - /* add control to mixer index 0 */ + } else if (i == AUTO_SEQ_FRONT) { + /* ADD control to mixer index 0 */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_INPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_INPUT)); if (err < 0) return err; - + /* add control to PW3 */ sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, @@ -1674,26 +2886,26 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, } else if (i == AUTO_SEQ_SURROUND) { sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); if (err < 0) return err; } else if (i == AUTO_SEQ_SIDE) { sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(0x29, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(0x29, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -1714,6 +2926,7 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) spec->multiout.hp_nid = VT1709_HP_DAC_NID; else if (spec->multiout.num_dacs == 3) /* 6 channels */ spec->multiout.hp_nid = 0; + spec->hp_independent_mode_index = 1; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", @@ -1752,7 +2965,7 @@ static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec, case 0x1d: /* Mic */ idx = 2; break; - + case 0x1e: /* Line In */ idx = 3; break; @@ -1765,8 +2978,7 @@ static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec, idx = 1; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], - idx, 0x18); + err = via_new_analog_input(spec, labels[i], idx, 0x18); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; @@ -1816,6 +3028,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) spec->mixers[spec->num_mixers++] = via_hp_mixer; + spec->mixers[spec->num_mixers++] = via_smart51_mixer; return 1; } @@ -1861,7 +3074,7 @@ static int patch_vt1709_10ch(struct hda_codec *codec) spec->stream_digital_playback = &vt1709_pcm_digital_playback; spec->stream_digital_capture = &vt1709_pcm_digital_capture; - + if (!spec->adc_nids && spec->input_mux) { spec->adc_nids = vt1709_adc_nids; spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); @@ -1955,7 +3168,7 @@ static int patch_vt1709_6ch(struct hda_codec *codec) spec->stream_digital_playback = &vt1709_pcm_digital_playback; spec->stream_digital_capture = &vt1709_pcm_digital_capture; - + if (!spec->adc_nids && spec->input_mux) { spec->adc_nids = vt1709_adc_nids; spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); @@ -2024,7 +3237,7 @@ static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* Setup default input to PW4 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, /* PW9 Output enable */ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* PW10 Input enable */ @@ -2068,10 +3281,29 @@ static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { }; static struct hda_verb vt1708B_uniwill_init_verbs[] = { - {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, + {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, { } }; +static int via_pcm_open_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + int idle = substream->pstr->substream_opened == 1 + && substream->ref_count == 0; + + analog_low_current_mode(codec, idle); + return 0; +} + static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { .substreams = 2, .channels_min = 2, @@ -2080,7 +3312,8 @@ static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { .ops = { .open = via_playback_pcm_open, .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -2102,8 +3335,10 @@ static struct hda_pcm_stream vt1708B_pcm_analog_capture = { .channels_max = 2, .nid = 0x13, /* NID to query formats and rates */ .ops = { + .open = via_pcm_open_close, .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -2260,6 +3495,7 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ + spec->hp_independent_mode_index = 1; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", @@ -2313,8 +3549,7 @@ static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec, idx = 1; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], - idx, 0x16); + err = via_new_analog_input(spec, labels[i], idx, 0x16); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; @@ -2364,6 +3599,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) spec->mixers[spec->num_mixers++] = via_hp_mixer; + spec->mixers[spec->num_mixers++] = via_smart51_mixer; return 1; } @@ -2376,12 +3612,14 @@ static struct hda_amp_list vt1708B_loopbacks[] = { { } /* end */ }; #endif - +static int patch_vt1708S(struct hda_codec *codec); static int patch_vt1708B_8ch(struct hda_codec *codec) { struct via_spec *spec; int err; + if (get_codec_type(codec) == VT1708BCE) + return patch_vt1708S(codec); /* create a codec specific record */ spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -2483,29 +3721,15 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) /* Patch for VT1708S */ -/* VT1708S software backdoor based override for buggy hardware micboost - * setting */ -#define MIC_BOOST_VOLUME(xname, nid) { \ - .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 = mic_boost_volume_info, \ - .get = snd_hda_mixer_amp_volume_get, \ - .put = snd_hda_mixer_amp_volume_put, \ - .tlv = { .c = mic_boost_tlv }, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT) } - /* capture mixer elements */ static struct snd_kcontrol_new vt1708S_capture_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), - MIC_BOOST_VOLUME("Mic Boost Capture Volume", 0x1A), - MIC_BOOST_VOLUME("Front Mic Boost Capture Volume", 0x1E), + HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, + HDA_INPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer @@ -2542,11 +3766,21 @@ static struct hda_verb vt1708S_volume_init_verbs[] = { {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* Enable Mic Boost Volume backdoor */ {0x1, 0xf98, 0x1}, + /* don't bybass mixer */ + {0x1, 0xf88, 0xc0}, { } }; static struct hda_verb vt1708S_uniwill_init_verbs[] = { - {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, + {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, { } }; @@ -2557,8 +3791,9 @@ static struct hda_pcm_stream vt1708S_pcm_analog_playback = { .nid = 0x10, /* NID to query formats and rates */ .ops = { .open = via_playback_pcm_open, - .prepare = via_playback_pcm_prepare, - .cleanup = via_playback_pcm_cleanup + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -2568,8 +3803,10 @@ static struct hda_pcm_stream vt1708S_pcm_analog_capture = { .channels_max = 2, .nid = 0x13, /* NID to query formats and rates */ .ops = { + .open = via_pcm_open_close, .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -2726,6 +3963,7 @@ static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ + spec->hp_independent_mode_index = 1; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", @@ -2780,8 +4018,7 @@ static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec, idx = 1; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], - idx, 0x16); + err = via_new_analog_input(spec, labels[i], idx, 0x16); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; @@ -2852,6 +4089,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) spec->mixers[spec->num_mixers++] = via_hp_mixer; + spec->mixers[spec->num_mixers++] = via_smart51_mixer; return 1; } @@ -2865,6 +4103,16 @@ static struct hda_amp_list vt1708S_loopbacks[] = { }; #endif +static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, + int offset, int num_steps, int step_size) +{ + snd_hda_override_amp_caps(codec, pin, HDA_INPUT, + (offset << AC_AMPCAP_OFFSET_SHIFT) | + (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | + (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); +} + static int patch_vt1708S(struct hda_codec *codec) { struct via_spec *spec; @@ -2890,17 +4138,25 @@ static int patch_vt1708S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; - spec->stream_name_analog = "VT1708S Analog"; + if (codec->vendor_id == 0x11060440) + spec->stream_name_analog = "VT1818S Analog"; + else + spec->stream_name_analog = "VT1708S Analog"; spec->stream_analog_playback = &vt1708S_pcm_analog_playback; spec->stream_analog_capture = &vt1708S_pcm_analog_capture; - spec->stream_name_digital = "VT1708S Digital"; + if (codec->vendor_id == 0x11060440) + spec->stream_name_digital = "VT1818S Digital"; + else + spec->stream_name_digital = "VT1708S Digital"; spec->stream_digital_playback = &vt1708S_pcm_digital_playback; if (!spec->adc_nids && spec->input_mux) { spec->adc_nids = vt1708S_adc_nids; spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); get_mux_nids(codec); + override_mic_boost(codec, 0x1a, 0, 3, 40); + override_mic_boost(codec, 0x1e, 0, 3, 40); spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; spec->num_mixers++; } @@ -2913,6 +4169,16 @@ static int patch_vt1708S(struct hda_codec *codec) spec->loopback.amplist = vt1708S_loopbacks; #endif + /* correct names for VT1708BCE */ + if (get_codec_type(codec) == VT1708BCE) { + kfree(codec->chip_name); + codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); + snprintf(codec->bus->card->mixername, + sizeof(codec->bus->card->mixername), + "%s %s", codec->vendor_name, codec->chip_name); + spec->stream_name_analog = "VT1708BCE Analog"; + spec->stream_name_digital = "VT1708BCE Digital"; + } return 0; } @@ -2967,12 +4233,20 @@ static struct hda_verb vt1702_volume_init_verbs[] = { /* PW6 PW7 Output enable */ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* mixer enable */ + {0x1, 0xF88, 0x3}, + /* GPIO 0~2 */ + {0x1, 0xF82, 0x3F}, { } }; static struct hda_verb vt1702_uniwill_init_verbs[] = { - {0x01, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_GPIO_EVENT}, - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, + {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, { } }; @@ -2984,7 +4258,8 @@ static struct hda_pcm_stream vt1702_pcm_analog_playback = { .ops = { .open = via_playback_pcm_open, .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -2994,8 +4269,10 @@ static struct hda_pcm_stream vt1702_pcm_analog_capture = { .channels_max = 2, .nid = 0x12, /* NID to query formats and rates */ .ops = { + .open = via_pcm_open_close, .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -3065,12 +4342,13 @@ static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) { - int err; - + int err, i; + struct hda_input_mux *imux; + static const char *texts[] = { "ON", "OFF", NULL}; if (!pin) return 0; - spec->multiout.hp_nid = 0x1D; + spec->hp_independent_mode_index = 0; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", @@ -3084,8 +4362,18 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) if (err < 0) return err; - create_hp_imux(spec); + imux = &spec->private_imux[1]; + + /* for hp mode select */ + i = 0; + while (texts[i] != NULL) { + imux->items[imux->num_items].label = texts[i]; + imux->items[imux->num_items].index = i; + imux->num_items++; + i++; + } + spec->hp_mux = &spec->private_imux[1]; return 0; } @@ -3121,8 +4409,7 @@ static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec, idx = 3; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], - labels[i], idx, 0x1A); + err = via_new_analog_input(spec, labels[i], idx, 0x1A); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; @@ -3152,6 +4439,12 @@ static int vt1702_parse_auto_config(struct hda_codec *codec) err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; + /* limit AA path volume to 0 dB */ + snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg); if (err < 0) return err; @@ -3185,8 +4478,6 @@ static int patch_vt1702(struct hda_codec *codec) { struct via_spec *spec; int err; - unsigned int response; - unsigned char control; /* create a codec specific record */ spec = kzalloc(sizeof(*spec), GFP_KERNEL); @@ -3231,17 +4522,1638 @@ static int patch_vt1702(struct hda_codec *codec) spec->loopback.amplist = vt1702_loopbacks; #endif - /* Open backdoor */ - response = snd_hda_codec_read(codec, codec->afg, 0, 0xF8C, 0); - control = (unsigned char)(response & 0xff); - control |= 0x3; - snd_hda_codec_write(codec, codec->afg, 0, 0xF88, control); + return 0; +} + +/* Patch for VT1718S */ - /* Enable GPIO 0&1 for volume&mute control */ - /* Enable GPIO 2 for DMIC-DATA */ - response = snd_hda_codec_read(codec, codec->afg, 0, 0xF84, 0); - control = (unsigned char)((response >> 16) & 0x3f); - snd_hda_codec_write(codec, codec->afg, 0, 0xF82, control); +/* capture mixer elements */ +static struct snd_kcontrol_new vt1718S_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, + HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + .name = "Input Source", + .count = 2, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; + +static struct hda_verb vt1718S_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x11, 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 + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + + /* Setup default input of Front HP to MW9 */ + {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* PW9 PW10 Output enable */ + {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, + {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, + /* PW11 Input enable */ + {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, + /* Enable Boost Volume backdoor */ + {0x1, 0xf88, 0x8}, + /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1c, 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_MUTE(1)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ + {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, + {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* Unmute MW4's index 0 */ + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { } +}; + + +static struct hda_verb vt1718S_uniwill_init_verbs[] = { + {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + { } +}; + +static struct hda_pcm_stream vt1718S_pcm_analog_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 10, + .nid = 0x8, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1718S_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_pcm_open_close, + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1718S_pcm_digital_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream vt1718S_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt1718S_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + int i; + hda_nid_t nid; + + spec->multiout.num_dacs = cfg->line_outs; + + spec->multiout.dac_nids = spec->private_dac_nids; + + for (i = 0; i < 4; i++) { + nid = cfg->line_out_pins[i]; + if (nid) { + /* config dac list */ + switch (i) { + case AUTO_SEQ_FRONT: + spec->multiout.dac_nids[i] = 0x8; + break; + case AUTO_SEQ_CENLFE: + spec->multiout.dac_nids[i] = 0xa; + break; + case AUTO_SEQ_SURROUND: + spec->multiout.dac_nids[i] = 0x9; + break; + case AUTO_SEQ_SIDE: + spec->multiout.dac_nids[i] = 0xb; + break; + } + } + } + + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + char name[32]; + static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; + hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb}; + hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27}; + hda_nid_t nid, nid_vol, nid_mute = 0; + int i, err; + + for (i = 0; i <= AUTO_SEQ_SIDE; i++) { + nid = cfg->line_out_pins[i]; + + if (!nid) + continue; + nid_vol = nid_vols[i]; + nid_mute = nid_mutes[i]; + + if (i == AUTO_SEQ_CENLFE) { + /* Center/LFE */ + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "LFE Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "Center Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "LFE Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else if (i == AUTO_SEQ_FRONT) { + /* Front */ + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else { + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + } + return 0; +} + +static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = 0xc; /* AOW4 */ + spec->hp_independent_mode_index = 1; + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + create_hp_imux(spec); + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + static char *labels[] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL + }; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx = 0; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = 5; + imux->num_items++; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x2b: /* Mic */ + idx = 1; + break; + + case 0x2a: /* Line In */ + idx = 2; + break; + + case 0x29: /* Front Mic */ + idx = 3; + break; + + case 0x2c: /* CD */ + idx = 0; + break; + } + err = via_new_analog_input(spec, labels[i], idx, 0x21); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = idx; + imux->num_items++; + } + return 0; +} + +static int vt1718S_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + + if (err < 0) + return err; + err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) + return 0; /* can't find valid BIOS pin config */ + + err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + fill_dig_outs(codec); + + if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) + spec->dig_in_nid = 0x13; + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + + if (spec->hp_mux) + spec->mixers[spec->num_mixers++] = via_hp_mixer; + + spec->mixers[spec->num_mixers++] = via_smart51_mixer; + + return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1718S_loopbacks[] = { + { 0x21, HDA_INPUT, 1 }, + { 0x21, HDA_INPUT, 2 }, + { 0x21, HDA_INPUT, 3 }, + { 0x21, HDA_INPUT, 4 }, + { } /* end */ +}; +#endif + +static int patch_vt1718S(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt1718S_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; + + if (codec->vendor_id == 0x11060441) + spec->stream_name_analog = "VT2020 Analog"; + else if (codec->vendor_id == 0x11064441) + spec->stream_name_analog = "VT1828S Analog"; + else + spec->stream_name_analog = "VT1718S Analog"; + spec->stream_analog_playback = &vt1718S_pcm_analog_playback; + spec->stream_analog_capture = &vt1718S_pcm_analog_capture; + + if (codec->vendor_id == 0x11060441) + spec->stream_name_digital = "VT2020 Digital"; + else if (codec->vendor_id == 0x11064441) + spec->stream_name_digital = "VT1828S Digital"; + else + spec->stream_name_digital = "VT1718S Digital"; + spec->stream_digital_playback = &vt1718S_pcm_digital_playback; + if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) + spec->stream_digital_capture = &vt1718S_pcm_digital_capture; + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt1718S_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); + get_mux_nids(codec); + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); + spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + codec->patch_ops.unsol_event = via_unsol_event; + +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1718S_loopbacks; +#endif + + return 0; +} + +/* Patch for VT1716S */ + +static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + int index = 0; + + index = snd_hda_codec_read(codec, 0x26, 0, + AC_VERB_GET_CONNECT_SEL, 0); + if (index != -1) + *ucontrol->value.integer.value = index; + + return 0; +} + +static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int index = *ucontrol->value.integer.value; + + snd_hda_codec_write(codec, 0x26, 0, + AC_VERB_SET_CONNECT_SEL, index); + spec->dmic_enabled = index; + set_jack_power_state(codec); + + return 1; +} + +/* capture mixer elements */ +static struct snd_kcontrol_new vt1716S_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, + HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .count = 1, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new vt1716s_dmic_mixer[] = { + HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Mic Capture Switch", + .count = 1, + .info = vt1716s_dmic_info, + .get = vt1716s_dmic_get, + .put = vt1716s_dmic_put, + }, + {} /* end */ +}; + + +/* mono-out mixer elements */ +static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { + HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static struct hda_verb vt1716S_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_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)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + + /* MUX Indices: Stereo Mixer = 5 */ + {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, + + /* Setup default input of PW4 to MW0 */ + {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, + + /* Setup default input of SW1 as MW0 */ + {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, + + /* Setup default input of SW4 as AOW0 */ + {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, + + /* PW9 PW10 Output enable */ + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + + /* Unmute SW1, PW12 */ + {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* PW12 Output enable */ + {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* Enable Boost Volume backdoor */ + {0x1, 0xf8a, 0x80}, + /* don't bybass mixer */ + {0x1, 0xf88, 0xc0}, + /* Enable mono output */ + {0x1, 0xf90, 0x08}, + { } +}; + + +static struct hda_verb vt1716S_uniwill_init_verbs[] = { + {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, + {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + { } +}; + +static struct hda_pcm_stream vt1716S_pcm_analog_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 6, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1716S_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x13, /* NID to query formats and rates */ + .ops = { + .open = via_pcm_open_close, + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1716S_pcm_digital_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup + }, +}; + +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt1716S_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ int i; + hda_nid_t nid; + + spec->multiout.num_dacs = cfg->line_outs; + + spec->multiout.dac_nids = spec->private_dac_nids; + + for (i = 0; i < 3; i++) { + nid = cfg->line_out_pins[i]; + if (nid) { + /* config dac list */ + switch (i) { + case AUTO_SEQ_FRONT: + spec->multiout.dac_nids[i] = 0x10; + break; + case AUTO_SEQ_CENLFE: + spec->multiout.dac_nids[i] = 0x25; + break; + case AUTO_SEQ_SURROUND: + spec->multiout.dac_nids[i] = 0x11; + break; + } + } + } + + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + char name[32]; + static const char *chname[3] = { "Front", "Surround", "C/LFE" }; + hda_nid_t nid_vols[] = {0x10, 0x11, 0x25}; + hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27}; + hda_nid_t nid, nid_vol, nid_mute; + int i, err; + + for (i = 0; i <= AUTO_SEQ_CENLFE; i++) { + nid = cfg->line_out_pins[i]; + + if (!nid) + continue; + + nid_vol = nid_vols[i]; + nid_mute = nid_mutes[i]; + + if (i == AUTO_SEQ_CENLFE) { + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, + "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, + "LFE Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "Center Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "LFE Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else if (i == AUTO_SEQ_FRONT) { + + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, + "Master Front Playback Volume", + HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "Master Front Playback Switch", + HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else { + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + } + return 0; +} + +static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = 0x25; /* AOW3 */ + spec->hp_independent_mode_index = 1; + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + create_hp_imux(spec); + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + static char *labels[] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL + }; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx = 0; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = 5; + imux->num_items++; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x1a: /* Mic */ + idx = 2; + break; + + case 0x1b: /* Line In */ + idx = 3; + break; + + case 0x1e: /* Front Mic */ + idx = 4; + break; + + case 0x1f: /* CD */ + idx = 1; + break; + } + err = via_new_analog_input(spec, labels[i], idx, 0x16); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = idx-1; + imux->num_items++; + } + return 0; +} + +static int vt1716S_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) + return 0; /* can't find valid BIOS pin config */ + + err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + fill_dig_outs(codec); + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + + if (spec->hp_mux) + spec->mixers[spec->num_mixers++] = via_hp_mixer; + + spec->mixers[spec->num_mixers++] = via_smart51_mixer; + + return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1716S_loopbacks[] = { + { 0x16, HDA_INPUT, 1 }, + { 0x16, HDA_INPUT, 2 }, + { 0x16, HDA_INPUT, 3 }, + { 0x16, HDA_INPUT, 4 }, + { } /* end */ +}; +#endif + +static int patch_vt1716S(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt1716S_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; + + spec->stream_name_analog = "VT1716S Analog"; + spec->stream_analog_playback = &vt1716S_pcm_analog_playback; + spec->stream_analog_capture = &vt1716S_pcm_analog_capture; + + spec->stream_name_digital = "VT1716S Digital"; + spec->stream_digital_playback = &vt1716S_pcm_digital_playback; + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt1716S_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids); + get_mux_nids(codec); + override_mic_boost(codec, 0x1a, 0, 3, 40); + override_mic_boost(codec, 0x1e, 0, 3, 40); + spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; + spec->num_mixers++; + } + + spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; + spec->num_mixers++; + + spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + codec->patch_ops.unsol_event = via_unsol_event; + +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1716S_loopbacks; +#endif + + return 0; +} + +/* for vt2002P */ + +/* capture mixer elements */ +static struct snd_kcontrol_new vt2002P_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, + HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; + +static struct hda_verb vt2002P_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x9, 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 + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + + /* MUX Indices: Mic = 0 */ + {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, + {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, + + /* PW9 Output enable */ + {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, + + /* Enable Boost Volume backdoor */ + {0x1, 0xfb9, 0x24}, + + /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, 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(1)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* set MUX0/1/4/8 = 0 (AOW0) */ + {0x34, AC_VERB_SET_CONNECT_SEL, 0}, + {0x35, AC_VERB_SET_CONNECT_SEL, 0}, + {0x37, AC_VERB_SET_CONNECT_SEL, 0}, + {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, + + /* set PW0 index=0 (MW0) */ + {0x24, AC_VERB_SET_CONNECT_SEL, 0}, + + /* Enable AOW0 to MW9 */ + {0x1, 0xfb8, 0x88}, + { } +}; + + +static struct hda_verb vt2002P_uniwill_init_verbs[] = { + {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, + {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, + {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + { } +}; + +static struct hda_pcm_stream vt2002P_pcm_analog_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x8, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt2002P_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_pcm_open_close, + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt2002P_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup + }, +}; + +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + spec->multiout.num_dacs = 1; + spec->multiout.dac_nids = spec->private_dac_nids; + if (cfg->line_out_pins[0]) + spec->multiout.dac_nids[0] = 0x8; + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + int err; + + if (!cfg->line_out_pins[0]) + return -1; + + + /* Line-Out: PortE */ + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Master Front Playback Volume", + HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, + "Master Front Playback Switch", + HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + return 0; +} + +static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = 0x9; + spec->hp_independent_mode_index = 1; + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL( + spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + create_hp_imux(spec); + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + static char *labels[] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL + }; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx = 0; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x2b: /* Mic */ + idx = 0; + break; + + case 0x2a: /* Line In */ + idx = 1; + break; + + case 0x29: /* Front Mic */ + idx = 2; + break; + } + err = via_new_analog_input(spec, labels[i], idx, 0x21); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = idx; + imux->num_items++; + } + + /* build volume/mute control of loopback */ + err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21); + if (err < 0) + return err; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = 3; + imux->num_items++; + + /* for digital mic select */ + imux->items[imux->num_items].label = "Digital Mic"; + imux->items[imux->num_items].index = 4; + imux->num_items++; + + return 0; +} + +static int vt2002P_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + + err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) + return 0; /* can't find valid BIOS pin config */ + + err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + fill_dig_outs(codec); + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + + if (spec->hp_mux) + spec->mixers[spec->num_mixers++] = via_hp_mixer; + + return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt2002P_loopbacks[] = { + { 0x21, HDA_INPUT, 0 }, + { 0x21, HDA_INPUT, 1 }, + { 0x21, HDA_INPUT, 2 }, + { } /* end */ +}; +#endif + + +/* patch for vt2002P */ +static int patch_vt2002P(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt2002P_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + spec->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs; + + spec->stream_name_analog = "VT2002P Analog"; + spec->stream_analog_playback = &vt2002P_pcm_analog_playback; + spec->stream_analog_capture = &vt2002P_pcm_analog_capture; + + spec->stream_name_digital = "VT2002P Digital"; + spec->stream_digital_playback = &vt2002P_pcm_digital_playback; + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt2002P_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids); + get_mux_nids(codec); + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); + spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + codec->patch_ops.unsol_event = via_unsol_event; + +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt2002P_loopbacks; +#endif + + return 0; +} + +/* for vt1812 */ + +/* capture mixer elements */ +static struct snd_kcontrol_new vt1812_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, + HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + .name = "Input Source", + .count = 2, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; + +static struct hda_verb vt1812_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x9, 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 + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + + /* MUX Indices: Mic = 0 */ + {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, + {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, + + /* PW9 Output enable */ + {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, + + /* Enable Boost Volume backdoor */ + {0x1, 0xfb9, 0x24}, + + /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* set MUX0/1/4/13/15 = 0 (AOW0) */ + {0x34, AC_VERB_SET_CONNECT_SEL, 0}, + {0x35, AC_VERB_SET_CONNECT_SEL, 0}, + {0x38, AC_VERB_SET_CONNECT_SEL, 0}, + {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, + {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, + + /* Enable AOW0 to MW9 */ + {0x1, 0xfb8, 0xa8}, + { } +}; + + +static struct hda_verb vt1812_uniwill_init_verbs[] = { + {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, + {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, + {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, + {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + { } +}; + +static struct hda_pcm_stream vt1812_pcm_analog_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x8, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1812_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_pcm_open_close, + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1812_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup + }, +}; +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt1812_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + spec->multiout.num_dacs = 1; + spec->multiout.dac_nids = spec->private_dac_nids; + if (cfg->line_out_pins[0]) + spec->multiout.dac_nids[0] = 0x8; + return 0; +} + + +/* add playback controls from the parsed DAC table */ +static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + int err; + + if (!cfg->line_out_pins[0]) + return -1; + + /* Line-Out: PortE */ + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Master Front Playback Volume", + HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, + "Master Front Playback Switch", + HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + return 0; +} + +static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = 0x9; + spec->hp_independent_mode_index = 1; + + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL( + spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + create_hp_imux(spec); + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + static char *labels[] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL + }; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx = 0; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x2b: /* Mic */ + idx = 0; + break; + + case 0x2a: /* Line In */ + idx = 1; + break; + + case 0x29: /* Front Mic */ + idx = 2; + break; + } + err = via_new_analog_input(spec, labels[i], idx, 0x21); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = idx; + imux->num_items++; + } + /* build volume/mute control of loopback */ + err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21); + if (err < 0) + return err; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = 5; + imux->num_items++; + + /* for digital mic select */ + imux->items[imux->num_items].label = "Digital Mic"; + imux->items[imux->num_items].index = 6; + imux->num_items++; + + return 0; +} + +static int vt1812_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + fill_dig_outs(codec); + err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + + if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) + return 0; /* can't find valid BIOS pin config */ + + err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + fill_dig_outs(codec); + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + + if (spec->hp_mux) + spec->mixers[spec->num_mixers++] = via_hp_mixer; + + return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1812_loopbacks[] = { + { 0x21, HDA_INPUT, 0 }, + { 0x21, HDA_INPUT, 1 }, + { 0x21, HDA_INPUT, 2 }, + { } /* end */ +}; +#endif + + +/* patch for vt1812 */ +static int patch_vt1812(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt1812_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + + spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; + + spec->stream_name_analog = "VT1812 Analog"; + spec->stream_analog_playback = &vt1812_pcm_analog_playback; + spec->stream_analog_capture = &vt1812_pcm_analog_capture; + + spec->stream_name_digital = "VT1812 Digital"; + spec->stream_digital_playback = &vt1812_pcm_digital_playback; + + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt1812_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids); + get_mux_nids(codec); + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); + spec->mixers[spec->num_mixers] = vt1812_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + codec->patch_ops.unsol_event = via_unsol_event; + +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1812_loopbacks; +#endif return 0; } @@ -3318,6 +6230,23 @@ static struct hda_codec_preset snd_hda_preset_via[] = { .patch = patch_vt1702}, { .id = 0x11067398, .name = "VT1702", .patch = patch_vt1702}, + { .id = 0x11060428, .name = "VT1718S", + .patch = patch_vt1718S}, + { .id = 0x11064428, .name = "VT1718S", + .patch = patch_vt1718S}, + { .id = 0x11060441, .name = "VT2020", + .patch = patch_vt1718S}, + { .id = 0x11064441, .name = "VT1828S", + .patch = patch_vt1718S}, + { .id = 0x11060433, .name = "VT1716S", + .patch = patch_vt1716S}, + { .id = 0x1106a721, .name = "VT1716S", + .patch = patch_vt1716S}, + { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, + { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, + { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, + { .id = 0x11060440, .name = "VT1818S", + .patch = patch_vt1708S}, {} /* terminator */ }; diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c index 37564300b50d..6da21a2bcade 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 0d0cdbdb4486..d74033a2cfbe 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -107,7 +107,7 @@ MODULE_PARM_DESC(dxr_enable, "Enable DXR support for Terratec DMX6FIRE."); static const struct pci_device_id snd_ice1712_ids[] = { - { PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICE1712 */ + { PCI_VDEVICE(ICE, PCI_DEVICE_ID_ICE_1712), 0 }, /* ICE1712 */ { 0, } }; @@ -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 adc909ec125c..9da2dae64c5b 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 36ade77cf371..10fc92c05574 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -93,7 +93,7 @@ MODULE_PARM_DESC(model, "Use the given board model."); /* Both VT1720 and VT1724 have the same PCI IDs */ static const struct pci_device_id snd_vt1724_ids[] = { - { PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_VT1724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VDEVICE(ICE, PCI_DEVICE_ID_VT1724), 0 }, { 0, } }; @@ -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 043a93879bd5..c75515f5be6f 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 8aa5687f392a..754867ed4785 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -421,29 +421,29 @@ struct intel8x0 { }; static struct pci_device_id snd_intel8x0_ids[] = { - { 0x8086, 0x2415, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801AA */ - { 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82901AB */ - { 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801BA */ - { 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH3 */ - { 0x8086, 0x24c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH4 */ - { 0x8086, 0x24d5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH5 */ - { 0x8086, 0x25a6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ESB */ - { 0x8086, 0x266e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH6 */ - { 0x8086, 0x27de, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH7 */ - { 0x8086, 0x2698, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ESB2 */ - { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */ - { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7012 */ - { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE */ - { 0x10de, 0x003a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* MCP04 */ - { 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE2 */ - { 0x10de, 0x0059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK804 */ - { 0x10de, 0x008a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK8 */ - { 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE3 */ - { 0x10de, 0x00ea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK8S */ - { 0x10de, 0x026b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* MCP51 */ - { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */ - { 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD768 */ - { 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI }, /* Ali5455 */ + { PCI_VDEVICE(INTEL, 0x2415), DEVICE_INTEL }, /* 82801AA */ + { PCI_VDEVICE(INTEL, 0x2425), DEVICE_INTEL }, /* 82901AB */ + { PCI_VDEVICE(INTEL, 0x2445), DEVICE_INTEL }, /* 82801BA */ + { PCI_VDEVICE(INTEL, 0x2485), DEVICE_INTEL }, /* ICH3 */ + { PCI_VDEVICE(INTEL, 0x24c5), DEVICE_INTEL_ICH4 }, /* ICH4 */ + { PCI_VDEVICE(INTEL, 0x24d5), DEVICE_INTEL_ICH4 }, /* ICH5 */ + { PCI_VDEVICE(INTEL, 0x25a6), DEVICE_INTEL_ICH4 }, /* ESB */ + { PCI_VDEVICE(INTEL, 0x266e), DEVICE_INTEL_ICH4 }, /* ICH6 */ + { PCI_VDEVICE(INTEL, 0x27de), DEVICE_INTEL_ICH4 }, /* ICH7 */ + { PCI_VDEVICE(INTEL, 0x2698), DEVICE_INTEL_ICH4 }, /* ESB2 */ + { PCI_VDEVICE(INTEL, 0x7195), DEVICE_INTEL }, /* 440MX */ + { PCI_VDEVICE(SI, 0x7012), DEVICE_SIS }, /* SI7012 */ + { PCI_VDEVICE(NVIDIA, 0x01b1), DEVICE_NFORCE }, /* NFORCE */ + { PCI_VDEVICE(NVIDIA, 0x003a), DEVICE_NFORCE }, /* MCP04 */ + { PCI_VDEVICE(NVIDIA, 0x006a), DEVICE_NFORCE }, /* NFORCE2 */ + { PCI_VDEVICE(NVIDIA, 0x0059), DEVICE_NFORCE }, /* CK804 */ + { PCI_VDEVICE(NVIDIA, 0x008a), DEVICE_NFORCE }, /* CK8 */ + { PCI_VDEVICE(NVIDIA, 0x00da), DEVICE_NFORCE }, /* NFORCE3 */ + { PCI_VDEVICE(NVIDIA, 0x00ea), DEVICE_NFORCE }, /* CK8S */ + { PCI_VDEVICE(NVIDIA, 0x026b), DEVICE_NFORCE }, /* MCP51 */ + { PCI_VDEVICE(AMD, 0x746d), DEVICE_INTEL }, /* AMD8111 */ + { PCI_VDEVICE(AMD, 0x7445), DEVICE_INTEL }, /* AMD768 */ + { PCI_VDEVICE(AL, 0x5455), DEVICE_ALI }, /* Ali5455 */ { 0, } }; @@ -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/intel8x0m.c b/sound/pci/intel8x0m.c index 6ec0fc50d6be..9e7d12e7673f 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -220,24 +220,24 @@ struct intel8x0m { }; static struct pci_device_id snd_intel8x0m_ids[] = { - { 0x8086, 0x2416, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801AA */ - { 0x8086, 0x2426, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82901AB */ - { 0x8086, 0x2446, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801BA */ - { 0x8086, 0x2486, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH3 */ - { 0x8086, 0x24c6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH4 */ - { 0x8086, 0x24d6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH5 */ - { 0x8086, 0x266d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH6 */ - { 0x8086, 0x27dd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH7 */ - { 0x8086, 0x7196, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */ - { 0x1022, 0x7446, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD768 */ - { 0x1039, 0x7013, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7013 */ - { 0x10de, 0x01c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE */ - { 0x10de, 0x0069, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE2 */ - { 0x10de, 0x0089, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE2s */ - { 0x10de, 0x00d9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE3 */ + { PCI_VDEVICE(INTEL, 0x2416), DEVICE_INTEL }, /* 82801AA */ + { PCI_VDEVICE(INTEL, 0x2426), DEVICE_INTEL }, /* 82901AB */ + { PCI_VDEVICE(INTEL, 0x2446), DEVICE_INTEL }, /* 82801BA */ + { PCI_VDEVICE(INTEL, 0x2486), DEVICE_INTEL }, /* ICH3 */ + { PCI_VDEVICE(INTEL, 0x24c6), DEVICE_INTEL }, /* ICH4 */ + { PCI_VDEVICE(INTEL, 0x24d6), DEVICE_INTEL }, /* ICH5 */ + { PCI_VDEVICE(INTEL, 0x266d), DEVICE_INTEL }, /* ICH6 */ + { PCI_VDEVICE(INTEL, 0x27dd), DEVICE_INTEL }, /* ICH7 */ + { PCI_VDEVICE(INTEL, 0x7196), DEVICE_INTEL }, /* 440MX */ + { PCI_VDEVICE(AMD, 0x7446), DEVICE_INTEL }, /* AMD768 */ + { PCI_VDEVICE(SI, 0x7013), DEVICE_SIS }, /* SI7013 */ + { PCI_VDEVICE(NVIDIA, 0x01c1), DEVICE_NFORCE }, /* NFORCE */ + { PCI_VDEVICE(NVIDIA, 0x0069), DEVICE_NFORCE }, /* NFORCE2 */ + { PCI_VDEVICE(NVIDIA, 0x0089), DEVICE_NFORCE }, /* NFORCE2s */ + { PCI_VDEVICE(NVIDIA, 0x00d9), DEVICE_NFORCE }, /* NFORCE3 */ #if 0 - { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */ - { 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI }, /* Ali5455 */ + { PCI_VDEVICE(AMD, 0x746d), DEVICE_INTEL }, /* AMD8111 */ + { PCI_VDEVICE(AL, 0x5455), DEVICE_ALI }, /* Ali5455 */ #endif { 0, } }; diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 18da2ef04d09..11b8c6514b3d 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -654,13 +654,12 @@ static int __devinit lx_init_ethersound_config(struct lx6464es *chip) int i; u32 orig_conf_es = lx_dsp_reg_read(chip, eReg_CONFES); - u32 default_conf_es = (64 << IOCR_OUTPUTS_OFFSET) | + /* configure 64 io channels */ + u32 conf_es = (orig_conf_es & CONFES_READ_PART_MASK) | (64 << IOCR_INPUTS_OFFSET) | + (64 << IOCR_OUTPUTS_OFFSET) | (FREQ_RATIO_SINGLE_MODE << FREQ_RATIO_OFFSET); - u32 conf_es = (orig_conf_es & CONFES_READ_PART_MASK) - | (default_conf_es & CONFES_WRITE_PART_MASK); - snd_printdd("->lx_init_ethersound\n"); chip->freq_ratio = FREQ_RATIO_SINGLE_MODE; diff --git a/sound/pci/lx6464es/lx6464es.h b/sound/pci/lx6464es/lx6464es.h index 012c010c8c89..51afc048961d 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 5812780d6e89..3086b751da4a 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/mixart/mixart.c b/sound/pci/mixart/mixart.c index 82bc5b9e7629..a83d1968a845 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -61,7 +61,7 @@ MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard."); */ static struct pci_device_id snd_mixart_ids[] = { - { 0x1057, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* MC8240 */ + { PCI_VDEVICE(MOTOROLA, 0x0003), 0, }, /* MC8240 */ { 0, } }; diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 522a040855d4..97a0731331a1 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -263,9 +263,9 @@ struct nm256 { * PCI ids */ static struct pci_device_id snd_nm256_ids[] = { - {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO), 0}, + {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO), 0}, + {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO), 0}, {0,}, }; diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c index c1eb923f2ac9..09b2b2a36df5 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/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 312251d39696..9a8936e20744 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -260,6 +260,9 @@ oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[]) * chip didn't if the first EEPROM word was overwritten. */ subdevice = oxygen_read_eeprom(chip, 2); + /* use default ID if EEPROM is missing */ + if (subdevice == 0xffff) + subdevice = 0x8788; /* * We use only the subsystem device ID for searching because it is * unique even without the subsystem vendor ID, which may have been diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index 304da169bfdc..5401c547c4e3 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -575,8 +575,10 @@ static int ac97_switch_put(struct snd_kcontrol *ctl, static int ac97_volume_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { + int stereo = (ctl->private_value >> 16) & 1; + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = 2; + info->count = stereo ? 2 : 1; info->value.integer.min = 0; info->value.integer.max = 0x1f; return 0; @@ -587,6 +589,7 @@ static int ac97_volume_get(struct snd_kcontrol *ctl, { struct oxygen *chip = ctl->private_data; unsigned int codec = (ctl->private_value >> 24) & 1; + int stereo = (ctl->private_value >> 16) & 1; unsigned int index = ctl->private_value & 0xff; u16 reg; @@ -594,7 +597,8 @@ static int ac97_volume_get(struct snd_kcontrol *ctl, reg = oxygen_read_ac97(chip, codec, index); mutex_unlock(&chip->mutex); value->value.integer.value[0] = 31 - (reg & 0x1f); - value->value.integer.value[1] = 31 - ((reg >> 8) & 0x1f); + if (stereo) + value->value.integer.value[1] = 31 - ((reg >> 8) & 0x1f); return 0; } @@ -603,6 +607,7 @@ static int ac97_volume_put(struct snd_kcontrol *ctl, { struct oxygen *chip = ctl->private_data; unsigned int codec = (ctl->private_value >> 24) & 1; + int stereo = (ctl->private_value >> 16) & 1; unsigned int index = ctl->private_value & 0xff; u16 oldreg, newreg; int change; @@ -612,8 +617,11 @@ static int ac97_volume_put(struct snd_kcontrol *ctl, newreg = oldreg; newreg = (newreg & ~0x1f) | (31 - (value->value.integer.value[0] & 0x1f)); - newreg = (newreg & ~0x1f00) | - ((31 - (value->value.integer.value[0] & 0x1f)) << 8); + if (stereo) + newreg = (newreg & ~0x1f00) | + ((31 - (value->value.integer.value[1] & 0x1f)) << 8); + else + newreg = (newreg & ~0x1f00) | ((newreg & 0x1f) << 8); change = newreg != oldreg; if (change) oxygen_write_ac97(chip, codec, index, newreg); @@ -673,7 +681,7 @@ static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl, .private_value = ((codec) << 24) | ((invert) << 16) | \ ((bitnr) << 8) | (index), \ } -#define AC97_VOLUME(xname, codec, index) { \ +#define AC97_VOLUME(xname, codec, index, stereo) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ @@ -682,7 +690,7 @@ static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl, .get = ac97_volume_get, \ .put = ac97_volume_put, \ .tlv = { .p = ac97_db_scale, }, \ - .private_value = ((codec) << 24) | (index), \ + .private_value = ((codec) << 24) | ((stereo) << 16) | (index), \ } static DECLARE_TLV_DB_SCALE(monitor_db_scale, -1000, 1000, 0); @@ -882,18 +890,18 @@ static const struct { }; static const struct snd_kcontrol_new ac97_controls[] = { - AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC), + AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC, 0), AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1), AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0), AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1), - AC97_VOLUME("CD Capture Volume", 0, AC97_CD), + AC97_VOLUME("CD Capture Volume", 0, AC97_CD, 1), AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1), - AC97_VOLUME("Aux Capture Volume", 0, AC97_AUX), + AC97_VOLUME("Aux Capture Volume", 0, AC97_AUX, 1), AC97_SWITCH("Aux Capture Switch", 0, AC97_AUX, 15, 1), }; static const struct snd_kcontrol_new ac97_fp_controls[] = { - AC97_VOLUME("Front Panel Playback Volume", 1, AC97_HEADPHONE), + AC97_VOLUME("Front Panel Playback Volume", 1, AC97_HEADPHONE, 1), AC97_SWITCH("Front Panel Playback Switch", 1, AC97_HEADPHONE, 15, 1), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index 3b5ca70c9d4d..ef2345d82b86 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -469,9 +469,11 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream, oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT, oxygen_rate(hw_params) | chip->model.dac_i2s_format | + oxygen_i2s_mclk(hw_params) | oxygen_i2s_bits(hw_params), OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_FORMAT_MASK | + OXYGEN_I2S_MCLK_MASK | OXYGEN_I2S_BITS_MASK); oxygen_update_dac_routing(chip); oxygen_update_spdif_source(chip); diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index bf971f7cfdc6..6ebcb6bdd712 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -635,6 +635,8 @@ static void xonar_d2_resume(struct oxygen *chip) static void xonar_d1_resume(struct oxygen *chip) { + oxygen_set_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); + msleep(1); cs43xx_init(chip); xonar_enable_output(chip); } diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 235a71e5ac8d..b5ca02e2038c 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -2197,9 +2197,12 @@ static int __init alsa_card_riptide_init(void) if (err < 0) return err; #if defined(SUPPORT_JOYSTICK) - pci_register_driver(&joystick_driver); + err = pci_register_driver(&joystick_driver); + /* On failure unregister formerly registered audio driver */ + if (err < 0) + pci_unregister_driver(&driver); #endif - return 0; + return err; } static void __exit alsa_card_riptide_exit(void) diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index d7b966e7c4cf..f977dba7cbd0 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -227,12 +227,9 @@ struct rme32 { }; static struct pci_device_id snd_rme32_ids[] = { - {PCI_VENDOR_ID_XILINX_RME, PCI_DEVICE_ID_RME_DIGI32, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,}, - {PCI_VENDOR_ID_XILINX_RME, PCI_DEVICE_ID_RME_DIGI32_8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,}, - {PCI_VENDOR_ID_XILINX_RME, PCI_DEVICE_ID_RME_DIGI32_PRO, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,}, + {PCI_VDEVICE(XILINX_RME, PCI_DEVICE_ID_RME_DIGI32), 0,}, + {PCI_VDEVICE(XILINX_RME, PCI_DEVICE_ID_RME_DIGI32_8), 0,}, + {PCI_VDEVICE(XILINX_RME, PCI_DEVICE_ID_RME_DIGI32_PRO), 0,}, {0,} }; diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 55fb1c131f58..2ba5c0fd55db 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -232,14 +232,10 @@ struct rme96 { }; static struct pci_device_id snd_rme96_ids[] = { - { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_RME_DIGI96, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_RME_DIGI96_8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_RME_DIGI96_8_PRO, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96), 0, }, + { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96_8), 0, }, + { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96_8_PRO), 0, }, + { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST), 0, }, { 0, } }; diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 3da5c029f93b..7bb827c7d806 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/sonicvibes.c b/sound/pci/sonicvibes.c index 7dc60ad4772e..1f6406c4534d 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -243,7 +243,7 @@ struct sonicvibes { }; static struct pci_device_id snd_sonic_ids[] = { - { 0x5333, 0xca00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_VDEVICE(S3, 0xca00), 0, }, { 0, } }; diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 949fcaf6b70e..8a332d2f615c 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -386,6 +386,7 @@ struct via82xx { struct snd_pcm *pcms[2]; struct snd_rawmidi *rmidi; + struct snd_kcontrol *dxs_controls[4]; struct snd_ac97_bus *ac97_bus; struct snd_ac97 *ac97; @@ -402,9 +403,9 @@ struct via82xx { static struct pci_device_id snd_via82xx_ids[] = { /* 0x1106, 0x3058 */ - { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA686, }, /* 686A */ + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C686_5), TYPE_CARD_VIA686, }, /* 686A */ /* 0x1106, 0x3059 */ - { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA8233, }, /* VT8233 */ + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_8233_5), TYPE_CARD_VIA8233, }, /* VT8233 */ { 0, } }; @@ -1216,9 +1217,9 @@ static int snd_via82xx_pcm_open(struct via82xx *chip, struct viadev *viadev, /* - * open callback for playback on via686 and via823x DSX + * open callback for playback on via686 */ -static int snd_via82xx_playback_open(struct snd_pcm_substream *substream) +static int snd_via686_playback_open(struct snd_pcm_substream *substream) { struct via82xx *chip = snd_pcm_substream_chip(substream); struct viadev *viadev = &chip->devs[chip->playback_devno + substream->number]; @@ -1230,6 +1231,32 @@ static int snd_via82xx_playback_open(struct snd_pcm_substream *substream) } /* + * open callback for playback on via823x DXS + */ +static int snd_via8233_playback_open(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev; + unsigned int stream; + int err; + + viadev = &chip->devs[chip->playback_devno + substream->number]; + if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0) + return err; + stream = viadev->reg_offset / 0x10; + if (chip->dxs_controls[stream]) { + chip->playback_volume[stream][0] = 0; + chip->playback_volume[stream][1] = 0; + chip->dxs_controls[stream]->vd[0].access &= + ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, + &chip->dxs_controls[stream]->id); + } + return 0; +} + +/* * open callback for playback on via823x multi-channel */ static int snd_via8233_multi_open(struct snd_pcm_substream *substream) @@ -1302,10 +1329,26 @@ static int snd_via82xx_pcm_close(struct snd_pcm_substream *substream) return 0; } +static int snd_via8233_playback_close(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = substream->runtime->private_data; + unsigned int stream; + + stream = viadev->reg_offset / 0x10; + if (chip->dxs_controls[stream]) { + chip->dxs_controls[stream]->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, + &chip->dxs_controls[stream]->id); + } + return snd_via82xx_pcm_close(substream); +} + /* via686 playback callbacks */ static struct snd_pcm_ops snd_via686_playback_ops = { - .open = snd_via82xx_playback_open, + .open = snd_via686_playback_open, .close = snd_via82xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_via82xx_hw_params, @@ -1331,8 +1374,8 @@ static struct snd_pcm_ops snd_via686_capture_ops = { /* via823x DSX playback callbacks */ static struct snd_pcm_ops snd_via8233_playback_ops = { - .open = snd_via82xx_playback_open, - .close = snd_via82xx_pcm_close, + .open = snd_via8233_playback_open, + .close = snd_via8233_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_via82xx_hw_params, .hw_free = snd_via82xx_hw_free, @@ -1626,7 +1669,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 +1689,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 +1748,13 @@ 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, - .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ), - .count = 4, + .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 | + SNDRV_CTL_ELEM_ACCESS_INACTIVE, .info = snd_via8233_dxs_volume_info, .get = snd_via8233_dxs_volume_get, .put = snd_via8233_dxs_volume_put, @@ -1936,10 +1981,19 @@ 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; + chip->dxs_controls[i] = kctl; + } } } /* select spdif data slot 10/11 */ diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 0d54e3503c1e..47eb61561dfc 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -261,7 +261,7 @@ struct via82xx_modem { }; static struct pci_device_id snd_via82xx_modem_ids[] = { - { 0x1106, 0x3068, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA82XX_MODEM, }, + { PCI_VDEVICE(VIA, 0x3068), TYPE_CARD_VIA82XX_MODEM, }, { 0, } }; diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c index 6416d3f0c7be..a69e774d0b13 100644 --- a/sound/pci/vx222/vx222_ops.c +++ b/sound/pci/vx222/vx222_ops.c @@ -885,10 +885,10 @@ static int vx_input_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem struct vx_core *_chip = snd_kcontrol_chip(kcontrol); struct snd_vx222 *chip = (struct snd_vx222 *)_chip; if (ucontrol->value.integer.value[0] < 0 || - ucontrol->value.integer.value[0] < MIC_LEVEL_MAX) + ucontrol->value.integer.value[0] > MIC_LEVEL_MAX) return -EINVAL; if (ucontrol->value.integer.value[1] < 0 || - ucontrol->value.integer.value[1] < MIC_LEVEL_MAX) + ucontrol->value.integer.value[1] > MIC_LEVEL_MAX) return -EINVAL; mutex_lock(&_chip->mixer_mutex); if (chip->input_level[0] != ucontrol->value.integer.value[0] || diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 4af66661f9b0..e6b18b90d451 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -67,12 +67,12 @@ module_param_array(rear_switch, bool, NULL, 0444); MODULE_PARM_DESC(rear_switch, "Enable shared rear/line-in switch"); static struct pci_device_id snd_ymfpci_ids[] = { - { 0x1073, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF724 */ - { 0x1073, 0x000d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF724F */ - { 0x1073, 0x000a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF740 */ - { 0x1073, 0x000c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF740C */ - { 0x1073, 0x0010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF744 */ - { 0x1073, 0x0012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF754 */ + { PCI_VDEVICE(YAMAHA, 0x0004), 0, }, /* YMF724 */ + { PCI_VDEVICE(YAMAHA, 0x000d), 0, }, /* YMF724F */ + { PCI_VDEVICE(YAMAHA, 0x000a), 0, }, /* YMF740 */ + { PCI_VDEVICE(YAMAHA, 0x000c), 0, }, /* YMF740C */ + { PCI_VDEVICE(YAMAHA, 0x0010), 0, }, /* YMF744 */ + { PCI_VDEVICE(YAMAHA, 0x0012), 0, }, /* YMF754 */ { 0, } }; diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 2f0925236a1b..5518371db13f 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, |