From: Christian Limpach Date: Thu, 25 Jun 2009 00:10:31 +0000 (+0100) Subject: Backport intel hda support from Linux 2.6.30. X-Git-Url: http://xenbits.xen.org/gitweb?a=commitdiff_plain;h=22647818bf5bcae73362787f91b2c154812adf02;p=xenclient%2Flinux-2.6.27-pq.git Backport intel hda support from Linux 2.6.30. Required to get sound output on Dell E4300. --- diff --git a/master/intel-hda-2.6.30 b/master/intel-hda-2.6.30 new file mode 100644 index 0000000..a7e075f --- /dev/null +++ b/master/intel-hda-2.6.30 @@ -0,0 +1,17458 @@ +diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h +index 4a6eba5..094bf89 100644 +--- a/include/linux/pci_ids.h ++++ b/include/linux/pci_ids.h +@@ -2087,6 +2087,8 @@ + #define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c + #define PCI_DEVICE_ID_MELLANOX_SINAI 0x6274 + ++#define PCI_VENDOR_ID_DFI 0x15bd ++ + #define PCI_VENDOR_ID_QUICKNET 0x15e2 + #define PCI_DEVICE_ID_QUICKNET_XJ 0x0500 + +diff --git a/include/sound/core.h b/include/sound/core.h +index 558b962..56af3dc 100644 +--- a/include/sound/core.h ++++ b/include/sound/core.h +@@ -28,6 +28,7 @@ + #include /* struct rw_semaphore */ + #include /* pm_message_t */ + #include ++#include + + /* number of supported soundcards */ + #ifdef CONFIG_SND_DYNAMIC_MINORS +@@ -63,6 +64,7 @@ typedef int __bitwise snd_device_type_t; + #define SNDRV_DEV_INFO ((__force snd_device_type_t) 0x1006) + #define SNDRV_DEV_BUS ((__force snd_device_type_t) 0x1007) + #define SNDRV_DEV_CODEC ((__force snd_device_type_t) 0x1008) ++#define SNDRV_DEV_JACK ((__force snd_device_type_t) 0x1009) + #define SNDRV_DEV_LOWLEVEL ((__force snd_device_type_t) 0x2000) + + typedef int __bitwise snd_device_state_t; +@@ -114,7 +116,7 @@ struct snd_card { + char shortname[32]; /* short name of this soundcard */ + char longname[80]; /* name of this soundcard */ + char mixername[80]; /* mixer name */ +- char components[80]; /* card components delimited with ++ char components[128]; /* card components delimited with + space */ + struct module *module; /* top-level module */ + +@@ -364,6 +366,23 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...) + printk(fmt ,##args) + #endif + ++/** ++ * snd_BUG_ON - debugging check macro ++ * @cond: condition to evaluate ++ * ++ * When CONFIG_SND_DEBUG is set, this macro evaluates the given condition, ++ * and call WARN() and returns the value if it's non-zero. ++ * ++ * When CONFIG_SND_DEBUG is not set, this just returns zero, and the given ++ * condition is ignored. ++ * ++ * NOTE: the argument won't be evaluated at all when CONFIG_SND_DEBUG=n. ++ * Thus, don't put any statement that influences on the code behavior, ++ * such as pre/post increment, to the argument of this macro. ++ * If you want to evaluate and give a warning, use standard WARN_ON(). ++ */ ++#define snd_BUG_ON(cond) WARN((cond), "BUG? (%s)\n", __stringify(cond)) ++ + #ifdef CONFIG_SND_DEBUG + + #define __ASTRING__(x) #x +@@ -441,21 +460,33 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...) + struct snd_pci_quirk { + unsigned short subvendor; /* PCI subvendor ID */ + unsigned short subdevice; /* PCI subdevice ID */ ++ unsigned short subdevice_mask; /* bitmask to match */ + int value; /* value */ + #ifdef CONFIG_SND_DEBUG_VERBOSE + const char *name; /* name of the device (optional) */ + #endif + }; + +-#define _SND_PCI_QUIRK_ID(vend,dev) \ +- .subvendor = (vend), .subdevice = (dev) ++#define _SND_PCI_QUIRK_ID_MASK(vend, mask, dev) \ ++ .subvendor = (vend), .subdevice = (dev), .subdevice_mask = (mask) ++#define _SND_PCI_QUIRK_ID(vend, dev) \ ++ _SND_PCI_QUIRK_ID_MASK(vend, 0xffff, dev) + #define SND_PCI_QUIRK_ID(vend,dev) {_SND_PCI_QUIRK_ID(vend, dev)} + #ifdef CONFIG_SND_DEBUG_VERBOSE + #define SND_PCI_QUIRK(vend,dev,xname,val) \ + {_SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname)} ++#define SND_PCI_QUIRK_VENDOR(vend, xname, val) \ ++ {_SND_PCI_QUIRK_ID_MASK(vend, 0, 0), .value = (val), .name = (xname)} ++#define SND_PCI_QUIRK_MASK(vend, mask, dev, xname, val) \ ++ {_SND_PCI_QUIRK_ID_MASK(vend, mask, dev), \ ++ .value = (val), .name = (xname)} + #else + #define SND_PCI_QUIRK(vend,dev,xname,val) \ + {_SND_PCI_QUIRK_ID(vend, dev), .value = (val)} ++#define SND_PCI_QUIRK_MASK(vend, mask, dev, xname, val) \ ++ {_SND_PCI_QUIRK_ID_MASK(vend, mask, dev), .value = (val)} ++#define SND_PCI_QUIRK_VENDOR(vend, xname, val) \ ++ {_SND_PCI_QUIRK_ID_MASK(vend, 0, 0), .value = (val)} + #endif + + const struct snd_pci_quirk * +diff --git a/include/sound/jack.h b/include/sound/jack.h +new file mode 100644 +index 0000000..f236e42 +--- /dev/null ++++ b/include/sound/jack.h +@@ -0,0 +1,84 @@ ++#ifndef __SOUND_JACK_H ++#define __SOUND_JACK_H ++ ++/* ++ * Jack abstraction layer ++ * ++ * Copyright 2008 Wolfson Microelectronics plc ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include ++ ++struct input_dev; ++ ++/** ++ * Jack types which can be reported. These values are used as a ++ * bitmask. ++ * ++ * Note that this must be kept in sync with the lookup table in ++ * sound/core/jack.c. ++ */ ++enum snd_jack_types { ++ SND_JACK_HEADPHONE = 0x0001, ++ SND_JACK_MICROPHONE = 0x0002, ++ SND_JACK_HEADSET = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE, ++ SND_JACK_LINEOUT = 0x0004, ++ SND_JACK_MECHANICAL = 0x0008, /* If detected separately */ ++ SND_JACK_VIDEOOUT = 0x0010, ++ SND_JACK_AVOUT = SND_JACK_LINEOUT | SND_JACK_VIDEOOUT, ++}; ++ ++struct snd_jack { ++ struct input_dev *input_dev; ++ int registered; ++ int type; ++ const char *id; ++ char name[100]; ++ void *private_data; ++ void (*private_free)(struct snd_jack *); ++}; ++ ++#ifdef CONFIG_SND_JACK ++ ++int snd_jack_new(struct snd_card *card, const char *id, int type, ++ struct snd_jack **jack); ++void snd_jack_set_parent(struct snd_jack *jack, struct device *parent); ++ ++void snd_jack_report(struct snd_jack *jack, int status); ++ ++#else ++ ++static inline int snd_jack_new(struct snd_card *card, const char *id, int type, ++ struct snd_jack **jack) ++{ ++ return 0; ++} ++ ++static inline void snd_jack_set_parent(struct snd_jack *jack, ++ struct device *parent) ++{ ++} ++ ++static inline void snd_jack_report(struct snd_jack *jack, int status) ++{ ++} ++ ++#endif ++ ++#endif +diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig +new file mode 100644 +index 0000000..eb2a19b +--- /dev/null ++++ b/sound/pci/hda/Kconfig +@@ -0,0 +1,188 @@ ++menuconfig SND_HDA_INTEL ++ tristate "Intel HD Audio" ++ select SND_PCM ++ select SND_VMASTER ++ select SND_JACK if INPUT=y || INPUT=SND ++ help ++ Say Y here to include support for Intel "High Definition ++ Audio" (Azalia) and its compatible devices. ++ ++ This option enables the HD-audio controller. Don't forget ++ to choose the appropriate codec options below. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called snd-hda-intel. ++ ++if SND_HDA_INTEL ++ ++config SND_HDA_HWDEP ++ bool "Build hwdep interface for HD-audio driver" ++ select SND_HWDEP ++ help ++ Say Y here to build a hwdep interface for HD-audio driver. ++ This interface can be used for out-of-band communication ++ with codecs for debugging purposes. ++ ++config SND_HDA_RECONFIG ++ bool "Allow dynamic codec reconfiguration (EXPERIMENTAL)" ++ depends on SND_HDA_HWDEP && EXPERIMENTAL ++ help ++ Say Y here to enable the HD-audio codec re-configuration feature. ++ This adds the sysfs interfaces to allow user to clear the whole ++ codec configuration, change the codec setup, add extra verbs, ++ and re-configure the codec dynamically. ++ ++config SND_HDA_INPUT_BEEP ++ bool "Support digital beep via input layer" ++ depends on INPUT=y || INPUT=SND_HDA_INTEL ++ help ++ Say Y here to build a digital beep interface for HD-audio ++ driver. This interface is used to generate digital beeps. ++ ++config SND_HDA_CODEC_REALTEK ++ bool "Build Realtek HD-audio codec support" ++ default y ++ help ++ Say Y here to include Realtek HD-audio codec support in ++ snd-hda-intel driver, such as ALC880. ++ ++ When the HD-audio driver is built as a module, the codec ++ support code is also built as another module, ++ snd-hda-codec-realtek. ++ This module is automatically loaded at probing. ++ ++config SND_HDA_CODEC_ANALOG ++ bool "Build Analog Device HD-audio codec support" ++ default y ++ help ++ Say Y here to include Analog Device HD-audio codec support in ++ snd-hda-intel driver, such as AD1986A. ++ ++ When the HD-audio driver is built as a module, the codec ++ support code is also built as another module, ++ snd-hda-codec-analog. ++ This module is automatically loaded at probing. ++ ++config SND_HDA_CODEC_SIGMATEL ++ bool "Build IDT/Sigmatel HD-audio codec support" ++ default y ++ help ++ Say Y here to include IDT (Sigmatel) HD-audio codec support in ++ snd-hda-intel driver, such as STAC9200. ++ ++ When the HD-audio driver is built as a module, the codec ++ support code is also built as another module, ++ snd-hda-codec-idt. ++ This module is automatically loaded at probing. ++ ++config SND_HDA_CODEC_VIA ++ bool "Build VIA HD-audio codec support" ++ default y ++ help ++ Say Y here to include VIA HD-audio codec support in ++ snd-hda-intel driver, such as VT1708. ++ ++ When the HD-audio driver is built as a module, the codec ++ support code is also built as another module, ++ snd-hda-codec-via. ++ This module is automatically loaded at probing. ++ ++config SND_HDA_CODEC_ATIHDMI ++ bool "Build ATI HDMI HD-audio codec support" ++ default y ++ help ++ Say Y here to include ATI HDMI HD-audio codec support in ++ snd-hda-intel driver, such as ATI RS600 HDMI. ++ ++ When the HD-audio driver is built as a module, the codec ++ support code is also built as another module, ++ snd-hda-codec-atihdmi. ++ This module is automatically loaded at probing. ++ ++config SND_HDA_CODEC_NVHDMI ++ bool "Build NVIDIA HDMI HD-audio codec support" ++ default y ++ help ++ Say Y here to include NVIDIA HDMI HD-audio codec support in ++ snd-hda-intel driver, such as NVIDIA MCP78 HDMI. ++ ++ When the HD-audio driver is built as a module, the codec ++ support code is also built as another module, ++ snd-hda-codec-nvhdmi. ++ This module is automatically loaded at probing. ++ ++config SND_HDA_CODEC_INTELHDMI ++ bool "Build INTEL HDMI HD-audio codec support" ++ default y ++ help ++ Say Y here to include INTEL HDMI HD-audio codec support in ++ snd-hda-intel driver, such as Eaglelake integrated HDMI. ++ ++ When the HD-audio driver is built as a module, the codec ++ support code is also built as another module, ++ snd-hda-codec-intelhdmi. ++ This module is automatically loaded at probing. ++ ++config SND_HDA_ELD ++ def_bool y ++ depends on SND_HDA_CODEC_INTELHDMI ++ ++config SND_HDA_CODEC_CONEXANT ++ bool "Build Conexant HD-audio codec support" ++ default y ++ help ++ Say Y here to include Conexant HD-audio codec support in ++ snd-hda-intel driver, such as CX20549. ++ ++ When the HD-audio driver is built as a module, the codec ++ support code is also built as another module, ++ snd-hda-codec-conexant. ++ This module is automatically loaded at probing. ++ ++config SND_HDA_CODEC_CMEDIA ++ bool "Build C-Media HD-audio codec support" ++ default y ++ help ++ Say Y here to include C-Media HD-audio codec support in ++ snd-hda-intel driver, such as CMI9880. ++ ++ When the HD-audio driver is built as a module, the codec ++ support code is also built as another module, ++ snd-hda-codec-cmedia. ++ This module is automatically loaded at probing. ++ ++config SND_HDA_CODEC_SI3054 ++ bool "Build Silicon Labs 3054 HD-modem codec support" ++ default y ++ help ++ Say Y here to include Silicon Labs 3054 HD-modem codec ++ (and compatibles) support in snd-hda-intel driver. ++ ++ When the HD-audio driver is built as a module, the codec ++ support code is also built as another module, ++ snd-hda-codec-si3054. ++ This module is automatically loaded at probing. ++ ++config SND_HDA_GENERIC ++ bool "Enable generic HD-audio codec parser" ++ default y ++ help ++ Say Y here to enable the generic HD-audio codec parser ++ in snd-hda-intel driver. ++ ++config SND_HDA_POWER_SAVE ++ bool "Aggressive power-saving on HD-audio" ++ help ++ Say Y here to enable more aggressive power-saving mode on ++ HD-audio driver. The power-saving timeout can be configured ++ via power_save option or over sysfs on-the-fly. ++ ++config SND_HDA_POWER_SAVE_DEFAULT ++ int "Default time-out for HD-audio power-save mode" ++ depends on SND_HDA_POWER_SAVE ++ default 0 ++ help ++ The default time-out value in seconds for HD-audio automatic ++ power-save mode. 0 means to disable the power-save mode. ++ ++endif +diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile +index 1980c6d..50f9d09 100644 +--- a/sound/pci/hda/Makefile ++++ b/sound/pci/hda/Makefile +@@ -1,20 +1,59 @@ +-snd-hda-intel-y := hda_intel.o +-# since snd-hda-intel is the only driver using hda-codec, +-# merge it into a single module although it was originally +-# designed to be individual modules +-snd-hda-intel-y += hda_codec.o +-snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o +-snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o +-snd-hda-intel-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o +-snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o +-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_REALTEK) += patch_realtek.o +-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CMEDIA) += patch_cmedia.o +-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ANALOG) += patch_analog.o +-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += patch_sigmatel.o +-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SI3054) += patch_si3054.o +-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ATIHDMI) += patch_atihdmi.o +-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CONEXANT) += patch_conexant.o +-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_VIA) += patch_via.o +-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_NVHDMI) += patch_nvhdmi.o ++snd-hda-intel-objs := hda_intel.o + ++snd-hda-codec-y := hda_codec.o ++snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o ++snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o ++# snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o ++snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o ++snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o ++ ++snd-hda-codec-realtek-objs := patch_realtek.o ++snd-hda-codec-cmedia-objs := patch_cmedia.o ++snd-hda-codec-analog-objs := patch_analog.o ++snd-hda-codec-idt-objs := patch_sigmatel.o ++snd-hda-codec-si3054-objs := patch_si3054.o ++snd-hda-codec-atihdmi-objs := patch_atihdmi.o ++snd-hda-codec-conexant-objs := patch_conexant.o ++snd-hda-codec-via-objs := patch_via.o ++snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o ++snd-hda-codec-intelhdmi-objs := patch_intelhdmi.o hda_eld.o ++ ++# common driver ++obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o ++ ++# codec drivers (note: CONFIG_SND_HDA_CODEC_XXX are booleans) ++ifdef CONFIG_SND_HDA_CODEC_REALTEK ++obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-realtek.o ++endif ++ifdef CONFIG_SND_HDA_CODEC_CMEDIA ++obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cmedia.o ++endif ++ifdef CONFIG_SND_HDA_CODEC_ANALOG ++obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-analog.o ++endif ++ifdef CONFIG_SND_HDA_CODEC_SIGMATEL ++obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-idt.o ++endif ++ifdef CONFIG_SND_HDA_CODEC_SI3054 ++obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o ++endif ++ifdef CONFIG_SND_HDA_CODEC_ATIHDMI ++obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o ++endif ++ifdef CONFIG_SND_HDA_CODEC_CONEXANT ++obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o ++endif ++ifdef CONFIG_SND_HDA_CODEC_VIA ++obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o ++endif ++ifdef CONFIG_SND_HDA_CODEC_NVHDMI ++obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-nvhdmi.o ++endif ++ifdef CONFIG_SND_HDA_CODEC_INTELHDMI ++obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-intelhdmi.o ++endif ++ ++# this must be the last entry after codec drivers; ++# otherwise the codec patches won't be hooked before the PCI probe ++# when built in kernel + obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o +diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c +index 3ecd7e7..4de5bac 100644 +--- a/sound/pci/hda/hda_beep.c ++++ b/sound/pci/hda/hda_beep.c +@@ -128,15 +128,17 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) + INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device); + + void snd_hda_detach_beep_device(struct hda_codec *codec) + { + struct hda_beep *beep = codec->beep; + if (beep) { + cancel_work_sync(&beep->beep_work); +- flush_scheduled_work(); + + input_unregister_device(beep->dev); + kfree(beep); ++ codec->beep = NULL; + } + } ++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 b9679f0..51bf6a5 100644 +--- a/sound/pci/hda/hda_beep.h ++++ b/sound/pci/hda/hda_beep.h +@@ -39,7 +39,7 @@ struct hda_beep { + int snd_hda_attach_beep_device(struct hda_codec *codec, int nid); + void snd_hda_detach_beep_device(struct hda_codec *codec); + #else +-#define snd_hda_attach_beep_device(...) ++#define snd_hda_attach_beep_device(...) 0 + #define snd_hda_detach_beep_device(...) + #endif + #endif +diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c +index b5b1ba5..8820faf 100644 +--- a/sound/pci/hda/hda_codec.c ++++ b/sound/pci/hda/hda_codec.c +@@ -31,15 +31,6 @@ + #include + #include "hda_local.h" + #include +-#include "hda_patch.h" /* codec presets */ +- +-#ifdef CONFIG_SND_HDA_POWER_SAVE +-/* define this option here to hide as static */ +-static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; +-module_param(power_save, int, 0644); +-MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " +- "(in second, 0 = disable)."); +-#endif + + /* + * vendor / preset table +@@ -55,6 +46,7 @@ static struct hda_vendor_id hda_vendor_ids[] = { + { 0x1002, "ATI" }, + { 0x1057, "Motorola" }, + { 0x1095, "Silicon Image" }, ++ { 0x10de, "Nvidia" }, + { 0x10ec, "Realtek" }, + { 0x1106, "VIA" }, + { 0x111d, "IDT" }, +@@ -64,41 +56,33 @@ static struct hda_vendor_id hda_vendor_ids[] = { + { 0x14f1, "Conexant" }, + { 0x17e8, "Chrontel" }, + { 0x1854, "LG" }, ++ { 0x1aec, "Wolfson Microelectronics" }, + { 0x434d, "C-Media" }, ++ { 0x8086, "Intel" }, + { 0x8384, "SigmaTel" }, + {} /* terminator */ + }; + +-static const struct hda_codec_preset *hda_preset_tables[] = { +-#ifdef CONFIG_SND_HDA_CODEC_REALTEK +- snd_hda_preset_realtek, +-#endif +-#ifdef CONFIG_SND_HDA_CODEC_CMEDIA +- snd_hda_preset_cmedia, +-#endif +-#ifdef CONFIG_SND_HDA_CODEC_ANALOG +- snd_hda_preset_analog, +-#endif +-#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL +- snd_hda_preset_sigmatel, +-#endif +-#ifdef CONFIG_SND_HDA_CODEC_SI3054 +- snd_hda_preset_si3054, +-#endif +-#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI +- snd_hda_preset_atihdmi, +-#endif +-#ifdef CONFIG_SND_HDA_CODEC_CONEXANT +- snd_hda_preset_conexant, +-#endif +-#ifdef CONFIG_SND_HDA_CODEC_VIA +- snd_hda_preset_via, +-#endif +-#ifdef CONFIG_SND_HDA_CODEC_NVHDMI +- snd_hda_preset_nvhdmi, +-#endif +- NULL +-}; ++static DEFINE_MUTEX(preset_mutex); ++static LIST_HEAD(hda_preset_tables); ++ ++int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset) ++{ ++ mutex_lock(&preset_mutex); ++ list_add_tail(&preset->list, &hda_preset_tables); ++ mutex_unlock(&preset_mutex); ++ return 0; ++} ++EXPORT_SYMBOL_HDA(snd_hda_add_codec_preset); ++ ++int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset) ++{ ++ mutex_lock(&preset_mutex); ++ list_del(&preset->list); ++ mutex_unlock(&preset_mutex); ++ return 0; ++} ++EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset); + + #ifdef CONFIG_SND_HDA_POWER_SAVE + static void hda_power_work(struct work_struct *work); +@@ -107,6 +91,72 @@ static void hda_keep_power_on(struct hda_codec *codec); + static inline void hda_keep_power_on(struct hda_codec *codec) {} + #endif + ++const char *snd_hda_get_jack_location(u32 cfg) ++{ ++ static char *bases[7] = { ++ "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom", ++ }; ++ static unsigned char specials_idx[] = { ++ 0x07, 0x08, ++ 0x17, 0x18, 0x19, ++ 0x37, 0x38 ++ }; ++ static char *specials[] = { ++ "Rear Panel", "Drive Bar", ++ "Riser", "HDMI", "ATAPI", ++ "Mobile-In", "Mobile-Out" ++ }; ++ int i; ++ cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT; ++ if ((cfg & 0x0f) < 7) ++ return bases[cfg & 0x0f]; ++ for (i = 0; i < ARRAY_SIZE(specials_idx); i++) { ++ if (cfg == specials_idx[i]) ++ return specials[i]; ++ } ++ return "UNKNOWN"; ++} ++EXPORT_SYMBOL_HDA(snd_hda_get_jack_location); ++ ++const char *snd_hda_get_jack_connectivity(u32 cfg) ++{ ++ static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; ++ ++ return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3]; ++} ++EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity); ++ ++const char *snd_hda_get_jack_type(u32 cfg) ++{ ++ static char *jack_types[16] = { ++ "Line Out", "Speaker", "HP Out", "CD", ++ "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand", ++ "Line In", "Aux", "Mic", "Telephony", ++ "SPDIF In", "Digitial In", "Reserved", "Other" ++ }; ++ ++ return jack_types[(cfg & AC_DEFCFG_DEVICE) ++ >> AC_DEFCFG_DEVICE_SHIFT]; ++} ++EXPORT_SYMBOL_HDA(snd_hda_get_jack_type); ++ ++/* ++ * Compose a 32bit command word to be sent to the HD-audio controller ++ */ ++static inline unsigned int ++make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct, ++ unsigned int verb, unsigned int parm) ++{ ++ u32 val; ++ ++ val = (u32)(codec->addr & 0x0f) << 28; ++ val |= (u32)direct << 27; ++ val |= (u32)nid << 20; ++ val |= verb << 8; ++ val |= parm; ++ return val; ++} ++ + /** + * snd_hda_codec_read - send a command and get the response + * @codec: the HDA codec +@@ -123,17 +173,21 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, + int direct, + unsigned int verb, unsigned int parm) + { ++ struct hda_bus *bus = codec->bus; + unsigned int res; ++ ++ res = make_codec_cmd(codec, nid, direct, verb, parm); + snd_hda_power_up(codec); +- mutex_lock(&codec->bus->cmd_mutex); +- if (!codec->bus->ops.command(codec, nid, direct, verb, parm)) +- res = codec->bus->ops.get_response(codec); ++ mutex_lock(&bus->cmd_mutex); ++ if (!bus->ops.command(bus, res)) ++ res = bus->ops.get_response(bus); + else + res = (unsigned int)-1; +- mutex_unlock(&codec->bus->cmd_mutex); ++ mutex_unlock(&bus->cmd_mutex); + snd_hda_power_down(codec); + return res; + } ++EXPORT_SYMBOL_HDA(snd_hda_codec_read); + + /** + * snd_hda_codec_write - send a single command without waiting for response +@@ -150,14 +204,19 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, + int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, + unsigned int verb, unsigned int parm) + { ++ struct hda_bus *bus = codec->bus; ++ unsigned int res; + int err; ++ ++ res = make_codec_cmd(codec, nid, direct, verb, parm); + snd_hda_power_up(codec); +- mutex_lock(&codec->bus->cmd_mutex); +- err = codec->bus->ops.command(codec, nid, direct, verb, parm); +- mutex_unlock(&codec->bus->cmd_mutex); ++ mutex_lock(&bus->cmd_mutex); ++ err = bus->ops.command(bus, res); ++ mutex_unlock(&bus->cmd_mutex); + snd_hda_power_down(codec); + return err; + } ++EXPORT_SYMBOL_HDA(snd_hda_codec_write); + + /** + * snd_hda_sequence_write - sequence writes +@@ -172,6 +231,7 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq) + for (; seq->nid; seq++) + snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param); + } ++EXPORT_SYMBOL_HDA(snd_hda_sequence_write); + + /** + * snd_hda_get_sub_nodes - get the range of sub nodes +@@ -193,6 +253,7 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, + *start_id = (parm >> 16) & 0x7fff; + return (int)(parm & 0x7fff); + } ++EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes); + + /** + * snd_hda_get_connections - get connection list +@@ -214,7 +275,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, + unsigned int shift, num_elems, mask; + hda_nid_t prev_nid; + +- snd_assert(conn_list && max_conns > 0, return -EINVAL); ++ if (snd_BUG_ON(!conn_list || max_conns <= 0)) ++ return -EINVAL; + + parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); + if (parm & AC_CLIST_LONG) { +@@ -280,6 +342,7 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, + } + return conns; + } ++EXPORT_SYMBOL_HDA(snd_hda_get_connections); + + + /** +@@ -310,13 +373,14 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) + unsol->queue[wp] = res; + unsol->queue[wp + 1] = res_ex; + +- schedule_work(&unsol->work); ++ queue_work(bus->workq, &unsol->work); + + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_queue_unsol_event); + + /* +- * process queueud unsolicited events ++ * process queued unsolicited events + */ + static void process_unsol_events(struct work_struct *work) + { +@@ -343,7 +407,7 @@ static void process_unsol_events(struct work_struct *work) + /* + * initialize unsolicited queue + */ +-static int __devinit init_unsol_queue(struct hda_bus *bus) ++static int init_unsol_queue(struct hda_bus *bus) + { + struct hda_bus_unsolicited *unsol; + +@@ -373,15 +437,17 @@ static int snd_hda_bus_free(struct hda_bus *bus) + + if (!bus) + return 0; +- if (bus->unsol) { +- flush_scheduled_work(); ++ if (bus->workq) ++ flush_workqueue(bus->workq); ++ if (bus->unsol) + kfree(bus->unsol); +- } + list_for_each_entry_safe(codec, n, &bus->codec_list, list) { + snd_hda_codec_free(codec); + } + if (bus->ops.private_free) + bus->ops.private_free(bus); ++ if (bus->workq) ++ destroy_workqueue(bus->workq); + kfree(bus); + return 0; + } +@@ -389,9 +455,24 @@ static int snd_hda_bus_free(struct hda_bus *bus) + static int snd_hda_bus_dev_free(struct snd_device *device) + { + struct hda_bus *bus = device->device_data; ++ bus->shutdown = 1; + return snd_hda_bus_free(bus); + } + ++#ifdef CONFIG_SND_HDA_HWDEP ++static int snd_hda_bus_dev_register(struct snd_device *device) ++{ ++ struct hda_bus *bus = device->device_data; ++ struct hda_codec *codec; ++ list_for_each_entry(codec, &bus->codec_list, list) { ++ snd_hda_hwdep_add_sysfs(codec); ++ } ++ return 0; ++} ++#else ++#define snd_hda_bus_dev_register NULL ++#endif ++ + /** + * snd_hda_bus_new - create a HDA bus + * @card: the card entry +@@ -400,18 +481,21 @@ static int snd_hda_bus_dev_free(struct snd_device *device) + * + * Returns 0 if successful, or a negative error code. + */ +-int __devinit snd_hda_bus_new(struct snd_card *card, ++int /*__devinit*/ snd_hda_bus_new(struct snd_card *card, + const struct hda_bus_template *temp, + struct hda_bus **busp) + { + struct hda_bus *bus; + int err; + static struct snd_device_ops dev_ops = { ++ .dev_register = snd_hda_bus_dev_register, + .dev_free = snd_hda_bus_dev_free, + }; + +- snd_assert(temp, return -EINVAL); +- snd_assert(temp->ops.command && temp->ops.get_response, return -EINVAL); ++ if (snd_BUG_ON(!temp)) ++ return -EINVAL; ++ if (snd_BUG_ON(!temp->ops.command || !temp->ops.get_response)) ++ return -EINVAL; + + if (busp) + *busp = NULL; +@@ -426,11 +510,22 @@ int __devinit snd_hda_bus_new(struct snd_card *card, + bus->private_data = temp->private_data; + bus->pci = temp->pci; + bus->modelname = temp->modelname; ++ bus->power_save = temp->power_save; + bus->ops = temp->ops; + + mutex_init(&bus->cmd_mutex); + INIT_LIST_HEAD(&bus->codec_list); + ++ snprintf(bus->workq_name, sizeof(bus->workq_name), ++ "hd-audio%d", card->number); ++ bus->workq = create_singlethread_workqueue(bus->workq_name); ++ if (!bus->workq) { ++ snd_printk(KERN_ERR "cannot create workqueue %s\n", ++ bus->workq_name); ++ kfree(bus); ++ return -ENOMEM; ++ } ++ + err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops); + if (err < 0) { + snd_hda_bus_free(bus); +@@ -440,27 +535,42 @@ int __devinit snd_hda_bus_new(struct snd_card *card, + *busp = bus; + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_bus_new); + + #ifdef CONFIG_SND_HDA_GENERIC + #define is_generic_config(codec) \ +- (codec->bus->modelname && !strcmp(codec->bus->modelname, "generic")) ++ (codec->modelname && !strcmp(codec->modelname, "generic")) + #else + #define is_generic_config(codec) 0 + #endif + ++#ifdef MODULE ++#define HDA_MODREQ_MAX_COUNT 2 /* two request_modules()'s */ ++#else ++#define HDA_MODREQ_MAX_COUNT 0 /* all presets are statically linked */ ++#endif ++ + /* + * find a matching codec preset + */ +-static const struct hda_codec_preset __devinit * ++static const struct hda_codec_preset * + find_codec_preset(struct hda_codec *codec) + { +- const struct hda_codec_preset **tbl, *preset; ++ struct hda_codec_preset_list *tbl; ++ const struct hda_codec_preset *preset; ++ int mod_requested = 0; + + if (is_generic_config(codec)) + return NULL; /* use the generic parser */ + +- for (tbl = hda_preset_tables; *tbl; tbl++) { +- for (preset = *tbl; preset->id; preset++) { ++ again: ++ mutex_lock(&preset_mutex); ++ list_for_each_entry(tbl, &hda_preset_tables, list) { ++ if (!try_module_get(tbl->owner)) { ++ snd_printk(KERN_ERR "hda_codec: cannot module_get\n"); ++ continue; ++ } ++ for (preset = tbl->preset; preset->id; preset++) { + u32 mask = preset->mask; + if (preset->afg && preset->afg != codec->afg) + continue; +@@ -470,23 +580,40 @@ find_codec_preset(struct hda_codec *codec) + mask = ~0; + if (preset->id == (codec->vendor_id & mask) && + (!preset->rev || +- preset->rev == codec->revision_id)) ++ preset->rev == codec->revision_id)) { ++ mutex_unlock(&preset_mutex); ++ codec->owner = tbl->owner; + return preset; ++ } + } ++ module_put(tbl->owner); ++ } ++ mutex_unlock(&preset_mutex); ++ ++ if (mod_requested < HDA_MODREQ_MAX_COUNT) { ++ char name[32]; ++ if (!mod_requested) ++ snprintf(name, sizeof(name), "snd-hda-codec-id:%08x", ++ codec->vendor_id); ++ else ++ snprintf(name, sizeof(name), "snd-hda-codec-id:%04x*", ++ (codec->vendor_id >> 16) & 0xffff); ++ request_module(name); ++ mod_requested++; ++ goto again; + } + return NULL; + } + + /* +- * snd_hda_get_codec_name - store the codec name ++ * get_codec_name - store the codec name + */ +-void snd_hda_get_codec_name(struct hda_codec *codec, +- char *name, int namelen) ++static int get_codec_name(struct hda_codec *codec) + { + const struct hda_vendor_id *c; + const char *vendor = NULL; + u16 vendor_id = codec->vendor_id >> 16; +- char tmp[16]; ++ char tmp[16], name[32]; + + for (c = hda_vendor_ids; c->id; c++) { + if (c->id == vendor_id) { +@@ -499,30 +626,37 @@ void snd_hda_get_codec_name(struct hda_codec *codec, + vendor = tmp; + } + if (codec->preset && codec->preset->name) +- snprintf(name, namelen, "%s %s", vendor, codec->preset->name); ++ snprintf(name, sizeof(name), "%s %s", vendor, ++ codec->preset->name); + else +- snprintf(name, namelen, "%s ID %x", vendor, ++ snprintf(name, sizeof(name), "%s ID %x", vendor, + codec->vendor_id & 0xffff); ++ codec->name = kstrdup(name, GFP_KERNEL); ++ if (!codec->name) ++ return -ENOMEM; ++ return 0; + } + + /* + * look for an AFG and MFG nodes + */ +-static void __devinit setup_fg_nodes(struct hda_codec *codec) ++static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec) + { +- int i, total_nodes; ++ int i, total_nodes, function_id; + hda_nid_t nid; + + total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid); + for (i = 0; i < total_nodes; i++, nid++) { +- unsigned int func; +- func = snd_hda_param_read(codec, nid, AC_PAR_FUNCTION_TYPE); +- switch (func & 0xff) { ++ function_id = snd_hda_param_read(codec, nid, ++ AC_PAR_FUNCTION_TYPE) & 0xff; ++ switch (function_id) { + case AC_GRP_AUDIO_FUNCTION: + codec->afg = nid; ++ codec->function_id = function_id; + break; + case AC_GRP_MODEM_FUNCTION: + codec->mfg = nid; ++ codec->function_id = function_id; + break; + default: + break; +@@ -550,11 +684,140 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node) + return 0; + } + ++/* read all pin default configurations and save codec->init_pins */ ++static int read_pin_defaults(struct hda_codec *codec) ++{ ++ int i; ++ hda_nid_t nid = codec->start_nid; ++ ++ for (i = 0; i < codec->num_nodes; i++, nid++) { ++ struct hda_pincfg *pin; ++ unsigned int wcaps = get_wcaps(codec, nid); ++ unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >> ++ AC_WCAP_TYPE_SHIFT; ++ if (wid_type != AC_WID_PIN) ++ continue; ++ pin = snd_array_new(&codec->init_pins); ++ if (!pin) ++ return -ENOMEM; ++ pin->nid = nid; ++ pin->cfg = snd_hda_codec_read(codec, nid, 0, ++ AC_VERB_GET_CONFIG_DEFAULT, 0); ++ } ++ return 0; ++} ++ ++/* look up the given pin config list and return the item matching with NID */ ++static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec, ++ struct snd_array *array, ++ hda_nid_t nid) ++{ ++ int i; ++ for (i = 0; i < array->used; i++) { ++ struct hda_pincfg *pin = snd_array_elem(array, i); ++ if (pin->nid == nid) ++ return pin; ++ } ++ return NULL; ++} ++ ++/* write a config value for the given NID */ ++static void set_pincfg(struct hda_codec *codec, hda_nid_t nid, ++ unsigned int cfg) ++{ ++ int i; ++ for (i = 0; i < 4; i++) { ++ snd_hda_codec_write(codec, nid, 0, ++ AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i, ++ cfg & 0xff); ++ cfg >>= 8; ++ } ++} ++ ++/* set the current pin config value for the given NID. ++ * the value is cached, and read via snd_hda_codec_get_pincfg() ++ */ ++int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, ++ hda_nid_t nid, unsigned int cfg) ++{ ++ struct hda_pincfg *pin; ++ unsigned int oldcfg; ++ ++ oldcfg = snd_hda_codec_get_pincfg(codec, nid); ++ pin = look_up_pincfg(codec, list, nid); ++ if (!pin) { ++ pin = snd_array_new(list); ++ if (!pin) ++ return -ENOMEM; ++ pin->nid = nid; ++ } ++ pin->cfg = cfg; ++ ++ /* change only when needed; e.g. if the pincfg is already present ++ * in user_pins[], don't write it ++ */ ++ cfg = snd_hda_codec_get_pincfg(codec, nid); ++ if (oldcfg != cfg) ++ set_pincfg(codec, nid, cfg); ++ return 0; ++} ++ ++int snd_hda_codec_set_pincfg(struct hda_codec *codec, ++ hda_nid_t nid, unsigned int cfg) ++{ ++ return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg); ++} ++EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg); ++ ++/* get the current pin config value of the given pin NID */ ++unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) ++{ ++ struct hda_pincfg *pin; ++ ++#ifdef CONFIG_SND_HDA_HWDEP ++ pin = look_up_pincfg(codec, &codec->user_pins, nid); ++ if (pin) ++ return pin->cfg; ++#endif ++ pin = look_up_pincfg(codec, &codec->driver_pins, nid); ++ if (pin) ++ return pin->cfg; ++ pin = look_up_pincfg(codec, &codec->init_pins, nid); ++ if (pin) ++ return pin->cfg; ++ return 0; ++} ++EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg); ++ ++/* restore all current pin configs */ ++static void restore_pincfgs(struct hda_codec *codec) ++{ ++ int i; ++ for (i = 0; i < codec->init_pins.used; i++) { ++ struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); ++ set_pincfg(codec, pin->nid, ++ snd_hda_codec_get_pincfg(codec, pin->nid)); ++ } ++} + + static void init_hda_cache(struct hda_cache_rec *cache, + unsigned int record_size); + static void free_hda_cache(struct hda_cache_rec *cache); + ++/* restore the initial pin cfgs and release all pincfg lists */ ++static void restore_init_pincfgs(struct hda_codec *codec) ++{ ++ /* first free driver_pins and user_pins, then call restore_pincfg ++ * so that only the values in init_pins are restored ++ */ ++ snd_array_free(&codec->driver_pins); ++#ifdef CONFIG_SND_HDA_HWDEP ++ snd_array_free(&codec->user_pins); ++#endif ++ restore_pincfgs(codec); ++ snd_array_free(&codec->init_pins); ++} ++ + /* + * codec destructor + */ +@@ -562,20 +825,28 @@ static void snd_hda_codec_free(struct hda_codec *codec) + { + if (!codec) + return; ++ restore_init_pincfgs(codec); + #ifdef CONFIG_SND_HDA_POWER_SAVE + cancel_delayed_work(&codec->power_work); +- flush_scheduled_work(); ++ flush_workqueue(codec->bus->workq); + #endif + list_del(&codec->list); ++ snd_array_free(&codec->mixers); + codec->bus->caddr_tbl[codec->addr] = NULL; + if (codec->patch_ops.free) + codec->patch_ops.free(codec); ++ module_put(codec->owner); + free_hda_cache(&codec->amp_cache); + free_hda_cache(&codec->cmd_cache); ++ kfree(codec->name); ++ kfree(codec->modelname); + kfree(codec->wcaps); + kfree(codec); + } + ++static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, ++ unsigned int power_state); ++ + /** + * snd_hda_codec_new - create a HDA codec + * @bus: the bus to assign +@@ -584,15 +855,17 @@ static void snd_hda_codec_free(struct hda_codec *codec) + * + * Returns 0 if successful, or a negative error code. + */ +-int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, +- struct hda_codec **codecp) ++int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, ++ int do_init, struct hda_codec **codecp) + { + struct hda_codec *codec; +- char component[13]; ++ char component[31]; + int err; + +- snd_assert(bus, return -EINVAL); +- snd_assert(codec_addr <= HDA_MAX_CODEC_ADDRESS, return -EINVAL); ++ if (snd_BUG_ON(!bus)) ++ return -EINVAL; ++ if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) ++ return -EINVAL; + + if (bus->caddr_tbl[codec_addr]) { + snd_printk(KERN_ERR "hda_codec: " +@@ -609,8 +882,19 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, + codec->bus = bus; + codec->addr = codec_addr; + mutex_init(&codec->spdif_mutex); ++ 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->init_pins, sizeof(struct hda_pincfg), 16); ++ snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); ++ if (codec->bus->modelname) { ++ codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); ++ if (!codec->modelname) { ++ snd_hda_codec_free(codec); ++ return -ENODEV; ++ } ++ } + + #ifdef CONFIG_SND_HDA_POWER_SAVE + INIT_DELAYED_WORK(&codec->power_work, hda_power_work); +@@ -640,15 +924,18 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, + setup_fg_nodes(codec); + if (!codec->afg && !codec->mfg) { + snd_printdd("hda_codec: no AFG or MFG node found\n"); +- snd_hda_codec_free(codec); +- return -ENODEV; ++ err = -ENODEV; ++ goto error; + } + +- if (read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg) < 0) { ++ err = read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg); ++ if (err < 0) { + snd_printk(KERN_ERR "hda_codec: cannot malloc\n"); +- snd_hda_codec_free(codec); +- return -ENOMEM; ++ goto error; + } ++ err = read_pin_defaults(codec); ++ if (err < 0) ++ goto error; + + if (!codec->subsystem_id) { + hda_nid_t nid = codec->afg ? codec->afg : codec->mfg; +@@ -656,12 +943,51 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_SUBSYSTEM_ID, 0); + } ++ if (bus->modelname) ++ codec->modelname = kstrdup(bus->modelname, GFP_KERNEL); ++ ++ /* power-up all before initialization */ ++ hda_set_power_state(codec, ++ codec->afg ? codec->afg : codec->mfg, ++ AC_PWRST_D0); ++ ++ if (do_init) { ++ err = snd_hda_codec_configure(codec); ++ if (err < 0) ++ goto error; ++ } ++ snd_hda_codec_proc_new(codec); ++ ++ snd_hda_create_hwdep(codec); ++ ++ sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, ++ codec->subsystem_id, codec->revision_id); ++ snd_component_add(codec->bus->card, component); ++ ++ if (codecp) ++ *codecp = codec; ++ return 0; ++ ++ error: ++ snd_hda_codec_free(codec); ++ return err; ++} ++EXPORT_SYMBOL_HDA(snd_hda_codec_new); ++ ++int snd_hda_codec_configure(struct hda_codec *codec) ++{ ++ int err; + + codec->preset = find_codec_preset(codec); ++ if (!codec->name) { ++ err = get_codec_name(codec); ++ if (err < 0) ++ return err; ++ } + /* audio codec should override the mixer name */ +- if (codec->afg || !*bus->card->mixername) +- snd_hda_get_codec_name(codec, bus->card->mixername, +- sizeof(bus->card->mixername)); ++ if (codec->afg || !*codec->bus->card->mixername) ++ strlcpy(codec->bus->card->mixername, codec->name, ++ sizeof(codec->bus->card->mixername)); + + if (is_generic_config(codec)) { + err = snd_hda_parse_generic_codec(codec); +@@ -678,25 +1004,9 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, + printk(KERN_ERR "hda-codec: No codec parser is available\n"); + + patched: +- if (err < 0) { +- snd_hda_codec_free(codec); +- return err; +- } +- +- if (codec->patch_ops.unsol_event) +- init_unsol_queue(bus); +- +- snd_hda_codec_proc_new(codec); +-#ifdef CONFIG_SND_HDA_HWDEP +- snd_hda_create_hwdep(codec); +-#endif +- +- sprintf(component, "HDA:%08x", codec->vendor_id); +- snd_component_add(codec->bus->card, component); +- +- if (codecp) +- *codecp = codec; +- return 0; ++ if (!err && codec->patch_ops.unsol_event) ++ err = init_unsol_queue(codec->bus); ++ return err; + } + + /** +@@ -722,6 +1032,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, + msleep(1); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format); + } ++EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); + + void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) + { +@@ -735,6 +1046,7 @@ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); + #endif + } ++EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream); + + /* + * amp access functions +@@ -742,21 +1054,22 @@ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) + + /* FIXME: more better hash key? */ + #define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24)) ++#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24)) + #define INFO_AMP_CAPS (1<<0) + #define INFO_AMP_VOL(ch) (1 << (1 + (ch))) + + /* initialize the hash table */ +-static void __devinit init_hda_cache(struct hda_cache_rec *cache, ++static void /*__devinit*/ init_hda_cache(struct hda_cache_rec *cache, + unsigned int record_size) + { + memset(cache, 0, sizeof(*cache)); + memset(cache->hash, 0xff, sizeof(cache->hash)); +- cache->record_size = record_size; ++ snd_array_init(&cache->buf, record_size, 64); + } + + static void free_hda_cache(struct hda_cache_rec *cache) + { +- kfree(cache->buffer); ++ snd_array_free(&cache->buf); + } + + /* query the hash. allocate an entry if not found. */ +@@ -768,35 +1081,17 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, + struct hda_cache_head *info; + + while (cur != 0xffff) { +- info = (struct hda_cache_head *)(cache->buffer + +- cur * cache->record_size); ++ info = snd_array_elem(&cache->buf, cur); + if (info->key == key) + return info; + cur = info->next; + } + + /* add a new hash entry */ +- if (cache->num_entries >= cache->size) { +- /* reallocate the array */ +- unsigned int new_size = cache->size + 64; +- void *new_buffer; +- new_buffer = kcalloc(new_size, cache->record_size, GFP_KERNEL); +- if (!new_buffer) { +- snd_printk(KERN_ERR "hda_codec: " +- "can't malloc amp_info\n"); +- return NULL; +- } +- if (cache->buffer) { +- memcpy(new_buffer, cache->buffer, +- cache->size * cache->record_size); +- kfree(cache->buffer); +- } +- cache->size = new_size; +- cache->buffer = new_buffer; +- } +- cur = cache->num_entries++; +- info = (struct hda_cache_head *)(cache->buffer + +- cur * cache->record_size); ++ info = snd_array_new(&cache->buf); ++ if (!info) ++ return NULL; ++ cur = snd_array_index(&cache->buf, info); + info->key = key; + info->val = 0; + info->next = cache->hash[idx]; +@@ -834,6 +1129,7 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) + } + return info->amp_caps; + } ++EXPORT_SYMBOL_HDA(query_amp_caps); + + int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int caps) +@@ -847,6 +1143,22 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, + info->head.val |= INFO_AMP_CAPS; + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps); ++ ++u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) ++{ ++ struct hda_amp_info *info; ++ ++ info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid)); ++ if (!info) ++ return 0; ++ if (!info->head.val) { ++ info->amp_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); ++ info->head.val |= INFO_AMP_CAPS; ++ } ++ return info->amp_caps; ++} ++EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps); + + /* + * read the current volume to info +@@ -900,6 +1212,7 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, + return 0; + return get_vol_mute(codec, info, nid, ch, direction, index); + } ++EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read); + + /* + * update the AMP value, mask = bit mask to set, val = the value +@@ -919,6 +1232,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, + put_vol_mute(codec, info, nid, ch, direction, idx, val); + return 1; + } ++EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update); + + /* + * update the AMP stereo with the same mask and value +@@ -932,15 +1246,16 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, + idx, mask, val); + return ret; + } ++EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); + + #ifdef SND_HDA_NEEDS_RESUME + /* 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.buffer; ++ struct hda_amp_info *buffer = codec->amp_cache.buf.list; + int i; + +- for (i = 0; i < codec->amp_cache.size; i++, buffer++) { ++ for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) { + u32 key = buffer->head.key; + hda_nid_t nid; + unsigned int idx, dir, ch; +@@ -957,6 +1272,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) + } + } + } ++EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); + #endif /* SND_HDA_NEEDS_RESUME */ + + /* volume */ +@@ -987,6 +1303,7 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, + uinfo->value.integer.max = caps; + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info); + + + static inline unsigned int +@@ -1031,6 +1348,7 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, + *valp = read_amp_value(codec, nid, 1, dir, idx, ofs); + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get); + + int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +@@ -1054,6 +1372,7 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, + snd_hda_power_down(codec); + return change; + } ++EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put); + + int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *_tlv) +@@ -1082,6 +1401,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, + return -EFAULT; + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv); + + /* + * set (static) TLV for virtual master volume; recalculated as max 0dB +@@ -1101,6 +1421,7 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, + tlv[2] = -nums * step; + tlv[3] = step; + } ++EXPORT_SYMBOL_HDA(snd_hda_set_vmaster_tlv); + + /* find a mixer control element with the given name */ + static struct snd_kcontrol * +@@ -1120,6 +1441,119 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, + { + return _snd_hda_find_mixer_ctl(codec, name, 0); + } ++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) ++{ ++ int err; ++ struct snd_kcontrol **knewp; ++ ++ err = snd_ctl_add(codec->bus->card, kctl); ++ if (err < 0) ++ return err; ++ knewp = snd_array_new(&codec->mixers); ++ if (!knewp) ++ return -ENOMEM; ++ *knewp = kctl; ++ return 0; ++} ++EXPORT_SYMBOL_HDA(snd_hda_ctl_add); ++ ++/* Clear all controls assigned to the given codec */ ++void snd_hda_ctls_clear(struct hda_codec *codec) ++{ ++ int i; ++ struct snd_kcontrol **kctls = codec->mixers.list; ++ for (i = 0; i < codec->mixers.used; i++) ++ snd_ctl_remove(codec->bus->card, kctls[i]); ++ snd_array_free(&codec->mixers); ++} ++ ++/* pseudo device locking ++ * toggle card->shutdown to allow/disallow the device access (as a hack) ++ */ ++static int hda_lock_devices(struct snd_card *card) ++{ ++ spin_lock(&card->files_lock); ++ if (card->shutdown) { ++ spin_unlock(&card->files_lock); ++ return -EINVAL; ++ } ++ card->shutdown = 1; ++ spin_unlock(&card->files_lock); ++ return 0; ++} ++ ++static void hda_unlock_devices(struct snd_card *card) ++{ ++ spin_lock(&card->files_lock); ++ card->shutdown = 0; ++ spin_unlock(&card->files_lock); ++} ++ ++int snd_hda_codec_reset(struct hda_codec *codec) ++{ ++ struct snd_card *card = codec->bus->card; ++ int i, pcm; ++ ++ if (hda_lock_devices(card) < 0) ++ return -EBUSY; ++ /* check whether the codec isn't used by any mixer or PCM streams */ ++ if (!list_empty(&card->ctl_files)) { ++ hda_unlock_devices(card); ++ return -EBUSY; ++ } ++ for (pcm = 0; pcm < codec->num_pcms; pcm++) { ++ struct hda_pcm *cpcm = &codec->pcm_info[pcm]; ++ if (!cpcm->pcm) ++ continue; ++ if (cpcm->pcm->streams[0].substream_opened || ++ cpcm->pcm->streams[1].substream_opened) { ++ hda_unlock_devices(card); ++ return -EBUSY; ++ } ++ } ++ ++ /* OK, let it free */ ++ ++#ifdef CONFIG_SND_HDA_POWER_SAVE ++ cancel_delayed_work(&codec->power_work); ++ flush_workqueue(codec->bus->workq); ++#endif ++ snd_hda_ctls_clear(codec); ++ /* relase PCMs */ ++ for (i = 0; i < codec->num_pcms; i++) { ++ if (codec->pcm_info[i].pcm) { ++ snd_device_free(card, codec->pcm_info[i].pcm); ++ clear_bit(codec->pcm_info[i].device, ++ codec->bus->pcm_dev_bits); ++ } ++ } ++ if (codec->patch_ops.free) ++ codec->patch_ops.free(codec); ++ codec->proc_widget_hook = NULL; ++ codec->spec = NULL; ++ free_hda_cache(&codec->amp_cache); ++ free_hda_cache(&codec->cmd_cache); ++ init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); ++ init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); ++ /* free only driver_pins so that init_pins + user_pins are restored */ ++ snd_array_free(&codec->driver_pins); ++ restore_pincfgs(codec); ++ codec->num_pcms = 0; ++ codec->pcm_info = NULL; ++ codec->preset = NULL; ++ memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); ++ codec->slave_dig_outs = NULL; ++ codec->spdif_status_reset = 0; ++ module_put(codec->owner); ++ codec->owner = NULL; ++ ++ /* allow device access again */ ++ hda_unlock_devices(card); ++ return 0; ++} + + /* create a virtual master control and add slaves */ + int snd_hda_add_vmaster(struct hda_codec *codec, char *name, +@@ -1138,24 +1572,30 @@ 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_ctl_add(codec->bus->card, kctl); ++ err = snd_hda_ctl_add(codec, kctl); + if (err < 0) + return err; + + for (s = slaves; *s; s++) { + struct snd_kcontrol *sctl; +- +- sctl = snd_hda_find_mixer_ctl(codec, *s); +- if (!sctl) { +- snd_printdd("Cannot find slave %s, skipped\n", *s); +- continue; ++ int i = 0; ++ for (;;) { ++ sctl = _snd_hda_find_mixer_ctl(codec, *s, i); ++ if (!sctl) { ++ if (!i) ++ snd_printdd("Cannot find slave %s, " ++ "skipped\n", *s); ++ break; ++ } ++ err = snd_ctl_add_slave(kctl, sctl); ++ if (err < 0) ++ return err; ++ i++; + } +- err = snd_ctl_add_slave(kctl, sctl); +- if (err < 0) +- return err; + } + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_add_vmaster); + + /* switch */ + int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, +@@ -1169,6 +1609,7 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, + uinfo->value.integer.max = 1; + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info); + + int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +@@ -1188,6 +1629,7 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, + HDA_AMP_MUTE) ? 0 : 1; + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get); + + int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +@@ -1218,6 +1660,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, + snd_hda_power_down(codec); + return change; + } ++EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put); + + /* + * bound volume controls +@@ -1235,14 +1678,15 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, + unsigned long pval; + int err; + +- mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ ++ mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */ + err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); + kcontrol->private_value = pval; +- mutex_unlock(&codec->spdif_mutex); ++ mutex_unlock(&codec->control_mutex); + return err; + } ++EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get); + + int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +@@ -1251,7 +1695,7 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, + unsigned long pval; + int i, indices, err = 0, change = 0; + +- mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ ++ mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT; + for (i = 0; i < indices; i++) { +@@ -1263,9 +1707,10 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, + change |= err; + } + kcontrol->private_value = pval; +- mutex_unlock(&codec->spdif_mutex); ++ mutex_unlock(&codec->control_mutex); + return err < 0 ? err : change; + } ++EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put); + + /* + * generic bound volume/swtich controls +@@ -1277,14 +1722,15 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, + struct hda_bind_ctls *c; + int err; + +- mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ ++ mutex_lock(&codec->control_mutex); + c = (struct hda_bind_ctls *)kcontrol->private_value; + kcontrol->private_value = *c->values; + err = c->ops->info(kcontrol, uinfo); + kcontrol->private_value = (long)c; +- mutex_unlock(&codec->spdif_mutex); ++ mutex_unlock(&codec->control_mutex); + return err; + } ++EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info); + + int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +@@ -1293,14 +1739,15 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, + struct hda_bind_ctls *c; + int err; + +- mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ ++ mutex_lock(&codec->control_mutex); + c = (struct hda_bind_ctls *)kcontrol->private_value; + kcontrol->private_value = *c->values; + err = c->ops->get(kcontrol, ucontrol); + kcontrol->private_value = (long)c; +- mutex_unlock(&codec->spdif_mutex); ++ mutex_unlock(&codec->control_mutex); + return err; + } ++EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get); + + int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +@@ -1310,7 +1757,7 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, + unsigned long *vals; + int err = 0, change = 0; + +- mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ ++ mutex_lock(&codec->control_mutex); + c = (struct hda_bind_ctls *)kcontrol->private_value; + for (vals = c->values; *vals; vals++) { + kcontrol->private_value = *vals; +@@ -1320,9 +1767,10 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, + change |= err; + } + kcontrol->private_value = (long)c; +- mutex_unlock(&codec->spdif_mutex); ++ mutex_unlock(&codec->control_mutex); + return err < 0 ? err : change; + } ++EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put); + + int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +@@ -1331,14 +1779,15 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, + struct hda_bind_ctls *c; + int err; + +- mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ ++ mutex_lock(&codec->control_mutex); + c = (struct hda_bind_ctls *)kcontrol->private_value; + kcontrol->private_value = *c->values; + err = c->ops->tlv(kcontrol, op_flag, size, tlv); + kcontrol->private_value = (long)c; +- mutex_unlock(&codec->spdif_mutex); ++ mutex_unlock(&codec->control_mutex); + return err; + } ++EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_tlv); + + struct hda_ctl_ops snd_hda_bind_vol = { + .info = snd_hda_mixer_amp_volume_info, +@@ -1346,6 +1795,7 @@ struct hda_ctl_ops snd_hda_bind_vol = { + .put = snd_hda_mixer_amp_volume_put, + .tlv = snd_hda_mixer_amp_tlv + }; ++EXPORT_SYMBOL_HDA(snd_hda_bind_vol); + + struct hda_ctl_ops snd_hda_bind_sw = { + .info = snd_hda_mixer_amp_switch_info, +@@ -1353,6 +1803,7 @@ struct hda_ctl_ops snd_hda_bind_sw = { + .put = snd_hda_mixer_amp_switch_put, + .tlv = snd_hda_mixer_amp_tlv + }; ++EXPORT_SYMBOL_HDA(snd_hda_bind_sw); + + /* + * SPDIF out controls +@@ -1600,9 +2051,11 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) + } + for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { + kctl = snd_ctl_new1(dig_mix, codec); ++ if (!kctl) ++ return -ENOMEM; + kctl->id.index = idx; + kctl->private_value = nid; +- err = snd_ctl_add(codec->bus->card, kctl); ++ err = snd_hda_ctl_add(codec, kctl); + if (err < 0) + return err; + } +@@ -1612,6 +2065,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) + codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls); + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls); + + /* + * SPDIF sharing with analog output +@@ -1646,9 +2100,10 @@ int snd_hda_create_spdif_share_sw(struct hda_codec *codec, + if (!mout->dig_out_nid) + return 0; + /* ATTENTION: here mout is passed as private_data, instead of codec */ +- return snd_ctl_add(codec->bus->card, ++ return snd_hda_ctl_add(codec, + snd_ctl_new1(&spdif_share_sw, mout)); + } ++EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw); + + /* + * SPDIF input +@@ -1747,8 +2202,10 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) + } + for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) { + kctl = snd_ctl_new1(dig_mix, codec); ++ if (!kctl) ++ return -ENOMEM; + kctl->private_value = nid; +- err = snd_ctl_add(codec->bus->card, kctl); ++ err = snd_hda_ctl_add(codec, kctl); + if (err < 0) + return err; + } +@@ -1758,6 +2215,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) + AC_DIG1_ENABLE; + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls); + + #ifdef SND_HDA_NEEDS_RESUME + /* +@@ -1784,29 +2242,38 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) + int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, + int direct, unsigned int verb, unsigned int parm) + { ++ struct hda_bus *bus = codec->bus; ++ unsigned int res; + int err; ++ ++ res = make_codec_cmd(codec, nid, direct, verb, parm); + snd_hda_power_up(codec); +- mutex_lock(&codec->bus->cmd_mutex); +- err = codec->bus->ops.command(codec, nid, direct, verb, parm); ++ mutex_lock(&bus->cmd_mutex); ++ err = bus->ops.command(bus, res); + if (!err) { + struct hda_cache_head *c; +- u32 key = build_cmd_cache_key(nid, verb); ++ u32 key; ++ /* parm may contain the verb stuff for get/set amp */ ++ verb = verb | (parm >> 8); ++ parm &= 0xff; ++ key = build_cmd_cache_key(nid, verb); + c = get_alloc_hash(&codec->cmd_cache, key); + if (c) + c->val = parm; + } +- mutex_unlock(&codec->bus->cmd_mutex); ++ mutex_unlock(&bus->cmd_mutex); + snd_hda_power_down(codec); + return err; + } ++EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache); + + /* resume the all commands from the cache */ + void snd_hda_codec_resume_cache(struct hda_codec *codec) + { +- struct hda_cache_head *buffer = codec->cmd_cache.buffer; ++ struct hda_cache_head *buffer = codec->cmd_cache.buf.list; + int i; + +- for (i = 0; i < codec->cmd_cache.size; i++, buffer++) { ++ for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) { + u32 key = buffer->key; + if (!key) + continue; +@@ -1814,6 +2281,7 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec) + get_cmd_cache_cmd(key), buffer->val); + } + } ++EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache); + + /** + * snd_hda_sequence_write_cache - sequence writes with caching +@@ -1831,6 +2299,7 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec, + snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb, + seq->param); + } ++EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache); + #endif /* SND_HDA_NEEDS_RESUME */ + + /* +@@ -1858,8 +2327,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, + * don't power down the widget if it controls + * eapd and EAPD_BTLENABLE is set. + */ +- pincap = snd_hda_param_read(codec, nid, +- AC_PAR_PIN_CAP); ++ pincap = snd_hda_query_pin_caps(codec, nid); + if (pincap & AC_PINCAP_EAPD) { + int eapd = snd_hda_codec_read(codec, + nid, 0, +@@ -1891,6 +2359,17 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, + } + } + ++#ifdef CONFIG_SND_HDA_HWDEP ++/* execute additional init verbs */ ++static void hda_exec_init_verbs(struct hda_codec *codec) ++{ ++ if (codec->init_verbs.list) ++ snd_hda_sequence_write(codec, codec->init_verbs.list); ++} ++#else ++static inline void hda_exec_init_verbs(struct hda_codec *codec) {} ++#endif ++ + #ifdef SND_HDA_NEEDS_RESUME + /* + * call suspend and power-down; used both from PM and power-save +@@ -1917,6 +2396,8 @@ static void hda_call_codec_resume(struct hda_codec *codec) + hda_set_power_state(codec, + codec->afg ? codec->afg : codec->mfg, + AC_PWRST_D0); ++ restore_pincfgs(codec); /* restore all current pin configs */ ++ hda_exec_init_verbs(codec); + if (codec->patch_ops.resume) + codec->patch_ops.resume(codec); + else { +@@ -1937,28 +2418,38 @@ static void hda_call_codec_resume(struct hda_codec *codec) + * + * Returns 0 if successful, otherwise a negative error code. + */ +-int __devinit snd_hda_build_controls(struct hda_bus *bus) ++int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus) + { + struct hda_codec *codec; + + list_for_each_entry(codec, &bus->codec_list, list) { +- int err = 0; +- /* fake as if already powered-on */ +- hda_keep_power_on(codec); +- /* then fire up */ +- hda_set_power_state(codec, +- codec->afg ? codec->afg : codec->mfg, +- AC_PWRST_D0); +- /* continue to initialize... */ +- if (codec->patch_ops.init) +- err = codec->patch_ops.init(codec); +- if (!err && codec->patch_ops.build_controls) +- err = codec->patch_ops.build_controls(codec); +- snd_hda_power_down(codec); +- if (err < 0) +- return err; ++ int err = snd_hda_codec_build_controls(codec); ++ if (err < 0) { ++ printk(KERN_ERR "hda_codec: cannot build controls" ++ "for #%d (error %d)\n", codec->addr, err); ++ err = snd_hda_codec_reset(codec); ++ if (err < 0) { ++ printk(KERN_ERR ++ "hda_codec: cannot revert codec\n"); ++ return err; ++ } ++ } + } ++ return 0; ++} ++EXPORT_SYMBOL_HDA(snd_hda_build_controls); + ++int snd_hda_codec_build_controls(struct hda_codec *codec) ++{ ++ int err = 0; ++ hda_exec_init_verbs(codec); ++ /* continue to initialize... */ ++ if (codec->patch_ops.init) ++ err = codec->patch_ops.init(codec); ++ if (!err && codec->patch_ops.build_controls) ++ err = codec->patch_ops.build_controls(codec); ++ if (err < 0) ++ return err; + return 0; + } + +@@ -2051,6 +2542,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, + + return val; + } ++EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format); + + /** + * snd_hda_query_supported_pcm - query the supported PCM rates and formats +@@ -2065,15 +2557,14 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, + * + * Returns 0 if successful, otherwise a negative error code. + */ +-int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, ++static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, + u32 *ratesp, u64 *formatsp, unsigned int *bpsp) + { +- int i; +- unsigned int val, streams; ++ unsigned int i, val, wcaps; + + val = 0; +- if (nid != codec->afg && +- (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) { ++ wcaps = get_wcaps(codec, nid); ++ if (nid != codec->afg && (wcaps & AC_WCAP_FORMAT_OVRD)) { + val = snd_hda_param_read(codec, nid, AC_PAR_PCM); + if (val == -1) + return -EIO; +@@ -2087,15 +2578,20 @@ int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, + if (val & (1 << i)) + rates |= rate_bits[i].alsa_bits; + } ++ if (rates == 0) { ++ snd_printk(KERN_ERR "hda_codec: rates == 0 " ++ "(nid=0x%x, val=0x%x, ovrd=%i)\n", ++ nid, val, ++ (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0); ++ return -EIO; ++ } + *ratesp = rates; + } + + if (formatsp || bpsp) { + u64 formats = 0; +- unsigned int bps; +- unsigned int wcaps; ++ unsigned int streams, bps; + +- wcaps = get_wcaps(codec, nid); + streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); + if (streams == -1) + return -EIO; +@@ -2148,6 +2644,15 @@ int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, + formats |= SNDRV_PCM_FMTBIT_U8; + bps = 8; + } ++ if (formats == 0) { ++ snd_printk(KERN_ERR "hda_codec: formats == 0 " ++ "(nid=0x%x, val=0x%x, ovrd=%i, " ++ "streams=0x%x)\n", ++ nid, val, ++ (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0, ++ streams); ++ return -EIO; ++ } + if (formatsp) + *formatsp = formats; + if (bpsp) +@@ -2230,6 +2735,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, + + return 1; + } ++EXPORT_SYMBOL_HDA(snd_hda_is_supported_format); + + /* + * PCM stuff +@@ -2259,31 +2765,151 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo, + return 0; + } + +-static int __devinit set_pcm_default_values(struct hda_codec *codec, +- struct hda_pcm_stream *info) ++static int set_pcm_default_values(struct hda_codec *codec, ++ struct hda_pcm_stream *info) + { ++ int err; ++ + /* query support PCM information from the given NID */ + if (info->nid && (!info->rates || !info->formats)) { +- snd_hda_query_supported_pcm(codec, info->nid, ++ err = snd_hda_query_supported_pcm(codec, info->nid, + info->rates ? NULL : &info->rates, + info->formats ? NULL : &info->formats, + info->maxbps ? NULL : &info->maxbps); ++ if (err < 0) ++ return err; + } + if (info->ops.open == NULL) + info->ops.open = hda_pcm_default_open_close; + if (info->ops.close == NULL) + info->ops.close = hda_pcm_default_open_close; + if (info->ops.prepare == NULL) { +- snd_assert(info->nid, return -EINVAL); ++ if (snd_BUG_ON(!info->nid)) ++ return -EINVAL; + info->ops.prepare = hda_pcm_default_prepare; + } + if (info->ops.cleanup == NULL) { +- snd_assert(info->nid, return -EINVAL); ++ if (snd_BUG_ON(!info->nid)) ++ return -EINVAL; + info->ops.cleanup = hda_pcm_default_cleanup; + } + return 0; + } + ++/* ++ * 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 ++ }; ++ /* 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: ++ snd_printk(KERN_WARNING "Invalid PCM type %d\n", type); ++ return -EINVAL; ++ } ++ ok: ++ set_bit(dev, bus->pcm_dev_bits); ++ return dev; ++} ++ ++/* ++ * attach a new PCM stream ++ */ ++static int snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm) ++{ ++ struct hda_bus *bus = codec->bus; ++ struct hda_pcm_stream *info; ++ int stream, err; ++ ++ if (snd_BUG_ON(!pcm->name)) ++ return -EINVAL; ++ for (stream = 0; stream < 2; stream++) { ++ info = &pcm->stream[stream]; ++ if (info->substreams) { ++ err = set_pcm_default_values(codec, info); ++ if (err < 0) ++ return err; ++ } ++ } ++ return bus->ops.attach_pcm(bus, codec, pcm); ++} ++ ++/* assign all PCMs of the given codec */ ++int snd_hda_codec_build_pcms(struct hda_codec *codec) ++{ ++ unsigned int pcm; ++ int err; ++ ++ if (!codec->num_pcms) { ++ if (!codec->patch_ops.build_pcms) ++ return 0; ++ err = codec->patch_ops.build_pcms(codec); ++ if (err < 0) { ++ printk(KERN_ERR "hda_codec: cannot build PCMs" ++ "for #%d (error %d)\n", codec->addr, err); ++ err = snd_hda_codec_reset(codec); ++ if (err < 0) { ++ printk(KERN_ERR ++ "hda_codec: cannot revert codec\n"); ++ return err; ++ } ++ } ++ } ++ for (pcm = 0; pcm < codec->num_pcms; pcm++) { ++ struct hda_pcm *cpcm = &codec->pcm_info[pcm]; ++ int dev; ++ ++ if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) ++ continue; /* no substreams assigned */ ++ ++ if (!cpcm->pcm) { ++ dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type); ++ if (dev < 0) ++ continue; /* no fatal error */ ++ cpcm->device = dev; ++ err = snd_hda_attach_pcm(codec, cpcm); ++ if (err < 0) { ++ printk(KERN_ERR "hda_codec: cannot attach " ++ "PCM stream %d for codec #%d\n", ++ dev, codec->addr); ++ continue; /* no fatal error */ ++ } ++ } ++ } ++ return 0; ++} ++ + /** + * snd_hda_build_pcms - build PCM information + * @bus: the BUS +@@ -2315,27 +2941,13 @@ int __devinit snd_hda_build_pcms(struct hda_bus *bus) + struct hda_codec *codec; + + list_for_each_entry(codec, &bus->codec_list, list) { +- unsigned int pcm, s; +- int err; +- if (!codec->patch_ops.build_pcms) +- continue; +- err = codec->patch_ops.build_pcms(codec); ++ int err = snd_hda_codec_build_pcms(codec); + if (err < 0) + return err; +- for (pcm = 0; pcm < codec->num_pcms; pcm++) { +- for (s = 0; s < 2; s++) { +- struct hda_pcm_stream *info; +- info = &codec->pcm_info[pcm].stream[s]; +- if (!info->substreams) +- continue; +- err = set_pcm_default_values(codec, info); +- if (err < 0) +- return err; +- } +- } + } + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_build_pcms); + + /** + * snd_hda_check_board_config - compare the current codec with the config table +@@ -2354,11 +2966,11 @@ int snd_hda_check_board_config(struct hda_codec *codec, + int num_configs, const char **models, + const struct snd_pci_quirk *tbl) + { +- if (codec->bus->modelname && models) { ++ if (codec->modelname && models) { + int i; + for (i = 0; i < num_configs; i++) { + if (models[i] && +- !strcmp(codec->bus->modelname, models[i])) { ++ !strcmp(codec->modelname, models[i])) { + snd_printd(KERN_INFO "hda_codec: model '%s' is " + "selected\n", models[i]); + return i; +@@ -2391,6 +3003,7 @@ int snd_hda_check_board_config(struct hda_codec *codec, + } + return -1; + } ++EXPORT_SYMBOL_HDA(snd_hda_check_board_config); + + /** + * snd_hda_check_board_codec_sid_config - compare the current codec +@@ -2451,6 +3064,7 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec, + } + return -1; + } ++EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config); + + /** + * snd_hda_add_new_ctls - create controls from the array +@@ -2471,7 +3085,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) + kctl = snd_ctl_new1(knew, codec); + if (!kctl) + return -ENOMEM; +- err = snd_ctl_add(codec->bus->card, kctl); ++ err = snd_hda_ctl_add(codec, kctl); + if (err < 0) { + if (!codec->addr) + return err; +@@ -2479,13 +3093,14 @@ 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_ctl_add(codec->bus->card, kctl); ++ err = snd_hda_ctl_add(codec, kctl); + if (err < 0) + return err; + } + } + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls); + + #ifdef CONFIG_SND_HDA_POWER_SAVE + static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, +@@ -2495,6 +3110,7 @@ static void hda_power_work(struct work_struct *work) + { + struct hda_codec *codec = + container_of(work, struct hda_codec, power_work.work); ++ struct hda_bus *bus = codec->bus; + + if (!codec->power_on || codec->power_count) { + codec->power_transition = 0; +@@ -2502,8 +3118,8 @@ static void hda_power_work(struct work_struct *work) + } + + hda_call_codec_suspend(codec); +- if (codec->bus->ops.pm_notify) +- codec->bus->ops.pm_notify(codec); ++ if (bus->ops.pm_notify) ++ bus->ops.pm_notify(bus); + } + + static void hda_keep_power_on(struct hda_codec *codec) +@@ -2514,29 +3130,39 @@ static void hda_keep_power_on(struct hda_codec *codec) + + void snd_hda_power_up(struct hda_codec *codec) + { ++ struct hda_bus *bus = codec->bus; ++ + codec->power_count++; + if (codec->power_on || codec->power_transition) + return; + + codec->power_on = 1; +- if (codec->bus->ops.pm_notify) +- codec->bus->ops.pm_notify(codec); ++ if (bus->ops.pm_notify) ++ bus->ops.pm_notify(bus); + hda_call_codec_resume(codec); + cancel_delayed_work(&codec->power_work); + codec->power_transition = 0; + } ++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) + + void snd_hda_power_down(struct hda_codec *codec) + { + --codec->power_count; + if (!codec->power_on || codec->power_count || codec->power_transition) + return; +- if (power_save) { ++ if (power_save(codec)) { + codec->power_transition = 1; /* avoid reentrance */ +- schedule_delayed_work(&codec->power_work, +- msecs_to_jiffies(power_save * 1000)); ++ queue_delayed_work(codec->bus->workq, &codec->power_work, ++ msecs_to_jiffies(power_save(codec) * 1000)); + } + } ++EXPORT_SYMBOL_HDA(snd_hda_power_down); + + int snd_hda_check_amp_list_power(struct hda_codec *codec, + struct hda_loopback_check *check, +@@ -2573,6 +3199,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, + } + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power); + #endif + + /* +@@ -2592,6 +3219,7 @@ int snd_hda_ch_mode_info(struct hda_codec *codec, + chmode[uinfo->value.enumerated.item].channels); + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info); + + int snd_hda_ch_mode_get(struct hda_codec *codec, + struct snd_ctl_elem_value *ucontrol, +@@ -2609,6 +3237,7 @@ int snd_hda_ch_mode_get(struct hda_codec *codec, + } + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get); + + int snd_hda_ch_mode_put(struct hda_codec *codec, + struct snd_ctl_elem_value *ucontrol, +@@ -2629,6 +3258,7 @@ int snd_hda_ch_mode_put(struct hda_codec *codec, + snd_hda_sequence_write_cache(codec, chmode[mode].sequence); + return 1; + } ++EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put); + + /* + * input MUX helper +@@ -2649,6 +3279,7 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux, + strcpy(uinfo->value.enumerated.name, imux->items[index].label); + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_input_mux_info); + + int snd_hda_input_mux_put(struct hda_codec *codec, + const struct hda_input_mux *imux, +@@ -2670,6 +3301,7 @@ int snd_hda_input_mux_put(struct hda_codec *codec, + *cur_val = idx; + return 1; + } ++EXPORT_SYMBOL_HDA(snd_hda_input_mux_put); + + + /* +@@ -2682,7 +3314,7 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid, + { + /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ + if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) +- set_dig_out_convert(codec, nid, ++ set_dig_out_convert(codec, nid, + codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff, + -1); + snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); +@@ -2722,6 +3354,7 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec, + mutex_unlock(&codec->spdif_mutex); + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open); + + int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, + struct hda_multi_out *mout, +@@ -2734,6 +3367,17 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, + mutex_unlock(&codec->spdif_mutex); + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare); ++ ++int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, ++ struct hda_multi_out *mout) ++{ ++ mutex_lock(&codec->spdif_mutex); ++ cleanup_dig_out_stream(codec, mout->dig_out_nid); ++ mutex_unlock(&codec->spdif_mutex); ++ return 0; ++} ++EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup); + + /* + * release the digital out +@@ -2746,6 +3390,7 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec, + mutex_unlock(&codec->spdif_mutex); + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close); + + /* + * set up more restrictions for analog out +@@ -2785,6 +3430,7 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec, + return snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, 2); + } ++EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open); + + /* + * set up the i/o for analog out +@@ -2843,6 +3489,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, + } + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare); + + /* + * clean up the setting for analog out +@@ -2869,9 +3516,10 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, + mutex_unlock(&codec->spdif_mutex); + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup); + + /* +- * Helper for automatic ping configuration ++ * Helper for automatic pin configuration + */ + + static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list) +@@ -2957,8 +3605,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, + if (ignore_nids && is_in_nid_list(nid, ignore_nids)) + continue; + +- def_conf = snd_hda_codec_read(codec, nid, 0, +- AC_VERB_GET_CONFIG_DEFAULT, 0); ++ def_conf = snd_hda_codec_get_pincfg(codec, nid); + if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) + continue; + loc = get_defcfg_location(def_conf); +@@ -3034,10 +3681,22 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, + cfg->input_pins[AUTO_PIN_AUX] = nid; + break; + case AC_JACK_SPDIF_OUT: +- cfg->dig_out_pin = nid; ++ case AC_JACK_DIG_OTHER_OUT: ++ if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins)) ++ continue; ++ cfg->dig_out_pins[cfg->dig_outs] = nid; ++ cfg->dig_out_type[cfg->dig_outs] = ++ (loc == AC_JACK_LOC_HDMI) ? ++ HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF; ++ cfg->dig_outs++; + break; + case AC_JACK_SPDIF_IN: ++ case AC_JACK_DIG_OTHER_IN: + cfg->dig_in_pin = nid; ++ if (loc == AC_JACK_LOC_HDMI) ++ cfg->dig_in_type = HDA_PCM_TYPE_HDMI; ++ else ++ cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; + break; + } + } +@@ -3143,6 +3802,9 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, + cfg->hp_pins[1], cfg->hp_pins[2], + cfg->hp_pins[3], cfg->hp_pins[4]); + snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin); ++ if (cfg->dig_outs) ++ snd_printd(" dig-out=0x%x/0x%x\n", ++ cfg->dig_out_pins[0], cfg->dig_out_pins[1]); + snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x," + " cd=0x%x, aux=0x%x\n", + cfg->input_pins[AUTO_PIN_MIC], +@@ -3151,14 +3813,18 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, + cfg->input_pins[AUTO_PIN_FRONT_LINE], + cfg->input_pins[AUTO_PIN_CD], + cfg->input_pins[AUTO_PIN_AUX]); ++ if (cfg->dig_in_pin) ++ snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin); + + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config); + + /* labels for input pins */ + const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux" + }; ++EXPORT_SYMBOL_HDA(auto_pin_cfg_labels); + + + #ifdef CONFIG_PM +@@ -3186,11 +3852,11 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state) + } + return 0; + } ++EXPORT_SYMBOL_HDA(snd_hda_suspend); + + /** + * snd_hda_resume - resume the codecs + * @bus: the HDA bus +- * @state: resume state + * + * Returns 0 if successful. + * +@@ -3207,16 +3873,79 @@ int snd_hda_resume(struct hda_bus *bus) + } + return 0; + } +-#ifdef CONFIG_SND_HDA_POWER_SAVE +-int snd_hda_codecs_inuse(struct hda_bus *bus) +-{ +- struct hda_codec *codec; ++EXPORT_SYMBOL_HDA(snd_hda_resume); ++#endif /* CONFIG_PM */ + +- list_for_each_entry(codec, &bus->codec_list, list) { +- if (snd_hda_codec_needs_resume(codec)) +- return 1; ++/* ++ * generic arrays ++ */ ++ ++/* get a new element from the given array ++ * if it exceeds the pre-allocated array size, re-allocate the array ++ */ ++void *snd_array_new(struct snd_array *array) ++{ ++ if (array->used >= array->alloced) { ++ int num = array->alloced + array->alloc_align; ++ void *nlist; ++ if (snd_BUG_ON(num >= 4096)) ++ return NULL; ++ nlist = kcalloc(num + 1, array->elem_size, GFP_KERNEL); ++ if (!nlist) ++ return NULL; ++ if (array->list) { ++ memcpy(nlist, array->list, ++ array->elem_size * array->alloced); ++ kfree(array->list); ++ } ++ array->list = nlist; ++ array->alloced = num; + } +- return 0; ++ return snd_array_elem(array, array->used++); + } +-#endif +-#endif ++EXPORT_SYMBOL_HDA(snd_array_new); ++ ++/* free the given array elements */ ++void snd_array_free(struct snd_array *array) ++{ ++ kfree(array->list); ++ array->used = 0; ++ array->alloced = 0; ++ array->list = NULL; ++} ++EXPORT_SYMBOL_HDA(snd_array_free); ++ ++/* ++ * used by hda_proc.c and hda_eld.c ++ */ ++void snd_print_pcm_rates(int pcm, char *buf, int buflen) ++{ ++ static unsigned int rates[] = { ++ 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, ++ 96000, 176400, 192000, 384000 ++ }; ++ int i, j; ++ ++ for (i = 0, j = 0; i < ARRAY_SIZE(rates); i++) ++ if (pcm & (1 << i)) ++ j += snprintf(buf + j, buflen - j, " %d", rates[i]); ++ ++ buf[j] = '\0'; /* necessary when j == 0 */ ++} ++EXPORT_SYMBOL_HDA(snd_print_pcm_rates); ++ ++void snd_print_pcm_bits(int pcm, char *buf, int buflen) ++{ ++ static unsigned int bits[] = { 8, 16, 20, 24, 32 }; ++ int i, j; ++ ++ for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++) ++ if (pcm & (AC_SUPPCM_BITS_8 << i)) ++ j += snprintf(buf + j, buflen - j, " %d", bits[i]); ++ ++ buf[j] = '\0'; /* necessary when j == 0 */ ++} ++EXPORT_SYMBOL_HDA(snd_print_pcm_bits); ++ ++MODULE_DESCRIPTION("HDA codec core"); ++MODULE_LICENSE("GPL"); +diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h +index 26f6f9e..2fdecf4 100644 +--- a/sound/pci/hda/hda_codec.h ++++ b/sound/pci/hda/hda_codec.h +@@ -306,15 +306,15 @@ enum { + #define AC_KNBCAP_DELTA (1<<7) + + /* HDMI LPCM capabilities */ +-#define AC_LPCMCAP_48K_CP_CHNS (0x0f<<0) /* max channels w/ CP-on */ ++#define AC_LPCMCAP_48K_CP_CHNS (0x0f<<0) /* max channels w/ CP-on */ + #define AC_LPCMCAP_48K_NO_CHNS (0x0f<<4) /* max channels w/o CP-on */ + #define AC_LPCMCAP_48K_20BIT (1<<8) /* 20b bitrate supported */ + #define AC_LPCMCAP_48K_24BIT (1<<9) /* 24b bitrate supported */ +-#define AC_LPCMCAP_96K_CP_CHNS (0x0f<<10) /* max channels w/ CP-on */ ++#define AC_LPCMCAP_96K_CP_CHNS (0x0f<<10) /* max channels w/ CP-on */ + #define AC_LPCMCAP_96K_NO_CHNS (0x0f<<14) /* max channels w/o CP-on */ + #define AC_LPCMCAP_96K_20BIT (1<<18) /* 20b bitrate supported */ + #define AC_LPCMCAP_96K_24BIT (1<<19) /* 24b bitrate supported */ +-#define AC_LPCMCAP_192K_CP_CHNS (0x0f<<20) /* max channels w/ CP-on */ ++#define AC_LPCMCAP_192K_CP_CHNS (0x0f<<20) /* max channels w/ CP-on */ + #define AC_LPCMCAP_192K_NO_CHNS (0x0f<<24) /* max channels w/o CP-on */ + #define AC_LPCMCAP_192K_20BIT (1<<28) /* 20b bitrate supported */ + #define AC_LPCMCAP_192K_24BIT (1<<29) /* 24b bitrate supported */ +@@ -520,6 +520,36 @@ enum { + #define HDA_MAX_CODEC_ADDRESS 0x0f + + /* ++ * generic arrays ++ */ ++struct snd_array { ++ unsigned int used; ++ unsigned int alloced; ++ unsigned int elem_size; ++ unsigned int alloc_align; ++ void *list; ++}; ++ ++void *snd_array_new(struct snd_array *array); ++void snd_array_free(struct snd_array *array); ++static inline void snd_array_init(struct snd_array *array, unsigned int size, ++ unsigned int align) ++{ ++ array->elem_size = size; ++ array->alloc_align = align; ++} ++ ++static inline void *snd_array_elem(struct snd_array *array, unsigned int idx) ++{ ++ return array->list + idx * array->elem_size; ++} ++ ++static inline unsigned int snd_array_index(struct snd_array *array, void *ptr) ++{ ++ return (unsigned long)(ptr - array->list) / array->elem_size; ++} ++ ++/* + * Structures + */ + +@@ -536,15 +566,17 @@ typedef u16 hda_nid_t; + /* bus operators */ + struct hda_bus_ops { + /* send a single command */ +- int (*command)(struct hda_codec *codec, hda_nid_t nid, int direct, +- unsigned int verb, unsigned int parm); ++ int (*command)(struct hda_bus *bus, unsigned int cmd); + /* get a response from the last command */ +- unsigned int (*get_response)(struct hda_codec *codec); ++ unsigned int (*get_response)(struct hda_bus *bus); + /* free the private data */ + void (*private_free)(struct hda_bus *); ++ /* attach a PCM stream */ ++ int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec, ++ struct hda_pcm *pcm); + #ifdef CONFIG_SND_HDA_POWER_SAVE + /* notify power-up/down from codec to controller */ +- void (*pm_notify)(struct hda_codec *codec); ++ void (*pm_notify)(struct hda_bus *bus); + #endif + }; + +@@ -553,6 +585,7 @@ struct hda_bus_template { + void *private_data; + struct pci_dev *pci; + const char *modelname; ++ int *power_save; + struct hda_bus_ops ops; + }; + +@@ -569,6 +602,7 @@ struct hda_bus { + void *private_data; + struct pci_dev *pci; + const char *modelname; ++ int *power_save; + struct hda_bus_ops ops; + + /* codec linked list */ +@@ -580,11 +614,15 @@ struct hda_bus { + + /* unsolicited event queue */ + struct hda_bus_unsolicited *unsol; ++ char workq_name[16]; ++ struct workqueue_struct *workq; /* common workqueue for codecs */ + +- struct snd_info_entry *proc; ++ /* assigned PCMs */ ++ DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES); + + /* misc op flags */ + unsigned int needs_damn_long_delay :1; ++ unsigned int shutdown :1; /* being unloaded */ + }; + + /* +@@ -604,6 +642,16 @@ struct hda_codec_preset { + int (*patch)(struct hda_codec *codec); + }; + ++struct hda_codec_preset_list { ++ const struct hda_codec_preset *preset; ++ struct module *owner; ++ struct list_head list; ++}; ++ ++/* initial hook */ ++int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset); ++int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset); ++ + /* ops set by the preset patch */ + struct hda_codec_ops { + int (*build_controls)(struct hda_codec *codec); +@@ -635,10 +683,7 @@ struct hda_amp_info { + + struct hda_cache_rec { + u16 hash[64]; /* hash table for index */ +- unsigned int num_entries; /* number of assigned entries */ +- unsigned int size; /* allocated size */ +- unsigned int record_size; /* record size (including header) */ +- void *buffer; /* hash table entries */ ++ struct snd_array buf; /* record entries */ + }; + + /* PCM callbacks */ +@@ -680,7 +725,8 @@ struct hda_pcm { + char *name; + struct hda_pcm_stream stream[2]; + unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */ +- int device; /* assigned device number */ ++ int device; /* device number to assign */ ++ struct snd_pcm *pcm; /* assigned PCM instance */ + }; + + /* codec information */ +@@ -693,12 +739,16 @@ struct hda_codec { + hda_nid_t mfg; /* MFG node id */ + + /* ids */ ++ u32 function_id; + u32 vendor_id; + u32 subsystem_id; + u32 revision_id; + + /* detected preset */ + const struct hda_codec_preset *preset; ++ struct module *owner; ++ const char *name; /* codec name */ ++ const char *modelname; /* model name for preset */ + + /* set by patch */ + struct hda_codec_ops patch_ops; +@@ -718,28 +768,45 @@ struct hda_codec { + hda_nid_t start_nid; + u32 *wcaps; + ++ struct snd_array mixers; /* list of assigned mixer elements */ ++ + struct hda_cache_rec amp_cache; /* cache for amp access */ + struct hda_cache_rec cmd_cache; /* cache for other commands */ + + struct mutex spdif_mutex; ++ struct mutex control_mutex; + unsigned int spdif_status; /* IEC958 status bits */ + unsigned short spdif_ctls; /* SPDIF control bits */ + unsigned int spdif_in_enable; /* SPDIF input enable? */ + hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ ++ struct snd_array init_pins; /* initial (BIOS) pin configurations */ ++ struct snd_array driver_pins; /* pin configs set by codec parser */ + ++#ifdef CONFIG_SND_HDA_HWDEP + struct snd_hwdep *hwdep; /* assigned hwdep device */ ++ struct snd_array init_verbs; /* additional init verbs */ ++ struct snd_array hints; /* additional hints */ ++ struct snd_array user_pins; /* default pin configs to override */ ++#endif + + /* misc flags */ + unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each + * status change + * (e.g. Realtek codecs) + */ ++ unsigned int pin_amp_workaround:1; /* pin out-amp takes index ++ * (e.g. Conexant codecs) ++ */ + #ifdef CONFIG_SND_HDA_POWER_SAVE + unsigned int power_on :1; /* current (global) power-state */ + 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 */ + #endif ++ ++ /* codec-specific additional proc output */ ++ void (*proc_widget_hook)(struct snd_info_buffer *buffer, ++ struct hda_codec *codec, hda_nid_t nid); + }; + + /* direction */ +@@ -754,7 +821,7 @@ enum { + int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp, + struct hda_bus **busp); + int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, +- struct hda_codec **codecp); ++ int do_init, struct hda_codec **codecp); + + /* + * low level functions +@@ -795,15 +862,29 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec); + #define snd_hda_sequence_write_cache snd_hda_sequence_write + #endif + ++/* the struct for codec->pin_configs */ ++struct hda_pincfg { ++ hda_nid_t nid; ++ unsigned int cfg; ++}; ++ ++unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid); ++int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid, ++ unsigned int cfg); ++int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, ++ hda_nid_t nid, unsigned int cfg); /* for hwdep */ ++ + /* + * Mixer + */ + int snd_hda_build_controls(struct hda_bus *bus); ++int snd_hda_codec_build_controls(struct hda_codec *codec); + + /* + * PCM + */ + int snd_hda_build_pcms(struct hda_bus *bus); ++int snd_hda_codec_build_pcms(struct hda_codec *codec); + void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, + u32 stream_tag, + int channel_id, int format); +@@ -812,8 +893,6 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, + unsigned int channels, + unsigned int format, + unsigned int maxbps); +-int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, +- u32 *ratesp, u64 *formatsp, unsigned int *bpsp); + int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, + unsigned int format); + +@@ -831,18 +910,38 @@ int snd_hda_resume(struct hda_bus *bus); + #endif + + /* ++ * get widget information ++ */ ++const char *snd_hda_get_jack_connectivity(u32 cfg); ++const char *snd_hda_get_jack_type(u32 cfg); ++const char *snd_hda_get_jack_location(u32 cfg); ++ ++/* + * power saving + */ + #ifdef CONFIG_SND_HDA_POWER_SAVE + 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 +-int snd_hda_codecs_inuse(struct hda_bus *bus); + #else + static inline void snd_hda_power_up(struct hda_codec *codec) {} + static inline void snd_hda_power_down(struct hda_codec *codec) {} + #define snd_hda_codec_needs_resume(codec) 1 +-#define snd_hda_codecs_inuse(bus) 1 ++#endif ++ ++/* ++ * Codec modularization ++ */ ++ ++/* Export symbols only for communication with codec drivers; ++ * When built in kernel, all HD-audio drivers are supposed to be statically ++ * linked to the kernel. Thus, the symbols don't have to (or shouldn't) be ++ * exported unless it's built as a module. ++ */ ++#ifdef MODULE ++#define EXPORT_SYMBOL_HDA(sym) EXPORT_SYMBOL_GPL(sym) ++#else ++#define EXPORT_SYMBOL_HDA(sym) + #endif + + #endif /* __SOUND_HDA_CODEC_H */ +diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c +new file mode 100644 +index 0000000..fcad5ec +--- /dev/null ++++ b/sound/pci/hda/hda_eld.c +@@ -0,0 +1,590 @@ ++/* ++ * Generic routines and proc interface for ELD(EDID Like Data) information ++ * ++ * Copyright(c) 2008 Intel Corporation. ++ * ++ * Authors: ++ * Wu Fengguang ++ * ++ * This driver is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This driver is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include "hda_codec.h" ++#include "hda_local.h" ++ ++enum eld_versions { ++ ELD_VER_CEA_861D = 2, ++ ELD_VER_PARTIAL = 31, ++}; ++ ++enum cea_edid_versions { ++ CEA_EDID_VER_NONE = 0, ++ CEA_EDID_VER_CEA861 = 1, ++ CEA_EDID_VER_CEA861A = 2, ++ CEA_EDID_VER_CEA861BCD = 3, ++ CEA_EDID_VER_RESERVED = 4, ++}; ++ ++static char *cea_speaker_allocation_names[] = { ++ /* 0 */ "FL/FR", ++ /* 1 */ "LFE", ++ /* 2 */ "FC", ++ /* 3 */ "RL/RR", ++ /* 4 */ "RC", ++ /* 5 */ "FLC/FRC", ++ /* 6 */ "RLC/RRC", ++ /* 7 */ "FLW/FRW", ++ /* 8 */ "FLH/FRH", ++ /* 9 */ "TC", ++ /* 10 */ "FCH", ++}; ++ ++static char *eld_connection_type_names[4] = { ++ "HDMI", ++ "DisplayPort", ++ "2-reserved", ++ "3-reserved" ++}; ++ ++enum cea_audio_coding_types { ++ AUDIO_CODING_TYPE_REF_STREAM_HEADER = 0, ++ AUDIO_CODING_TYPE_LPCM = 1, ++ AUDIO_CODING_TYPE_AC3 = 2, ++ AUDIO_CODING_TYPE_MPEG1 = 3, ++ AUDIO_CODING_TYPE_MP3 = 4, ++ AUDIO_CODING_TYPE_MPEG2 = 5, ++ AUDIO_CODING_TYPE_AACLC = 6, ++ AUDIO_CODING_TYPE_DTS = 7, ++ AUDIO_CODING_TYPE_ATRAC = 8, ++ AUDIO_CODING_TYPE_SACD = 9, ++ AUDIO_CODING_TYPE_EAC3 = 10, ++ AUDIO_CODING_TYPE_DTS_HD = 11, ++ AUDIO_CODING_TYPE_MLP = 12, ++ AUDIO_CODING_TYPE_DST = 13, ++ AUDIO_CODING_TYPE_WMAPRO = 14, ++ AUDIO_CODING_TYPE_REF_CXT = 15, ++ /* also include valid xtypes below */ ++ AUDIO_CODING_TYPE_HE_AAC = 15, ++ AUDIO_CODING_TYPE_HE_AAC2 = 16, ++ AUDIO_CODING_TYPE_MPEG_SURROUND = 17, ++}; ++ ++enum cea_audio_coding_xtypes { ++ AUDIO_CODING_XTYPE_HE_REF_CT = 0, ++ AUDIO_CODING_XTYPE_HE_AAC = 1, ++ AUDIO_CODING_XTYPE_HE_AAC2 = 2, ++ AUDIO_CODING_XTYPE_MPEG_SURROUND = 3, ++ AUDIO_CODING_XTYPE_FIRST_RESERVED = 4, ++}; ++ ++static char *cea_audio_coding_type_names[] = { ++ /* 0 */ "undefined", ++ /* 1 */ "LPCM", ++ /* 2 */ "AC-3", ++ /* 3 */ "MPEG1", ++ /* 4 */ "MP3", ++ /* 5 */ "MPEG2", ++ /* 6 */ "AAC-LC", ++ /* 7 */ "DTS", ++ /* 8 */ "ATRAC", ++ /* 9 */ "DSD (One Bit Audio)", ++ /* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)", ++ /* 11 */ "DTS-HD", ++ /* 12 */ "MLP (Dolby TrueHD)", ++ /* 13 */ "DST", ++ /* 14 */ "WMAPro", ++ /* 15 */ "HE-AAC", ++ /* 16 */ "HE-AACv2", ++ /* 17 */ "MPEG Surround", ++}; ++ ++/* ++ * The following two lists are shared between ++ * - HDMI audio InfoFrame (source to sink) ++ * - CEA E-EDID Extension (sink to source) ++ */ ++ ++/* ++ * SS1:SS0 index => sample size ++ */ ++static int cea_sample_sizes[4] = { ++ 0, /* 0: Refer to Stream Header */ ++ AC_SUPPCM_BITS_16, /* 1: 16 bits */ ++ AC_SUPPCM_BITS_20, /* 2: 20 bits */ ++ AC_SUPPCM_BITS_24, /* 3: 24 bits */ ++}; ++ ++/* ++ * SF2:SF1:SF0 index => sampling frequency ++ */ ++static int cea_sampling_frequencies[8] = { ++ 0, /* 0: Refer to Stream Header */ ++ SNDRV_PCM_RATE_32000, /* 1: 32000Hz */ ++ SNDRV_PCM_RATE_44100, /* 2: 44100Hz */ ++ SNDRV_PCM_RATE_48000, /* 3: 48000Hz */ ++ SNDRV_PCM_RATE_88200, /* 4: 88200Hz */ ++ SNDRV_PCM_RATE_96000, /* 5: 96000Hz */ ++ SNDRV_PCM_RATE_176400, /* 6: 176400Hz */ ++ SNDRV_PCM_RATE_192000, /* 7: 192000Hz */ ++}; ++ ++static unsigned char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid, ++ int byte_index) ++{ ++ unsigned int val; ++ ++ val = snd_hda_codec_read(codec, nid, 0, ++ AC_VERB_GET_HDMI_ELDD, byte_index); ++ ++#ifdef BE_PARANOID ++ printk(KERN_INFO "HDMI: ELD data byte %d: 0x%x\n", byte_index, val); ++#endif ++ ++ if ((val & AC_ELDD_ELD_VALID) == 0) { ++ snd_printd(KERN_INFO "HDMI: invalid ELD data byte %d\n", ++ byte_index); ++ val = 0; ++ } ++ ++ return val & AC_ELDD_ELD_DATA; ++} ++ ++#define GRAB_BITS(buf, byte, lowbit, bits) \ ++({ \ ++ BUILD_BUG_ON(lowbit > 7); \ ++ BUILD_BUG_ON(bits > 8); \ ++ BUILD_BUG_ON(bits <= 0); \ ++ \ ++ (buf[byte] >> (lowbit)) & ((1 << (bits)) - 1); \ ++}) ++ ++static void hdmi_update_short_audio_desc(struct cea_sad *a, ++ const unsigned char *buf) ++{ ++ int i; ++ int val; ++ ++ val = GRAB_BITS(buf, 1, 0, 7); ++ a->rates = 0; ++ for (i = 0; i < 7; i++) ++ if (val & (1 << i)) ++ a->rates |= cea_sampling_frequencies[i + 1]; ++ ++ a->channels = GRAB_BITS(buf, 0, 0, 3); ++ a->channels++; ++ ++ a->format = GRAB_BITS(buf, 0, 3, 4); ++ switch (a->format) { ++ case AUDIO_CODING_TYPE_REF_STREAM_HEADER: ++ snd_printd(KERN_INFO ++ "HDMI: audio coding type 0 not expected\n"); ++ break; ++ ++ case AUDIO_CODING_TYPE_LPCM: ++ val = GRAB_BITS(buf, 2, 0, 3); ++ a->sample_bits = 0; ++ for (i = 0; i < 3; i++) ++ if (val & (1 << i)) ++ a->sample_bits |= cea_sample_sizes[i + 1]; ++ break; ++ ++ case AUDIO_CODING_TYPE_AC3: ++ case AUDIO_CODING_TYPE_MPEG1: ++ case AUDIO_CODING_TYPE_MP3: ++ case AUDIO_CODING_TYPE_MPEG2: ++ case AUDIO_CODING_TYPE_AACLC: ++ case AUDIO_CODING_TYPE_DTS: ++ case AUDIO_CODING_TYPE_ATRAC: ++ a->max_bitrate = GRAB_BITS(buf, 2, 0, 8); ++ a->max_bitrate *= 8000; ++ break; ++ ++ case AUDIO_CODING_TYPE_SACD: ++ break; ++ ++ case AUDIO_CODING_TYPE_EAC3: ++ break; ++ ++ case AUDIO_CODING_TYPE_DTS_HD: ++ break; ++ ++ case AUDIO_CODING_TYPE_MLP: ++ break; ++ ++ case AUDIO_CODING_TYPE_DST: ++ break; ++ ++ case AUDIO_CODING_TYPE_WMAPRO: ++ a->profile = GRAB_BITS(buf, 2, 0, 3); ++ break; ++ ++ case AUDIO_CODING_TYPE_REF_CXT: ++ a->format = GRAB_BITS(buf, 2, 3, 5); ++ if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT || ++ a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) { ++ snd_printd(KERN_INFO ++ "HDMI: audio coding xtype %d not expected\n", ++ a->format); ++ a->format = 0; ++ } else ++ a->format += AUDIO_CODING_TYPE_HE_AAC - ++ AUDIO_CODING_XTYPE_HE_AAC; ++ break; ++ } ++} ++ ++/* ++ * Be careful, ELD buf could be totally rubbish! ++ */ ++static int hdmi_update_eld(struct hdmi_eld *e, ++ const unsigned char *buf, int size) ++{ ++ int mnl; ++ int i; ++ ++ e->eld_ver = GRAB_BITS(buf, 0, 3, 5); ++ if (e->eld_ver != ELD_VER_CEA_861D && ++ e->eld_ver != ELD_VER_PARTIAL) { ++ snd_printd(KERN_INFO "HDMI: Unknown ELD version %d\n", ++ e->eld_ver); ++ goto out_fail; ++ } ++ ++ e->eld_size = size; ++ e->baseline_len = GRAB_BITS(buf, 2, 0, 8); ++ mnl = GRAB_BITS(buf, 4, 0, 5); ++ e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3); ++ ++ e->support_hdcp = GRAB_BITS(buf, 5, 0, 1); ++ e->support_ai = GRAB_BITS(buf, 5, 1, 1); ++ e->conn_type = GRAB_BITS(buf, 5, 2, 2); ++ e->sad_count = GRAB_BITS(buf, 5, 4, 4); ++ ++ e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2; ++ e->spk_alloc = GRAB_BITS(buf, 7, 0, 7); ++ ++ e->port_id = get_unaligned_le64(buf + 8); ++ ++ /* not specified, but the spec's tendency is little endian */ ++ e->manufacture_id = get_unaligned_le16(buf + 16); ++ e->product_id = get_unaligned_le16(buf + 18); ++ ++ if (mnl > ELD_MAX_MNL) { ++ snd_printd(KERN_INFO "HDMI: MNL is reserved value %d\n", mnl); ++ goto out_fail; ++ } else if (ELD_FIXED_BYTES + mnl > size) { ++ snd_printd(KERN_INFO "HDMI: out of range MNL %d\n", mnl); ++ goto out_fail; ++ } else ++ strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl); ++ ++ for (i = 0; i < e->sad_count; i++) { ++ if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) { ++ snd_printd(KERN_INFO "HDMI: out of range SAD %d\n", i); ++ goto out_fail; ++ } ++ hdmi_update_short_audio_desc(e->sad + i, ++ buf + ELD_FIXED_BYTES + mnl + 3 * i); ++ } ++ ++ return 0; ++ ++out_fail: ++ e->eld_ver = 0; ++ return -EINVAL; ++} ++ ++static int hdmi_present_sense(struct hda_codec *codec, hda_nid_t nid) ++{ ++ return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0); ++} ++ ++static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid) ++{ ++ int eldv; ++ int present; ++ ++ present = hdmi_present_sense(codec, nid); ++ eldv = (present & AC_PINSENSE_ELDV); ++ present = (present & AC_PINSENSE_PRESENCE); ++ ++#ifdef CONFIG_SND_DEBUG_VERBOSE ++ printk(KERN_INFO "HDMI: sink_present = %d, eld_valid = %d\n", ++ !!present, !!eldv); ++#endif ++ ++ return eldv && present; ++} ++ ++int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid) ++{ ++ return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, ++ AC_DIPSIZE_ELD_BUF); ++} ++ ++int snd_hdmi_get_eld(struct hdmi_eld *eld, ++ struct hda_codec *codec, hda_nid_t nid) ++{ ++ int i; ++ int ret; ++ int size; ++ unsigned char *buf; ++ ++ if (!hdmi_eld_valid(codec, nid)) ++ return -ENOENT; ++ ++ size = snd_hdmi_get_eld_size(codec, nid); ++ if (size == 0) { ++ /* wfg: workaround for ASUS P5E-VM HDMI board */ ++ snd_printd(KERN_INFO "HDMI: ELD buf size is 0, force 128\n"); ++ size = 128; ++ } ++ if (size < ELD_FIXED_BYTES || size > PAGE_SIZE) { ++ snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d\n", size); ++ return -ERANGE; ++ } ++ ++ buf = kmalloc(size, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ for (i = 0; i < size; i++) ++ buf[i] = hdmi_get_eld_byte(codec, nid, i); ++ ++ ret = hdmi_update_eld(eld, buf, size); ++ ++ kfree(buf); ++ return ret; ++} ++ ++static void hdmi_show_short_audio_desc(struct cea_sad *a) ++{ ++ char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; ++ char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits ="; ++ ++ if (!a->format) ++ return; ++ ++ snd_print_pcm_rates(a->rates, buf, sizeof(buf)); ++ ++ if (a->format == AUDIO_CODING_TYPE_LPCM) ++ snd_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2 - 8)); ++ else if (a->max_bitrate) ++ snprintf(buf2, sizeof(buf2), ++ ", max bitrate = %d", a->max_bitrate); ++ else ++ buf2[0] = '\0'; ++ ++ printk(KERN_INFO "HDMI: supports coding type %s:" ++ " channels = %d, rates =%s%s\n", ++ cea_audio_coding_type_names[a->format], ++ a->channels, ++ buf, ++ buf2); ++} ++ ++void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) ++{ ++ int i, j; ++ ++ for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { ++ if (spk_alloc & (1 << i)) ++ j += snprintf(buf + j, buflen - j, " %s", ++ cea_speaker_allocation_names[i]); ++ } ++ buf[j] = '\0'; /* necessary when j == 0 */ ++} ++ ++void snd_hdmi_show_eld(struct hdmi_eld *e) ++{ ++ int i; ++ ++ printk(KERN_INFO "HDMI: detected monitor %s at connection type %s\n", ++ e->monitor_name, ++ eld_connection_type_names[e->conn_type]); ++ ++ if (e->spk_alloc) { ++ char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; ++ snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); ++ printk(KERN_INFO "HDMI: available speakers:%s\n", buf); ++ } ++ ++ for (i = 0; i < e->sad_count; i++) ++ hdmi_show_short_audio_desc(e->sad + i); ++} ++ ++#ifdef CONFIG_PROC_FS ++ ++static void hdmi_print_sad_info(int i, struct cea_sad *a, ++ struct snd_info_buffer *buffer) ++{ ++ char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; ++ ++ snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n", ++ i, a->format, cea_audio_coding_type_names[a->format]); ++ snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels); ++ ++ snd_print_pcm_rates(a->rates, buf, sizeof(buf)); ++ snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf); ++ ++ if (a->format == AUDIO_CODING_TYPE_LPCM) { ++ snd_print_pcm_bits(a->sample_bits, buf, sizeof(buf)); ++ snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n", ++ i, a->sample_bits, buf); ++ } ++ ++ if (a->max_bitrate) ++ snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n", ++ i, a->max_bitrate); ++ ++ if (a->profile) ++ snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile); ++} ++ ++static void hdmi_print_eld_info(struct snd_info_entry *entry, ++ struct snd_info_buffer *buffer) ++{ ++ struct hdmi_eld *e = entry->private_data; ++ char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; ++ int i; ++ static char *eld_versoin_names[32] = { ++ "reserved", ++ "reserved", ++ "CEA-861D or below", ++ [3 ... 30] = "reserved", ++ [31] = "partial" ++ }; ++ static char *cea_edid_version_names[8] = { ++ "no CEA EDID Timing Extension block present", ++ "CEA-861", ++ "CEA-861-A", ++ "CEA-861-B, C or D", ++ [4 ... 7] = "reserved" ++ }; ++ ++ snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); ++ snd_iprintf(buffer, "connection_type\t\t%s\n", ++ eld_connection_type_names[e->conn_type]); ++ snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver, ++ eld_versoin_names[e->eld_ver]); ++ snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver, ++ cea_edid_version_names[e->cea_edid_ver]); ++ snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id); ++ snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id); ++ snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id); ++ snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp); ++ snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai); ++ snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay); ++ ++ snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); ++ snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf); ++ ++ snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count); ++ ++ for (i = 0; i < e->sad_count; i++) ++ hdmi_print_sad_info(i, e->sad + i, buffer); ++} ++ ++static void hdmi_write_eld_info(struct snd_info_entry *entry, ++ struct snd_info_buffer *buffer) ++{ ++ struct hdmi_eld *e = entry->private_data; ++ char line[64]; ++ char name[64]; ++ char *sname; ++ long long val; ++ int n; ++ ++ while (!snd_info_get_line(buffer, line, sizeof(line))) { ++ if (sscanf(line, "%s %llx", name, &val) != 2) ++ continue; ++ /* ++ * We don't allow modification to these fields: ++ * monitor_name manufacture_id product_id ++ * eld_version edid_version ++ */ ++ if (!strcmp(name, "connection_type")) ++ e->conn_type = val; ++ else if (!strcmp(name, "port_id")) ++ e->port_id = val; ++ else if (!strcmp(name, "support_hdcp")) ++ e->support_hdcp = val; ++ else if (!strcmp(name, "support_ai")) ++ e->support_ai = val; ++ else if (!strcmp(name, "audio_sync_delay")) ++ e->aud_synch_delay = val; ++ else if (!strcmp(name, "speakers")) ++ e->spk_alloc = val; ++ else if (!strcmp(name, "sad_count")) ++ e->sad_count = val; ++ else if (!strncmp(name, "sad", 3)) { ++ sname = name + 4; ++ n = name[3] - '0'; ++ if (name[4] >= '0' && name[4] <= '9') { ++ sname++; ++ n = 10 * n + name[4] - '0'; ++ } ++ if (n < 0 || n > 31) /* double the CEA limit */ ++ continue; ++ if (!strcmp(sname, "_coding_type")) ++ e->sad[n].format = val; ++ else if (!strcmp(sname, "_channels")) ++ e->sad[n].channels = val; ++ else if (!strcmp(sname, "_rates")) ++ e->sad[n].rates = val; ++ else if (!strcmp(sname, "_bits")) ++ e->sad[n].sample_bits = val; ++ else if (!strcmp(sname, "_max_bitrate")) ++ e->sad[n].max_bitrate = val; ++ else if (!strcmp(sname, "_profile")) ++ e->sad[n].profile = val; ++ if (n >= e->sad_count) ++ e->sad_count = n + 1; ++ } ++ } ++} ++ ++ ++int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld) ++{ ++ char name[32]; ++ struct snd_info_entry *entry; ++ int err; ++ ++ snprintf(name, sizeof(name), "eld#%d", codec->addr); ++ err = snd_card_proc_new(codec->bus->card, name, &entry); ++ if (err < 0) ++ return err; ++ ++ snd_info_set_text_ops(entry, eld, hdmi_print_eld_info); ++ entry->c.text.write = hdmi_write_eld_info; ++ entry->mode |= S_IWUSR; ++ eld->proc_entry = entry; ++ ++ return 0; ++} ++ ++void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld) ++{ ++ if (!codec->bus->shutdown && eld->proc_entry) { ++ snd_device_free(codec->bus->card, eld->proc_entry); ++ eld->proc_entry = NULL; ++ } ++} ++ ++#endif /* CONFIG_PROC_FS */ +diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c +index 59e4389..1d5797a 100644 +--- a/sound/pci/hda/hda_generic.c ++++ b/sound/pci/hda/hda_generic.c +@@ -144,9 +144,9 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid + node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + + if (node->type == AC_WID_PIN) { +- node->pin_caps = snd_hda_param_read(codec, node->nid, AC_PAR_PIN_CAP); ++ node->pin_caps = snd_hda_query_pin_caps(codec, node->nid); + node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); +- node->def_cfg = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); ++ node->def_cfg = snd_hda_codec_get_pincfg(codec, node->nid); + } + + if (node->wid_caps & AC_WCAP_OUT_AMP) { +@@ -174,7 +174,8 @@ static int build_afg_tree(struct hda_codec *codec) + int i, nodes, err; + hda_nid_t nid; + +- snd_assert(spec, return -EINVAL); ++ if (snd_BUG_ON(!spec)) ++ return -EINVAL; + + spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP); + spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP); +@@ -722,7 +723,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); +- if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) ++ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); ++ if (err < 0) + return err; + created = 1; + } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && +@@ -731,7 +733,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); +- if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) ++ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); ++ if (err < 0) + return err; + created = 1; + } +@@ -744,14 +747,16 @@ 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); +- if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) ++ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); ++ if (err < 0) + return err; + created = 1; + } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && + (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); +- if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) ++ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); ++ if (err < 0) + return err; + created = 1; + } +@@ -848,8 +853,8 @@ static int build_input_controls(struct hda_codec *codec) + } + + /* create input MUX if multiple sources are available */ +- if ((err = snd_ctl_add(codec->bus->card, +- snd_ctl_new1(&cap_sel, codec))) < 0) ++ err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec)); ++ if (err < 0) + return err; + + /* no volume control? */ +@@ -866,8 +871,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); +- if ((err = snd_ctl_add(codec->bus->card, +- snd_ctl_new1(&knew, codec))) < 0) ++ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); ++ if (err < 0) + return err; + } + +@@ -1096,3 +1101,4 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec) + snd_hda_generic_free(codec); + return err; + } ++EXPORT_SYMBOL(snd_hda_parse_generic_codec); +diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c +index 6e18a42..1c57505 100644 +--- a/sound/pci/hda/hda_hwdep.c ++++ b/sound/pci/hda/hda_hwdep.c +@@ -23,10 +23,18 @@ + #include + #include + #include ++#include + #include + #include "hda_codec.h" + #include "hda_local.h" + #include ++#include ++ ++/* hint string pair */ ++struct hda_hint { ++ const char *key; ++ const char *val; /* contained in the same alloc as key */ ++}; + + /* + * write/read an out-of-bound verb +@@ -95,7 +103,27 @@ static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file) + return 0; + } + +-int __devinit snd_hda_create_hwdep(struct hda_codec *codec) ++static void clear_hwdep_elements(struct hda_codec *codec) ++{ ++ int i; ++ ++ /* clear init verbs */ ++ snd_array_free(&codec->init_verbs); ++ /* clear hints */ ++ for (i = 0; i < codec->hints.used; i++) { ++ struct hda_hint *hint = snd_array_elem(&codec->hints, i); ++ kfree(hint->key); /* we don't need to free hint->val */ ++ } ++ snd_array_free(&codec->hints); ++ snd_array_free(&codec->user_pins); ++} ++ ++static void hwdep_free(struct snd_hwdep *hwdep) ++{ ++ clear_hwdep_elements(hwdep->private_data); ++} ++ ++int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec) + { + char hwname[16]; + struct snd_hwdep *hwdep; +@@ -109,6 +137,7 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec) + sprintf(hwdep->name, "HDA Codec %d", codec->addr); + hwdep->iface = SNDRV_HWDEP_IFACE_HDA; + hwdep->private_data = codec; ++ hwdep->private_free = hwdep_free; + hwdep->exclusive = 1; + + hwdep->ops.open = hda_hwdep_open; +@@ -117,5 +146,407 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec) + hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat; + #endif + ++ snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32); ++ snd_array_init(&codec->hints, sizeof(struct hda_hint), 32); ++ snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_SND_HDA_RECONFIG ++ ++/* ++ * sysfs interface ++ */ ++ ++static int clear_codec(struct hda_codec *codec) ++{ ++ int err; ++ ++ err = snd_hda_codec_reset(codec); ++ if (err < 0) { ++ snd_printk(KERN_ERR "The codec is being used, can't free.\n"); ++ return err; ++ } ++ clear_hwdep_elements(codec); + return 0; + } ++ ++static int reconfig_codec(struct hda_codec *codec) ++{ ++ int err; ++ ++ snd_hda_power_up(codec); ++ snd_printk(KERN_INFO "hda-codec: reconfiguring\n"); ++ err = snd_hda_codec_reset(codec); ++ if (err < 0) { ++ snd_printk(KERN_ERR ++ "The codec is being used, can't reconfigure.\n"); ++ goto error; ++ } ++ err = snd_hda_codec_configure(codec); ++ if (err < 0) ++ goto error; ++ /* rebuild PCMs */ ++ err = snd_hda_codec_build_pcms(codec); ++ if (err < 0) ++ goto error; ++ /* rebuild mixers */ ++ err = snd_hda_codec_build_controls(codec); ++ if (err < 0) ++ goto error; ++ err = snd_card_register(codec->bus->card); ++ error: ++ snd_hda_power_down(codec); ++ return err; ++} ++ ++/* ++ * allocate a string at most len chars, and remove the trailing EOL ++ */ ++static char *kstrndup_noeol(const char *src, size_t len) ++{ ++ char *s = kstrndup(src, len, GFP_KERNEL); ++ char *p; ++ if (!s) ++ return NULL; ++ p = strchr(s, '\n'); ++ if (p) ++ *p = 0; ++ return s; ++} ++ ++#define CODEC_INFO_SHOW(type) \ ++static ssize_t type##_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; \ ++ return sprintf(buf, "0x%x\n", codec->type); \ ++} ++ ++#define CODEC_INFO_STR_SHOW(type) \ ++static ssize_t type##_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; \ ++ return sprintf(buf, "%s\n", \ ++ codec->type ? codec->type : ""); \ ++} ++ ++CODEC_INFO_SHOW(vendor_id); ++CODEC_INFO_SHOW(subsystem_id); ++CODEC_INFO_SHOW(revision_id); ++CODEC_INFO_SHOW(afg); ++CODEC_INFO_SHOW(mfg); ++CODEC_INFO_STR_SHOW(name); ++CODEC_INFO_STR_SHOW(modelname); ++ ++#define CODEC_INFO_STORE(type) \ ++static ssize_t type##_store(struct device *dev, \ ++ struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \ ++ struct hda_codec *codec = hwdep->private_data; \ ++ char *after; \ ++ codec->type = simple_strtoul(buf, &after, 0); \ ++ return count; \ ++} ++ ++#define CODEC_INFO_STR_STORE(type) \ ++static ssize_t type##_store(struct device *dev, \ ++ struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \ ++ struct hda_codec *codec = hwdep->private_data; \ ++ char *s = kstrndup_noeol(buf, 64); \ ++ if (!s) \ ++ return -ENOMEM; \ ++ kfree(codec->type); \ ++ codec->type = s; \ ++ return count; \ ++} ++ ++CODEC_INFO_STORE(vendor_id); ++CODEC_INFO_STORE(subsystem_id); ++CODEC_INFO_STORE(revision_id); ++CODEC_INFO_STR_STORE(name); ++CODEC_INFO_STR_STORE(modelname); ++ ++#define CODEC_ACTION_STORE(type) \ ++static ssize_t type##_store(struct device *dev, \ ++ struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \ ++ struct hda_codec *codec = hwdep->private_data; \ ++ int err = 0; \ ++ if (*buf) \ ++ err = type##_codec(codec); \ ++ return err < 0 ? err : count; \ ++} ++ ++CODEC_ACTION_STORE(reconfig); ++CODEC_ACTION_STORE(clear); ++ ++static ssize_t init_verbs_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; ++ int i, len = 0; ++ for (i = 0; i < codec->init_verbs.used; i++) { ++ struct hda_verb *v = snd_array_elem(&codec->init_verbs, i); ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "0x%02x 0x%03x 0x%04x\n", ++ v->nid, v->verb, v->param); ++ } ++ return len; ++} ++ ++static ssize_t init_verbs_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct snd_hwdep *hwdep = dev_get_drvdata(dev); ++ struct hda_codec *codec = hwdep->private_data; ++ struct hda_verb *v; ++ int nid, verb, param; ++ ++ if (sscanf(buf, "%i %i %i", &nid, &verb, ¶m) != 3) ++ return -EINVAL; ++ if (!nid || !verb) ++ return -EINVAL; ++ v = snd_array_new(&codec->init_verbs); ++ if (!v) ++ return -ENOMEM; ++ v->nid = nid; ++ v->verb = verb; ++ v->param = param; ++ return count; ++} ++ ++static ssize_t hints_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; ++ int i, len = 0; ++ for (i = 0; i < codec->hints.used; i++) { ++ struct hda_hint *hint = snd_array_elem(&codec->hints, i); ++ len += snprintf(buf + len, PAGE_SIZE - len, ++ "%s = %s\n", hint->key, hint->val); ++ } ++ return len; ++} ++ ++static struct hda_hint *get_hint(struct hda_codec *codec, const char *key) ++{ ++ int i; ++ ++ for (i = 0; i < codec->hints.used; i++) { ++ struct hda_hint *hint = snd_array_elem(&codec->hints, i); ++ if (!strcmp(hint->key, key)) ++ return hint; ++ } ++ return NULL; ++} ++ ++static void remove_trail_spaces(char *str) ++{ ++ char *p; ++ if (!*str) ++ return; ++ p = str + strlen(str) - 1; ++ for (; isspace(*p); p--) { ++ *p = 0; ++ if (p == str) ++ return; ++ } ++} ++ ++#define MAX_HINTS 1024 ++ ++static ssize_t hints_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct snd_hwdep *hwdep = dev_get_drvdata(dev); ++ struct hda_codec *codec = hwdep->private_data; ++ char *key, *val; ++ struct hda_hint *hint; ++ ++ while (isspace(*buf)) ++ buf++; ++ if (!*buf || *buf == '#' || *buf == '\n') ++ return count; ++ if (*buf == '=') ++ return -EINVAL; ++ key = kstrndup_noeol(buf, 1024); ++ if (!key) ++ return -ENOMEM; ++ /* extract key and val */ ++ val = strchr(key, '='); ++ if (!val) { ++ kfree(key); ++ return -EINVAL; ++ } ++ *val++ = 0; ++ while (isspace(*val)) ++ val++; ++ remove_trail_spaces(key); ++ remove_trail_spaces(val); ++ hint = get_hint(codec, key); ++ if (hint) { ++ /* replace */ ++ kfree(hint->key); ++ hint->key = key; ++ hint->val = val; ++ return count; ++ } ++ /* allocate a new hint entry */ ++ if (codec->hints.used >= MAX_HINTS) ++ hint = NULL; ++ else ++ hint = snd_array_new(&codec->hints); ++ if (!hint) { ++ kfree(key); ++ return -ENOMEM; ++ } ++ hint->key = key; ++ hint->val = val; ++ return count; ++} ++ ++static ssize_t pin_configs_show(struct hda_codec *codec, ++ struct snd_array *list, ++ char *buf) ++{ ++ int i, len = 0; ++ for (i = 0; i < list->used; i++) { ++ struct hda_pincfg *pin = snd_array_elem(list, i); ++ len += sprintf(buf + len, "0x%02x 0x%08x\n", ++ pin->nid, pin->cfg); ++ } ++ return len; ++} ++ ++static ssize_t init_pin_configs_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; ++ return pin_configs_show(codec, &codec->init_pins, buf); ++} ++ ++static ssize_t user_pin_configs_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; ++ return pin_configs_show(codec, &codec->user_pins, buf); ++} ++ ++static ssize_t driver_pin_configs_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; ++ return pin_configs_show(codec, &codec->driver_pins, buf); ++} ++ ++#define MAX_PIN_CONFIGS 32 ++ ++static ssize_t user_pin_configs_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct snd_hwdep *hwdep = dev_get_drvdata(dev); ++ struct hda_codec *codec = hwdep->private_data; ++ int nid, cfg; ++ int err; ++ ++ if (sscanf(buf, "%i %i", &nid, &cfg) != 2) ++ return -EINVAL; ++ if (!nid) ++ return -EINVAL; ++ err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); ++ if (err < 0) ++ return err; ++ return count; ++} ++ ++#define CODEC_ATTR_RW(type) \ ++ __ATTR(type, 0644, type##_show, type##_store) ++#define CODEC_ATTR_RO(type) \ ++ __ATTR_RO(type) ++#define CODEC_ATTR_WO(type) \ ++ __ATTR(type, 0200, NULL, type##_store) ++ ++static struct device_attribute codec_attrs[] = { ++ CODEC_ATTR_RW(vendor_id), ++ CODEC_ATTR_RW(subsystem_id), ++ CODEC_ATTR_RW(revision_id), ++ CODEC_ATTR_RO(afg), ++ CODEC_ATTR_RO(mfg), ++ CODEC_ATTR_RW(name), ++ CODEC_ATTR_RW(modelname), ++ CODEC_ATTR_RW(init_verbs), ++ CODEC_ATTR_RW(hints), ++ CODEC_ATTR_RO(init_pin_configs), ++ CODEC_ATTR_RW(user_pin_configs), ++ CODEC_ATTR_RO(driver_pin_configs), ++ CODEC_ATTR_WO(reconfig), ++ CODEC_ATTR_WO(clear), ++}; ++ ++/* ++ * create sysfs files on hwdep directory ++ */ ++int snd_hda_hwdep_add_sysfs(struct hda_codec *codec) ++{ ++ struct snd_hwdep *hwdep = codec->hwdep; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(codec_attrs); i++) ++ snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, ++ hwdep->device, &codec_attrs[i]); ++ return 0; ++} ++ ++/* ++ * Look for hint string ++ */ ++const char *snd_hda_get_hint(struct hda_codec *codec, const char *key) ++{ ++ struct hda_hint *hint = get_hint(codec, key); ++ return hint ? hint->val : NULL; ++} ++EXPORT_SYMBOL_HDA(snd_hda_get_hint); ++ ++int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) ++{ ++ const char *p = snd_hda_get_hint(codec, key); ++ if (!p || !*p) ++ return -ENOENT; ++ switch (toupper(*p)) { ++ case 'T': /* true */ ++ case 'Y': /* yes */ ++ case '1': ++ return 1; ++ } ++ return 0; ++} ++EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint); ++ ++#endif /* CONFIG_SND_HDA_RECONFIG */ +diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c +index d9d2943..d1c03f9 100644 +--- a/sound/pci/hda/hda_intel.c ++++ b/sound/pci/hda/hda_intel.c +@@ -58,6 +58,7 @@ static char *model[SNDRV_CARDS]; + static int position_fix[SNDRV_CARDS]; + 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; + +@@ -76,6 +77,8 @@ module_param_array(bdl_pos_adj, int, NULL, 0644); + MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset."); + module_param_array(probe_mask, int, NULL, 0444); + MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1)."); ++module_param_array(probe_only, bool, NULL, 0444); ++MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization."); + module_param(single_cmd, bool, 0444); + MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " + "(for debugging only)."); +@@ -83,7 +86,10 @@ module_param(enable_msi, int, 0444); + MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); + + #ifdef CONFIG_SND_HDA_POWER_SAVE +-/* power_save option is defined in hda_codec.c */ ++static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; ++module_param(power_save, int, 0644); ++MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " ++ "(in second, 0 = disable)."); + + /* reset the HD-audio controller in power save mode. + * this may give more power-saving, but will take longer time to +@@ -292,6 +298,8 @@ enum { + /* Define VIA HD Audio Device ID*/ + #define VIA_HDAC_DEVICE_ID 0x3288 + ++/* HD Audio class code */ ++#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 + + /* + */ +@@ -304,6 +312,8 @@ struct azx_dev { + unsigned int period_bytes; /* size of the period in bytes */ + unsigned int frags; /* number for period in the play buffer */ + unsigned int fifo_size; /* FIFO size */ ++ unsigned long start_jiffies; /* start + minimum jiffies */ ++ unsigned long min_jiffies; /* minimum jiffies before position is valid */ + + void __iomem *sd_addr; /* stream descriptor pointer */ + +@@ -322,7 +332,7 @@ struct azx_dev { + unsigned int opened :1; + unsigned int running :1; + unsigned int irq_pending :1; +- unsigned int irq_ignore :1; ++ unsigned int start_flag: 1; /* stream full start flag */ + /* + * For VIA: + * A flag to ensure DMA position is 0 +@@ -373,6 +383,7 @@ struct azx { + + /* HD codec */ + unsigned short codec_mask; ++ int codec_probe_mask; /* copied from probe_mask option */ + struct hda_bus *bus; + + /* CORB/RIRB */ +@@ -392,6 +403,7 @@ struct azx { + unsigned int msi :1; + unsigned int irq_pending_warned :1; + unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */ ++ unsigned int probing :1; /* codec probing phase */ + + /* for debugging */ + unsigned int last_cmd; /* last issued command (to sync) */ +@@ -414,6 +426,7 @@ enum { + AZX_DRIVER_ULI, + AZX_DRIVER_NVIDIA, + AZX_DRIVER_TERA, ++ AZX_DRIVER_GENERIC, + AZX_NUM_DRIVERS, /* keep this as last entry */ + }; + +@@ -427,6 +440,7 @@ static char *driver_short_names[] __devinitdata = { + [AZX_DRIVER_ULI] = "HDA ULI M5461", + [AZX_DRIVER_NVIDIA] = "HDA NVidia", + [AZX_DRIVER_TERA] = "HDA Teradici", ++ [AZX_DRIVER_GENERIC] = "HD-Audio Generic", + }; + + /* +@@ -527,9 +541,9 @@ static void azx_free_cmd_io(struct azx *chip) + } + + /* send a command */ +-static int azx_corb_send_cmd(struct hda_codec *codec, u32 val) ++static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) + { +- struct azx *chip = codec->bus->private_data; ++ struct azx *chip = bus->private_data; + unsigned int wp; + + /* add command to corb */ +@@ -577,9 +591,9 @@ static void azx_update_rirb(struct azx *chip) + } + + /* receive a response */ +-static unsigned int azx_rirb_get_response(struct hda_codec *codec) ++static unsigned int azx_rirb_get_response(struct hda_bus *bus) + { +- struct azx *chip = codec->bus->private_data; ++ struct azx *chip = bus->private_data; + unsigned long timeout; + + again: +@@ -596,7 +610,7 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec) + } + if (time_after(jiffies, timeout)) + break; +- if (codec->bus->needs_damn_long_delay) ++ if (bus->needs_damn_long_delay) + msleep(2); /* temporary workaround */ + else { + udelay(10); +@@ -624,6 +638,14 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec) + 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 ++ * slot. Better to return an error and reset the system. ++ */ ++ return -1; ++ } ++ + snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, " + "switching to single_cmd mode: last cmd=0x%08x\n", + chip->last_cmd); +@@ -646,9 +668,9 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec) + */ + + /* send a command */ +-static int azx_single_send_cmd(struct hda_codec *codec, u32 val) ++static int azx_single_send_cmd(struct hda_bus *bus, u32 val) + { +- struct azx *chip = codec->bus->private_data; ++ struct azx *chip = bus->private_data; + int timeout = 50; + + while (timeout--) { +@@ -671,9 +693,9 @@ static int azx_single_send_cmd(struct hda_codec *codec, u32 val) + } + + /* receive a response */ +-static unsigned int azx_single_get_response(struct hda_codec *codec) ++static unsigned int azx_single_get_response(struct hda_bus *bus) + { +- struct azx *chip = codec->bus->private_data; ++ struct azx *chip = bus->private_data; + int timeout = 50; + + while (timeout--) { +@@ -696,38 +718,29 @@ static unsigned int azx_single_get_response(struct hda_codec *codec) + */ + + /* send a command */ +-static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, +- int direct, unsigned int verb, +- unsigned int para) +-{ +- struct azx *chip = codec->bus->private_data; +- u32 val; +- +- val = (u32)(codec->addr & 0x0f) << 28; +- val |= (u32)direct << 27; +- val |= (u32)nid << 20; +- val |= verb << 8; +- val |= para; +- chip->last_cmd = val; ++static int azx_send_cmd(struct hda_bus *bus, unsigned int val) ++{ ++ struct azx *chip = bus->private_data; + ++ chip->last_cmd = val; + if (chip->single_cmd) +- return azx_single_send_cmd(codec, val); ++ return azx_single_send_cmd(bus, val); + else +- return azx_corb_send_cmd(codec, val); ++ return azx_corb_send_cmd(bus, val); + } + + /* get a response */ +-static unsigned int azx_get_response(struct hda_codec *codec) ++static unsigned int azx_get_response(struct hda_bus *bus) + { +- struct azx *chip = codec->bus->private_data; ++ struct azx *chip = bus->private_data; + if (chip->single_cmd) +- return azx_single_get_response(codec); ++ return azx_single_get_response(bus); + else +- return azx_rirb_get_response(codec); ++ return azx_rirb_get_response(bus); + } + + #ifdef CONFIG_SND_HDA_POWER_SAVE +-static void azx_power_notify(struct hda_codec *codec); ++static void azx_power_notify(struct hda_bus *bus); + #endif + + /* reset codec link */ +@@ -848,13 +861,18 @@ static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev) + SD_CTL_DMA_START | SD_INT_MASK); + } + +-/* stop a stream */ +-static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev) ++/* stop DMA */ ++static void azx_stream_clear(struct azx *chip, struct azx_dev *azx_dev) + { +- /* stop DMA */ + azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & + ~(SD_CTL_DMA_START | SD_INT_MASK)); + azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ ++} ++ ++/* stop a stream */ ++static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev) ++{ ++ azx_stream_clear(chip, azx_dev); + /* disable SIE */ + azx_writeb(chip, INTCTL, + azx_readb(chip, INTCTL) & ~(1 << azx_dev->index)); +@@ -959,7 +977,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) + struct azx *chip = dev_id; + struct azx_dev *azx_dev; + u32 status; +- int i; ++ int i, ok; + + spin_lock(&chip->reg_lock); + +@@ -975,21 +993,18 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) + azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); + if (!azx_dev->substream || !azx_dev->running) + continue; +- /* ignore the first dummy IRQ (due to pos_adj) */ +- if (azx_dev->irq_ignore) { +- azx_dev->irq_ignore = 0; +- continue; +- } + /* check whether this IRQ is really acceptable */ +- if (azx_position_ok(chip, azx_dev)) { ++ ok = azx_position_ok(chip, azx_dev); ++ if (ok == 1) { + azx_dev->irq_pending = 0; + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(azx_dev->substream); + spin_lock(&chip->reg_lock); +- } else { ++ } else if (ok == 0 && chip->bus && chip->bus->workq) { + /* bogus IRQ, process it later */ + azx_dev->irq_pending = 1; +- schedule_work(&chip->irq_pending_work); ++ queue_work(chip->bus->workq, ++ &chip->irq_pending_work); + } + } + } +@@ -1067,15 +1082,13 @@ static int azx_setup_periods(struct azx *chip, + azx_sd_writel(azx_dev, SD_BDLPL, 0); + azx_sd_writel(azx_dev, SD_BDLPU, 0); + +- period_bytes = snd_pcm_lib_period_bytes(substream); +- azx_dev->period_bytes = period_bytes; ++ period_bytes = azx_dev->period_bytes; + periods = azx_dev->bufsize / period_bytes; + + /* program the initial BDL entries */ + bdl = (u32 *)azx_dev->bdl.area; + ofs = 0; + azx_dev->frags = 0; +- azx_dev->irq_ignore = 0; + pos_adj = bdl_pos_adj[chip->dev_index]; + if (pos_adj > 0) { + struct snd_pcm_runtime *runtime = substream->runtime; +@@ -1096,7 +1109,6 @@ static int azx_setup_periods(struct azx *chip, + &bdl, ofs, pos_adj, 1); + if (ofs < 0) + goto error; +- azx_dev->irq_ignore = 1; + } + } else + pos_adj = 0; +@@ -1115,24 +1127,17 @@ static int azx_setup_periods(struct azx *chip, + error: + snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n", + azx_dev->bufsize, period_bytes); +- /* reset */ +- azx_sd_writel(azx_dev, SD_BDLPL, 0); +- azx_sd_writel(azx_dev, SD_BDLPU, 0); + return -EINVAL; + } + +-/* +- * set up the SD for streaming +- */ +-static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) ++/* reset stream */ ++static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev) + { + unsigned char val; + int timeout; + +- /* make sure the run bit is zero for SD */ +- azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & +- ~SD_CTL_DMA_START); +- /* reset stream */ ++ azx_stream_clear(chip, azx_dev); ++ + azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | + SD_CTL_STREAM_RESET); + udelay(3); +@@ -1150,6 +1155,17 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) + --timeout) + ; + ++ /* reset first position - may not be synced with hw at this time */ ++ *azx_dev->posbuf = 0; ++} ++ ++/* ++ * set up the SD for streaming ++ */ ++static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) ++{ ++ /* make sure the run bit is zero for SD */ ++ azx_stream_clear(chip, azx_dev); + /* program the stream_tag */ + azx_sd_writel(azx_dev, SD_CTL, + (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK)| +@@ -1187,6 +1203,28 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) + return 0; + } + ++/* ++ * Probe the given codec address ++ */ ++static int probe_codec(struct azx *chip, int addr) ++{ ++ unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | ++ (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; ++ unsigned int res; ++ ++ chip->probing = 1; ++ azx_send_cmd(chip->bus, cmd); ++ res = azx_get_response(chip->bus); ++ chip->probing = 0; ++ if (res == -1) ++ return -EIO; ++ snd_printdd("hda_intel: codec #%d probed OK\n", addr); ++ return 0; ++} ++ ++static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, ++ struct hda_pcm *cpcm); ++static void azx_stop_chip(struct azx *chip); + + /* + * Codec initialization +@@ -1197,21 +1235,12 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = { + [AZX_DRIVER_TERA] = 1, + }; + +-/* number of slots to probe as default +- * this can be different from azx_max_codecs[] -- e.g. some boards +- * report wrongly the non-existing 4th slot availability +- */ +-static unsigned int azx_default_codecs[AZX_NUM_DRIVERS] __devinitdata = { +- [AZX_DRIVER_ICH] = 3, +- [AZX_DRIVER_ATI] = 3, +-}; +- + static int __devinit azx_codec_create(struct azx *chip, const char *model, +- unsigned int codec_probe_mask) ++ int no_init) + { + struct hda_bus_template bus_temp; +- int c, codecs, audio_codecs, err; +- int def_slots, max_slots; ++ int c, codecs, err; ++ int max_slots; + + memset(&bus_temp, 0, sizeof(bus_temp)); + bus_temp.private_data = chip; +@@ -1219,7 +1248,9 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, + bus_temp.pci = chip->pci; + bus_temp.ops.command = azx_send_cmd; + bus_temp.ops.get_response = azx_get_response; ++ bus_temp.ops.attach_pcm = azx_attach_pcm_stream; + #ifdef CONFIG_SND_HDA_POWER_SAVE ++ bus_temp.power_save = &power_save; + bus_temp.ops.pm_notify = azx_power_notify; + #endif + +@@ -1230,33 +1261,43 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, + if (chip->driver_type == AZX_DRIVER_NVIDIA) + chip->bus->needs_damn_long_delay = 1; + +- codecs = audio_codecs = 0; ++ codecs = 0; + max_slots = azx_max_codecs[chip->driver_type]; + if (!max_slots) + max_slots = AZX_MAX_CODECS; +- def_slots = azx_default_codecs[chip->driver_type]; +- if (!def_slots) +- def_slots = max_slots; +- for (c = 0; c < def_slots; c++) { +- if ((chip->codec_mask & (1 << c)) & codec_probe_mask) { ++ ++ /* First try to probe all given codec slots */ ++ for (c = 0; c < max_slots; c++) { ++ if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) { ++ if (probe_codec(chip, c) < 0) { ++ /* Some BIOSen give you wrong codec addresses ++ * that don't exist ++ */ ++ snd_printk(KERN_WARNING ++ "hda_intel: Codec #%d probe error; " ++ "disabling it...\n", c); ++ chip->codec_mask &= ~(1 << c); ++ /* More badly, accessing to a non-existing ++ * codec often screws up the controller chip, ++ * and distrubs the further communications. ++ * Thus if an error occurs during probing, ++ * better to reset the controller chip to ++ * get back to the sanity state. ++ */ ++ azx_stop_chip(chip); ++ azx_init_chip(chip); ++ } ++ } ++ } ++ ++ /* Then create codec instances */ ++ for (c = 0; c < max_slots; c++) { ++ if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) { + struct hda_codec *codec; +- err = snd_hda_codec_new(chip->bus, c, &codec); ++ err = snd_hda_codec_new(chip->bus, c, !no_init, &codec); + if (err < 0) + continue; + codecs++; +- if (codec->afg) +- audio_codecs++; +- } +- } +- if (!audio_codecs) { +- /* probe additional slots if no codec is found */ +- for (; c < max_slots; c++) { +- if ((chip->codec_mask & (1 << c)) & codec_probe_mask) { +- err = snd_hda_codec_new(chip->bus, c, NULL); +- if (err < 0) +- continue; +- codecs++; +- } + } + } + if (!codecs) { +@@ -1369,6 +1410,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) + runtime->private_data = azx_dev; + snd_pcm_set_sync(substream); + mutex_unlock(&chip->open_mutex); ++ + return 0; + } + +@@ -1395,6 +1437,11 @@ static int azx_pcm_close(struct snd_pcm_substream *substream) + static int azx_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) + { ++ struct azx_dev *azx_dev = get_azx_dev(substream); ++ ++ azx_dev->bufsize = 0; ++ azx_dev->period_bytes = 0; ++ azx_dev->format_val = 0; + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + } +@@ -1409,6 +1456,9 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream) + azx_sd_writel(azx_dev, SD_BDLPL, 0); + azx_sd_writel(azx_dev, SD_BDLPU, 0); + azx_sd_writel(azx_dev, SD_CTL, 0); ++ azx_dev->bufsize = 0; ++ azx_dev->period_bytes = 0; ++ azx_dev->format_val = 0; + + hinfo->ops.cleanup(hinfo, apcm->codec, substream); + +@@ -1422,23 +1472,40 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) + struct azx_dev *azx_dev = get_azx_dev(substream); + struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + struct snd_pcm_runtime *runtime = substream->runtime; ++ unsigned int bufsize, period_bytes, format_val; ++ int err; + +- azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream); +- azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate, +- runtime->channels, +- runtime->format, +- hinfo->maxbps); +- if (!azx_dev->format_val) { ++ azx_stream_reset(chip, azx_dev); ++ format_val = snd_hda_calc_stream_format(runtime->rate, ++ runtime->channels, ++ runtime->format, ++ hinfo->maxbps); ++ if (!format_val) { + snd_printk(KERN_ERR SFX + "invalid format_val, rate=%d, ch=%d, format=%d\n", + runtime->rate, runtime->channels, runtime->format); + return -EINVAL; + } + ++ bufsize = snd_pcm_lib_buffer_bytes(substream); ++ period_bytes = snd_pcm_lib_period_bytes(substream); ++ + snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n", +- azx_dev->bufsize, azx_dev->format_val); +- if (azx_setup_periods(chip, substream, azx_dev) < 0) +- return -EINVAL; ++ bufsize, format_val); ++ ++ if (bufsize != azx_dev->bufsize || ++ period_bytes != azx_dev->period_bytes || ++ format_val != azx_dev->format_val) { ++ azx_dev->bufsize = bufsize; ++ azx_dev->period_bytes = period_bytes; ++ azx_dev->format_val = format_val; ++ err = azx_setup_periods(chip, substream, azx_dev); ++ if (err < 0) ++ return err; ++ } ++ ++ azx_dev->min_jiffies = (runtime->period_size * HZ) / ++ (runtime->rate * 2); + azx_setup_controller(chip, azx_dev); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1; +@@ -1455,13 +1522,14 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) + struct azx *chip = apcm->chip; + struct azx_dev *azx_dev; + struct snd_pcm_substream *s; +- int start, nsync = 0, sbits = 0; ++ int rstart = 0, start, nsync = 0, sbits = 0; + int nwait, timeout; + + switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ rstart = 1; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: +- case SNDRV_PCM_TRIGGER_START: + start = 1; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: +@@ -1491,6 +1559,10 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) + if (s->pcm->card != substream->pcm->card) + continue; + azx_dev = get_azx_dev(s); ++ if (rstart) { ++ azx_dev->start_flag = 1; ++ azx_dev->start_jiffies = jiffies + azx_dev->min_jiffies; ++ } + if (start) + azx_stream_start(chip, azx_dev); + else +@@ -1640,6 +1712,11 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) + { + unsigned int pos; + ++ if (azx_dev->start_flag && ++ time_before_eq(jiffies, azx_dev->start_jiffies)) ++ return -1; /* bogus (too early) interrupt */ ++ azx_dev->start_flag = 0; ++ + pos = azx_get_position(chip, azx_dev); + if (chip->position_fix == POS_FIX_AUTO) { + if (!pos) { +@@ -1708,7 +1785,6 @@ static void azx_clear_irq_pending(struct azx *chip) + for (i = 0; i < chip->num_streams; i++) + chip->azx_dev[i].irq_pending = 0; + spin_unlock_irq(&chip->reg_lock); +- flush_scheduled_work(); + } + + static struct snd_pcm_ops azx_pcm_ops = { +@@ -1725,110 +1801,59 @@ static struct snd_pcm_ops azx_pcm_ops = { + + static void azx_pcm_free(struct snd_pcm *pcm) + { +- kfree(pcm->private_data); ++ struct azx_pcm *apcm = pcm->private_data; ++ if (apcm) { ++ apcm->chip->pcm[pcm->device] = NULL; ++ kfree(apcm); ++ } + } + +-static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, +- struct hda_pcm *cpcm) ++static int ++azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, ++ struct hda_pcm *cpcm) + { +- int err; ++ struct azx *chip = bus->private_data; + struct snd_pcm *pcm; + struct azx_pcm *apcm; ++ int pcm_dev = cpcm->device; ++ int s, err; + +- /* if no substreams are defined for both playback and capture, +- * it's just a placeholder. ignore it. +- */ +- if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) +- return 0; +- +- snd_assert(cpcm->name, return -EINVAL); +- +- err = snd_pcm_new(chip->card, cpcm->name, cpcm->device, +- cpcm->stream[0].substreams, +- cpcm->stream[1].substreams, ++ if (pcm_dev >= AZX_MAX_PCMS) { ++ snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n", ++ pcm_dev); ++ return -EINVAL; ++ } ++ if (chip->pcm[pcm_dev]) { ++ snd_printk(KERN_ERR SFX "PCM %d already exists\n", pcm_dev); ++ return -EBUSY; ++ } ++ err = snd_pcm_new(chip->card, cpcm->name, pcm_dev, ++ cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams, ++ cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams, + &pcm); + if (err < 0) + return err; + strcpy(pcm->name, cpcm->name); +- apcm = kmalloc(sizeof(*apcm), GFP_KERNEL); ++ apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); + if (apcm == NULL) + return -ENOMEM; + apcm->chip = chip; + apcm->codec = codec; +- apcm->hinfo[0] = &cpcm->stream[0]; +- apcm->hinfo[1] = &cpcm->stream[1]; + pcm->private_data = apcm; + pcm->private_free = azx_pcm_free; +- if (cpcm->stream[0].substreams) +- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops); +- if (cpcm->stream[1].substreams) +- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops); ++ if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM) ++ pcm->dev_class = SNDRV_PCM_CLASS_MODEM; ++ chip->pcm[pcm_dev] = pcm; ++ cpcm->pcm = pcm; ++ for (s = 0; s < 2; s++) { ++ apcm->hinfo[s] = &cpcm->stream[s]; ++ if (cpcm->stream[s].substreams) ++ snd_pcm_set_ops(pcm, s, &azx_pcm_ops); ++ } ++ /* buffer pre-allocation */ + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), + 1024 * 64, 32 * 1024 * 1024); +- chip->pcm[cpcm->device] = pcm; +- return 0; +-} +- +-static int __devinit azx_pcm_create(struct azx *chip) +-{ +- 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 +- }; +- /* normal audio device indices; not linear to keep compatibility */ +- static int audio_idx[4] = { 0, 2, 4, 5 }; +- struct hda_codec *codec; +- int c, err; +- int num_devs[HDA_PCM_NTYPES]; +- +- err = snd_hda_build_pcms(chip->bus); +- if (err < 0) +- return err; +- +- /* create audio PCMs */ +- memset(num_devs, 0, sizeof(num_devs)); +- list_for_each_entry(codec, &chip->bus->codec_list, list) { +- for (c = 0; c < codec->num_pcms; c++) { +- struct hda_pcm *cpcm = &codec->pcm_info[c]; +- int type = cpcm->pcm_type; +- switch (type) { +- case HDA_PCM_TYPE_AUDIO: +- if (num_devs[type] >= ARRAY_SIZE(audio_idx)) { +- snd_printk(KERN_WARNING +- "Too many audio devices\n"); +- continue; +- } +- cpcm->device = audio_idx[num_devs[type]]; +- break; +- case HDA_PCM_TYPE_SPDIF: +- case HDA_PCM_TYPE_HDMI: +- case HDA_PCM_TYPE_MODEM: +- if (num_devs[type]) { +- snd_printk(KERN_WARNING +- "%s already defined\n", +- dev_name[type]); +- continue; +- } +- cpcm->device = dev_idx[type]; +- break; +- default: +- snd_printk(KERN_WARNING +- "Invalid PCM type %d\n", type); +- continue; +- } +- num_devs[type]++; +- err = create_codec_pcm(chip, codec, cpcm); +- if (err < 0) +- return err; +- } +- } + return 0; + } + +@@ -1905,13 +1930,13 @@ static void azx_stop_chip(struct azx *chip) + + #ifdef CONFIG_SND_HDA_POWER_SAVE + /* power-up/down the controller */ +-static void azx_power_notify(struct hda_codec *codec) ++static void azx_power_notify(struct hda_bus *bus) + { +- struct azx *chip = codec->bus->private_data; ++ struct azx *chip = bus->private_data; + struct hda_codec *c; + int power_on = 0; + +- list_for_each_entry(c, &codec->bus->codec_list, list) { ++ list_for_each_entry(c, &bus->codec_list, list) { + if (c->power_on) { + power_on = 1; + break; +@@ -1928,6 +1953,18 @@ static void azx_power_notify(struct hda_codec *codec) + /* + * power management + */ ++ ++static int snd_hda_codecs_inuse(struct hda_bus *bus) ++{ ++ struct hda_codec *codec; ++ ++ list_for_each_entry(codec, &bus->codec_list, list) { ++ if (snd_hda_codec_needs_resume(codec)) ++ return 1; ++ } ++ return 0; ++} ++ + static int azx_suspend(struct pci_dev *pci, pm_message_t state) + { + struct snd_card *card = pci_get_drvdata(pci); +@@ -2065,26 +2102,31 @@ static int __devinit check_position_fix(struct azx *chip, int fix) + { + const struct snd_pci_quirk *q; + +- /* Check VIA HD Audio Controller exist */ +- if (chip->pci->vendor == PCI_VENDOR_ID_VIA && +- chip->pci->device == VIA_HDAC_DEVICE_ID) { ++ switch (fix) { ++ case POS_FIX_LPIB: ++ case POS_FIX_POSBUF: ++ return fix; ++ } ++ ++ /* Check VIA/ATI HD Audio Controller exist */ ++ switch (chip->driver_type) { ++ case AZX_DRIVER_VIA: ++ case AZX_DRIVER_ATI: + chip->via_dmapos_patch = 1; + /* Use link position directly, avoid any transfer problem. */ + return POS_FIX_LPIB; + } + chip->via_dmapos_patch = 0; + +- if (fix == POS_FIX_AUTO) { +- q = snd_pci_quirk_lookup(chip->pci, position_fix_list); +- if (q) { +- printk(KERN_INFO +- "hda_intel: position_fix set to %d " +- "for device %04x:%04x\n", +- q->value, q->subvendor, q->subdevice); +- return q->value; +- } ++ q = snd_pci_quirk_lookup(chip->pci, position_fix_list); ++ if (q) { ++ printk(KERN_INFO ++ "hda_intel: position_fix set to %d " ++ "for device %04x:%04x\n", ++ q->value, q->subvendor, q->subdevice); ++ return q->value; + } +- return fix; ++ return POS_FIX_AUTO; + } + + /* +@@ -2099,23 +2141,39 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = { + SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X/T/R61", 0x01), + /* broken BIOS */ + SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01), ++ /* including bogus ALC268 in slot#2 that conflicts with ALC888 */ ++ SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01), ++ /* forced codec slots */ ++ SND_PCI_QUIRK(0x1043, 0x1262, "ASUS W5Fm", 0x103), ++ SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103), + {} + }; + ++#define AZX_FORCE_CODEC_MASK 0x100 ++ + static void __devinit check_probe_mask(struct azx *chip, int dev) + { + const struct snd_pci_quirk *q; + +- if (probe_mask[dev] == -1) { ++ chip->codec_probe_mask = probe_mask[dev]; ++ if (chip->codec_probe_mask == -1) { + q = snd_pci_quirk_lookup(chip->pci, probe_mask_list); + if (q) { + printk(KERN_INFO + "hda_intel: probe_mask set to 0x%x " + "for device %04x:%04x\n", + q->value, q->subvendor, q->subdevice); +- probe_mask[dev] = q->value; ++ chip->codec_probe_mask = q->value; + } + } ++ ++ /* check forced option */ ++ if (chip->codec_probe_mask != -1 && ++ (chip->codec_probe_mask & AZX_FORCE_CODEC_MASK)) { ++ chip->codec_mask = chip->codec_probe_mask & 0xff; ++ printk(KERN_INFO "hda_intel: codec_mask forced to 0x%x\n", ++ chip->codec_mask); ++ } + } + + +@@ -2212,9 +2270,17 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, + gcap = azx_readw(chip, GCAP); + snd_printdd("chipset global capabilities = 0x%x\n", gcap); + ++ /* ATI chips seems buggy about 64bit DMA addresses */ ++ if (chip->driver_type == AZX_DRIVER_ATI) ++ gcap &= ~0x01; ++ + /* allow 64bit DMA address if supported by H/W */ +- if ((gcap & 0x01) && !pci_set_dma_mask(pci, DMA_64BIT_MASK)) +- pci_set_consistent_dma_mask(pci, DMA_64BIT_MASK); ++ if ((gcap & 0x01) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64))) ++ pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64)); ++ else { ++ pci_set_dma_mask(pci, DMA_BIT_MASK(32)); ++ pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)); ++ } + + /* read number of streams from GCAP register instead of using + * hardcoded value +@@ -2233,6 +2299,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, + chip->playback_streams = ATIHDMI_NUM_PLAYBACK; + chip->capture_streams = ATIHDMI_NUM_CAPTURE; + break; ++ case AZX_DRIVER_GENERIC: + default: + chip->playback_streams = ICH6_NUM_PLAYBACK; + chip->capture_streams = ICH6_NUM_CAPTURE; +@@ -2342,40 +2409,30 @@ static int __devinit azx_probe(struct pci_dev *pci, + } + + err = azx_create(card, pci, dev, pci_id->driver_data, &chip); +- if (err < 0) { +- snd_card_free(card); +- return err; +- } ++ if (err < 0) ++ goto out_free; + card->private_data = chip; + + /* create codec instances */ +- err = azx_codec_create(chip, model[dev], probe_mask[dev]); +- if (err < 0) { +- snd_card_free(card); +- return err; +- } ++ err = azx_codec_create(chip, model[dev], probe_only[dev]); ++ if (err < 0) ++ goto out_free; + + /* create PCM streams */ +- err = azx_pcm_create(chip); +- if (err < 0) { +- snd_card_free(card); +- return err; +- } ++ err = snd_hda_build_pcms(chip->bus); ++ if (err < 0) ++ goto out_free; + + /* create mixer controls */ + err = azx_mixer_create(chip); +- if (err < 0) { +- snd_card_free(card); +- return err; +- } ++ if (err < 0) ++ goto out_free; + + snd_card_set_dev(card, &pci->dev); + + err = snd_card_register(card); +- if (err < 0) { +- snd_card_free(card); +- return err; +- } ++ if (err < 0) ++ goto out_free; + + pci_set_drvdata(pci, card); + chip->running = 1; +@@ -2384,6 +2441,9 @@ static int __devinit azx_probe(struct pci_dev *pci, + + dev++; + return err; ++out_free: ++ snd_card_free(card); ++ return err; + } + + static void __devexit azx_remove(struct pci_dev *pci) +@@ -2451,12 +2511,17 @@ static struct pci_device_id azx_ids[] = { + { PCI_DEVICE(0x10de, 0x0ac1), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0ac2), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA }, +- { PCI_DEVICE(0x10de, 0x0bd4), .driver_data = AZX_DRIVER_NVIDIA }, +- { PCI_DEVICE(0x10de, 0x0bd5), .driver_data = AZX_DRIVER_NVIDIA }, +- { PCI_DEVICE(0x10de, 0x0bd6), .driver_data = AZX_DRIVER_NVIDIA }, +- { PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA }, ++ { PCI_DEVICE(0x10de, 0x0d94), .driver_data = AZX_DRIVER_NVIDIA }, ++ { PCI_DEVICE(0x10de, 0x0d95), .driver_data = AZX_DRIVER_NVIDIA }, ++ { PCI_DEVICE(0x10de, 0x0d96), .driver_data = AZX_DRIVER_NVIDIA }, ++ { PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA }, + /* Teradici */ + { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA }, ++ /* AMD Generic, PCI class code and Vendor ID for HD Audio */ ++ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID), ++ .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, ++ .class_mask = 0xffffff, ++ .driver_data = AZX_DRIVER_GENERIC }, + { 0, } + }; + MODULE_DEVICE_TABLE(pci, azx_ids); +diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h +index 3018b76..8334901 100644 +--- a/sound/pci/hda/hda_local.h ++++ b/sound/pci/hda/hda_local.h +@@ -98,6 +98,8 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, + const char *name); + int snd_hda_add_vmaster(struct hda_codec *codec, char *name, + unsigned int *tlv, const char **slaves); ++int snd_hda_codec_reset(struct hda_codec *codec); ++int snd_hda_codec_configure(struct hda_codec *codec); + + /* amp value bits */ + #define HDA_AMP_MUTE 0x80 +@@ -134,7 +136,7 @@ extern struct hda_ctl_ops snd_hda_bind_sw; /* for bind-switch */ + + struct hda_bind_ctls { + struct hda_ctl_ops *ops; +- long values[]; ++ unsigned long values[]; + }; + + int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, +@@ -227,6 +229,7 @@ struct hda_multi_out { + hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */ + hda_nid_t extra_out_nid[3]; /* optional DACs, 0 when not exists */ + hda_nid_t dig_out_nid; /* digital out audio widget */ ++ hda_nid_t *slave_dig_outs; + int max_channels; /* currently supported analog channels */ + int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */ + int no_share_stream; /* don't share a stream with multiple pins */ +@@ -251,6 +254,8 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream); ++int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, ++ struct hda_multi_out *mout); + int snd_hda_multi_out_analog_open(struct hda_codec *codec, + struct hda_multi_out *mout, + struct snd_pcm_substream *substream, +@@ -284,6 +289,12 @@ int snd_hda_codec_proc_new(struct hda_codec *codec); + static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; } + #endif + ++#define SND_PRINT_RATES_ADVISED_BUFSIZE 80 ++void snd_print_pcm_rates(int pcm, char *buf, int buflen); ++ ++#define SND_PRINT_BITS_ADVISED_BUFSIZE 16 ++void snd_print_pcm_bits(int pcm, char *buf, int buflen); ++ + /* + * Misc + */ +@@ -346,9 +357,12 @@ struct auto_pin_cfg { + int line_out_type; /* AUTO_PIN_XXX_OUT */ + hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS]; + hda_nid_t input_pins[AUTO_PIN_LAST]; +- hda_nid_t dig_out_pin; ++ int dig_outs; ++ hda_nid_t dig_out_pins[2]; + hda_nid_t dig_in_pin; + hda_nid_t mono_out_pin; ++ int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */ ++ int dig_in_type; /* HDA_PCM_TYPE_XXX */ + }; + + #define get_defcfg_connect(cfg) \ +@@ -369,17 +383,17 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, + /* amp values */ + #define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8)) + #define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8)) +-#define AMP_OUT_MUTE 0xb080 +-#define AMP_OUT_UNMUTE 0xb000 +-#define AMP_OUT_ZERO 0xb000 ++#define AMP_OUT_MUTE 0xb080 ++#define AMP_OUT_UNMUTE 0xb000 ++#define AMP_OUT_ZERO 0xb000 + /* pinctl values */ + #define PIN_IN (AC_PINCTL_IN_EN) +-#define PIN_VREFHIZ (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ) ++#define PIN_VREFHIZ (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ) + #define PIN_VREF50 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_50) +-#define PIN_VREFGRD (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD) ++#define PIN_VREFGRD (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD) + #define PIN_VREF80 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_80) +-#define PIN_VREF100 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100) +-#define PIN_OUT (AC_PINCTL_OUT_EN) ++#define PIN_VREF100 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100) ++#define PIN_OUT (AC_PINCTL_OUT_EN) + #define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN) + #define PIN_HP_AMP (AC_PINCTL_HP_EN) + +@@ -397,11 +411,45 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid) + u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction); + int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int caps); ++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); ++void snd_hda_ctls_clear(struct hda_codec *codec); + + /* + * hwdep interface + */ ++#ifdef CONFIG_SND_HDA_HWDEP + int snd_hda_create_hwdep(struct hda_codec *codec); ++#else ++static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; } ++#endif ++ ++#ifdef CONFIG_SND_HDA_RECONFIG ++int snd_hda_hwdep_add_sysfs(struct hda_codec *codec); ++#else ++static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec) ++{ ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_SND_HDA_RECONFIG ++const char *snd_hda_get_hint(struct hda_codec *codec, const char *key); ++int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key); ++#else ++static inline ++const char *snd_hda_get_hint(struct hda_codec *codec, const char *key) ++{ ++ return NULL; ++} ++ ++static inline ++int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) ++{ ++ return -ENOENT; ++} ++#endif + + /* + * power-management +@@ -436,4 +484,66 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, + #define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) + #define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f) + ++/* ++ * CEA Short Audio Descriptor data ++ */ ++struct cea_sad { ++ int channels; ++ int format; /* (format == 0) indicates invalid SAD */ ++ int rates; ++ int sample_bits; /* for LPCM */ ++ int max_bitrate; /* for AC3...ATRAC */ ++ int profile; /* for WMAPRO */ ++}; ++ ++#define ELD_FIXED_BYTES 20 ++#define ELD_MAX_MNL 16 ++#define ELD_MAX_SAD 16 ++ ++/* ++ * ELD: EDID Like Data ++ */ ++struct hdmi_eld { ++ int eld_size; ++ int baseline_len; ++ int eld_ver; /* (eld_ver == 0) indicates invalid ELD */ ++ int cea_edid_ver; ++ char monitor_name[ELD_MAX_MNL + 1]; ++ int manufacture_id; ++ int product_id; ++ u64 port_id; ++ int support_hdcp; ++ int support_ai; ++ int conn_type; ++ int aud_synch_delay; ++ int spk_alloc; ++ int sad_count; ++ struct cea_sad sad[ELD_MAX_SAD]; ++#ifdef CONFIG_PROC_FS ++ struct snd_info_entry *proc_entry; ++#endif ++}; ++ ++int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid); ++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); ++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) ++{ ++ return 0; ++} ++static inline void snd_hda_eld_proc_free(struct hda_codec *codec, ++ struct hdmi_eld *eld) ++{ ++} ++#endif ++ ++#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 ++void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); ++ + #endif /* __SOUND_HDA_LOCAL_H */ +diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h +deleted file mode 100644 +index dfbcfa8..0000000 +--- a/sound/pci/hda/hda_patch.h ++++ /dev/null +@@ -1,22 +0,0 @@ +-/* +- * HDA Patches - included by hda_codec.c +- */ +- +-/* Realtek codecs */ +-extern struct hda_codec_preset snd_hda_preset_realtek[]; +-/* C-Media codecs */ +-extern struct hda_codec_preset snd_hda_preset_cmedia[]; +-/* Analog Devices codecs */ +-extern struct hda_codec_preset snd_hda_preset_analog[]; +-/* SigmaTel codecs */ +-extern struct hda_codec_preset snd_hda_preset_sigmatel[]; +-/* SiLabs 3054/3055 modem codecs */ +-extern struct hda_codec_preset snd_hda_preset_si3054[]; +-/* ATI HDMI codecs */ +-extern struct hda_codec_preset snd_hda_preset_atihdmi[]; +-/* Conexant audio codec */ +-extern struct hda_codec_preset snd_hda_preset_conexant[]; +-/* VIA codecs */ +-extern struct hda_codec_preset snd_hda_preset_via[]; +-/* NVIDIA HDMI codecs */ +-extern struct hda_codec_preset snd_hda_preset_nvhdmi[]; +diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c +index 0890528..93d7499 100644 +--- a/sound/pci/hda/hda_proc.c ++++ b/sound/pci/hda/hda_proc.c +@@ -91,31 +91,21 @@ static void print_amp_vals(struct snd_info_buffer *buffer, + + static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm) + { +- static unsigned int rates[] = { +- 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, +- 96000, 176400, 192000, 384000 +- }; +- int i; ++ char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; + + pcm &= AC_SUPPCM_RATES; + snd_iprintf(buffer, " rates [0x%x]:", pcm); +- for (i = 0; i < ARRAY_SIZE(rates); i++) +- if (pcm & (1 << i)) +- snd_iprintf(buffer, " %d", rates[i]); +- snd_iprintf(buffer, "\n"); ++ snd_print_pcm_rates(pcm, buf, sizeof(buf)); ++ snd_iprintf(buffer, "%s\n", buf); + } + + static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm) + { +- static unsigned int bits[] = { 8, 16, 20, 24, 32 }; +- int i; ++ char buf[SND_PRINT_BITS_ADVISED_BUFSIZE]; + +- pcm = (pcm >> 16) & 0xff; +- snd_iprintf(buffer, " bits [0x%x]:", pcm); +- for (i = 0; i < ARRAY_SIZE(bits); i++) +- if (pcm & (1 << i)) +- snd_iprintf(buffer, " %d", bits[i]); +- snd_iprintf(buffer, "\n"); ++ snd_iprintf(buffer, " bits [0x%x]:", (pcm >> 16) & 0xff); ++ snd_print_pcm_bits(pcm, buf, sizeof(buf)); ++ snd_iprintf(buffer, "%s\n", buf); + } + + static void print_pcm_formats(struct snd_info_buffer *buffer, +@@ -145,32 +135,6 @@ static void print_pcm_caps(struct snd_info_buffer *buffer, + print_pcm_formats(buffer, stream); + } + +-static const char *get_jack_location(u32 cfg) +-{ +- static char *bases[7] = { +- "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom", +- }; +- static unsigned char specials_idx[] = { +- 0x07, 0x08, +- 0x17, 0x18, 0x19, +- 0x37, 0x38 +- }; +- static char *specials[] = { +- "Rear Panel", "Drive Bar", +- "Riser", "HDMI", "ATAPI", +- "Mobile-In", "Mobile-Out" +- }; +- int i; +- cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT; +- if ((cfg & 0x0f) < 7) +- return bases[cfg & 0x0f]; +- for (i = 0; i < ARRAY_SIZE(specials_idx); i++) { +- if (cfg == specials_idx[i]) +- return specials[i]; +- } +- return "UNKNOWN"; +-} +- + static const char *get_jack_connection(u32 cfg) + { + static char *names[16] = { +@@ -206,13 +170,6 @@ static void print_pin_caps(struct snd_info_buffer *buffer, + int *supports_vref) + { + static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" }; +- static char *jack_types[16] = { +- "Line Out", "Speaker", "HP Out", "CD", +- "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand", +- "Line In", "Aux", "Mic", "Telephony", +- "SPDIF In", "Digitial In", "Reserved", "Other" +- }; +- static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; + unsigned int caps, val; + + caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); +@@ -236,8 +193,6 @@ static void print_pin_caps(struct snd_info_buffer *buffer, + else + snd_iprintf(buffer, " HDMI"); + } +- if (caps & AC_PINCAP_LR_SWAP) +- snd_iprintf(buffer, " R/L"); + if (caps & AC_PINCAP_TRIG_REQ) + snd_iprintf(buffer, " Trigger"); + if (caps & AC_PINCAP_IMP_SENSE) +@@ -276,9 +231,9 @@ static void print_pin_caps(struct snd_info_buffer *buffer, + caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); + snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps, + jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT], +- jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT], +- jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3], +- get_jack_location(caps)); ++ snd_hda_get_jack_type(caps), ++ snd_hda_get_jack_connectivity(caps), ++ snd_hda_get_jack_location(caps)); + snd_iprintf(buffer, " Conn = %s, Color = %s\n", + get_jack_connection(caps), + get_jack_color(caps)); +@@ -444,7 +399,10 @@ static void print_conn_list(struct snd_info_buffer *buffer, + { + int c, curr = -1; + +- if (conn_len > 1 && wid_type != AC_WID_AUD_MIX) ++ if (conn_len > 1 && ++ wid_type != AC_WID_AUD_MIX && ++ wid_type != AC_WID_VOL_KNB && ++ wid_type != AC_WID_POWER) + curr = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONNECT_SEL, 0); + snd_iprintf(buffer, " Connection: %d\n", conn_len); +@@ -459,17 +417,6 @@ static void print_conn_list(struct snd_info_buffer *buffer, + } + } + +-static void print_realtek_coef(struct snd_info_buffer *buffer, +- struct hda_codec *codec, hda_nid_t nid) +-{ +- int coeff = snd_hda_codec_read(codec, nid, 0, +- AC_VERB_GET_PROC_COEF, 0); +- snd_iprintf(buffer, " Processing Coefficient: 0x%02x\n", coeff); +- coeff = snd_hda_codec_read(codec, nid, 0, +- AC_VERB_GET_COEF_INDEX, 0); +- snd_iprintf(buffer, " Coefficient Index: 0x%02x\n", coeff); +-} +- + static void print_gpio(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) + { +@@ -502,12 +449,13 @@ static void print_gpio(struct snd_info_buffer *buffer, + for (i = 0; i < max; ++i) + snd_iprintf(buffer, + " IO[%d]: enable=%d, dir=%d, wake=%d, " +- "sticky=%d, data=%d\n", i, ++ "sticky=%d, data=%d, unsol=%d\n", i, + (enable & (1<private_data; +- char buf[32]; + hda_nid_t nid; + int i, nodes; + +- snd_hda_get_codec_name(codec, buf, sizeof(buf)); +- snd_iprintf(buffer, "Codec: %s\n", buf); ++ snd_iprintf(buffer, "Codec: %s\n", ++ codec->name ? codec->name : "Not Set"); + snd_iprintf(buffer, "Address: %d\n", codec->addr); +- snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id); +- snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id); ++ snd_iprintf(buffer, "Function Id: 0x%x\n", codec->function_id); ++ snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id); ++ snd_iprintf(buffer, "Subsystem Id: 0x%08x\n", codec->subsystem_id); + snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id); + + if (codec->mfg) +@@ -549,6 +497,8 @@ static void print_codec_info(struct snd_info_entry *entry, + } + + print_gpio(buffer, codec, codec->afg); ++ if (codec->proc_widget_hook) ++ codec->proc_widget_hook(buffer, codec, codec->afg); + + for (i = 0; i < nodes; i++, nid++) { + unsigned int wid_caps = +@@ -607,8 +557,14 @@ static void print_codec_info(struct snd_info_entry *entry, + snd_iprintf(buffer, " Amp-Out caps: "); + print_amp_caps(buffer, codec, nid, HDA_OUTPUT); + snd_iprintf(buffer, " Amp-Out vals: "); +- print_amp_vals(buffer, codec, nid, HDA_OUTPUT, +- wid_caps & AC_WCAP_STEREO, 1); ++ if (wid_type == AC_WID_PIN && ++ codec->pin_amp_workaround) ++ print_amp_vals(buffer, codec, nid, HDA_OUTPUT, ++ wid_caps & AC_WCAP_STEREO, ++ conn_len); ++ else ++ print_amp_vals(buffer, codec, nid, HDA_OUTPUT, ++ wid_caps & AC_WCAP_STEREO, 1); + } + + switch (wid_type) { +@@ -651,9 +607,8 @@ static void print_codec_info(struct snd_info_entry *entry, + if (wid_caps & AC_WCAP_PROC_WID) + print_proc_caps(buffer, codec, nid); + +- /* NID 0x20 == Realtek Define Registers */ +- if (codec->vendor_id == 0x10ec && nid == 0x20) +- print_realtek_coef(buffer, codec, nid); ++ if (codec->proc_widget_hook) ++ codec->proc_widget_hook(buffer, codec, nid); + } + snd_hda_power_down(codec); + } +diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c +index 10fe100..84cc49c 100644 +--- a/sound/pci/hda/patch_analog.c ++++ b/sound/pci/hda/patch_analog.c +@@ -27,12 +27,12 @@ + #include + #include "hda_codec.h" + #include "hda_local.h" +-#include "hda_patch.h" ++#include "hda_beep.h" + + struct ad198x_spec { + struct snd_kcontrol_new *mixers[5]; + int num_mixers; +- ++ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ + const struct hda_verb *init_verbs[5]; /* initialization verbs + * don't forget NULL termination! + */ +@@ -67,8 +67,7 @@ struct ad198x_spec { + + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; +- unsigned int num_kctl_alloc, num_kctl_used; +- struct snd_kcontrol_new *kctl_alloc; ++ struct snd_array kctls; + struct hda_input_mux private_imux; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + +@@ -154,6 +153,18 @@ static const char *ad_slave_sws[] = { + NULL + }; + ++static void ad198x_free_kctls(struct hda_codec *codec); ++ ++/* 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), ++ { } /* end */ ++}; ++ ++#define set_beep_amp(spec, nid, idx, dir) \ ++ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */ ++ + static int ad198x_build_controls(struct hda_codec *codec) + { + struct ad198x_spec *spec = codec->spec; +@@ -181,6 +192,21 @@ static int ad198x_build_controls(struct hda_codec *codec) + return err; + } + ++ /* create beep controls if needed */ ++ if (spec->beep_amp) { ++ struct snd_kcontrol_new *knew; ++ for (knew = ad_beep_mixer; knew->name; knew++) { ++ struct snd_kcontrol *kctl; ++ kctl = snd_ctl_new1(knew, codec); ++ if (!kctl) ++ return -ENOMEM; ++ kctl->private_value = spec->beep_amp; ++ err = snd_hda_ctl_add(codec, kctl); ++ if (err < 0) ++ return err; ++ } ++ } ++ + /* if we have no master control, let's create it */ + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + unsigned int vmaster_tlv[4]; +@@ -202,6 +228,7 @@ static int ad198x_build_controls(struct hda_codec *codec) + return err; + } + ++ ad198x_free_kctls(codec); /* no longer needed */ + return 0; + } + +@@ -274,6 +301,14 @@ static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + format, substream); + } + ++static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, ++ struct hda_codec *codec, ++ struct snd_pcm_substream *substream) ++{ ++ struct ad198x_spec *spec = codec->spec; ++ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); ++} ++ + /* + * Analog capture + */ +@@ -332,7 +367,8 @@ static struct hda_pcm_stream ad198x_pcm_digital_playback = { + .ops = { + .open = ad198x_dig_playback_pcm_open, + .close = ad198x_dig_playback_pcm_close, +- .prepare = ad198x_dig_playback_pcm_prepare ++ .prepare = ad198x_dig_playback_pcm_prepare, ++ .cleanup = ad198x_dig_playback_pcm_cleanup + }, + }; + +@@ -375,17 +411,29 @@ static int ad198x_build_pcms(struct hda_codec *codec) + return 0; + } + +-static void ad198x_free(struct hda_codec *codec) ++static void ad198x_free_kctls(struct hda_codec *codec) + { + struct ad198x_spec *spec = codec->spec; +- unsigned int i; + +- if (spec->kctl_alloc) { +- for (i = 0; i < spec->num_kctl_used; i++) +- kfree(spec->kctl_alloc[i].name); +- kfree(spec->kctl_alloc); ++ if (spec->kctls.list) { ++ struct snd_kcontrol_new *kctl = spec->kctls.list; ++ int i; ++ for (i = 0; i < spec->kctls.used; i++) ++ kfree(kctl[i].name); + } +- kfree(codec->spec); ++ snd_array_free(&spec->kctls); ++} ++ ++static void ad198x_free(struct hda_codec *codec) ++{ ++ struct ad198x_spec *spec = codec->spec; ++ ++ if (!spec) ++ return; ++ ++ ad198x_free_kctls(codec); ++ kfree(spec); ++ snd_hda_detach_beep_device(codec); + } + + static struct hda_codec_ops ad198x_patch_ops = { +@@ -524,8 +572,6 @@ static struct snd_kcontrol_new ad1986a_mixers[] = { + HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), +@@ -589,8 +635,7 @@ static struct snd_kcontrol_new ad1986a_laptop_mixers[] = { + HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), +- /* HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT), ++ /* + HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */ + HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), +@@ -788,8 +833,6 @@ static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = { + HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x18, 0x0, HDA_OUTPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x18, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), + { +@@ -981,10 +1024,8 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = { + SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), + SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), +- SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_SAMSUNG), +- SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_SAMSUNG), +- SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_SAMSUNG), + SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), ++ SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG), + SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), + SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), + SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), +@@ -1006,15 +1047,14 @@ static struct hda_amp_list ad1986a_loopbacks[] = { + + static int is_jack_available(struct hda_codec *codec, hda_nid_t nid) + { +- unsigned int conf = snd_hda_codec_read(codec, nid, 0, +- AC_VERB_GET_CONFIG_DEFAULT, 0); ++ unsigned int conf = snd_hda_codec_get_pincfg(codec, nid); + return get_defcfg_connect(conf) != AC_JACK_PORT_NONE; + } + + static int patch_ad1986a(struct hda_codec *codec) + { + struct ad198x_spec *spec; +- int board_config; ++ int err, board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) +@@ -1022,6 +1062,13 @@ static int patch_ad1986a(struct hda_codec *codec) + + codec->spec = spec; + ++ err = snd_hda_attach_beep_device(codec, 0x19); ++ if (err < 0) { ++ ad198x_free(codec); ++ return err; ++ } ++ set_beep_amp(spec, 0x18, 0, HDA_OUTPUT); ++ + spec->multiout.max_channels = 6; + spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids); + spec->multiout.dac_nids = ad1986a_dac_nids; +@@ -1201,8 +1248,6 @@ static struct snd_kcontrol_new ad1983_mixers[] = { + HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT), +- HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT), +- HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), +@@ -1273,6 +1318,7 @@ static struct hda_amp_list ad1983_loopbacks[] = { + static int patch_ad1983(struct hda_codec *codec) + { + struct ad198x_spec *spec; ++ int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) +@@ -1280,6 +1326,13 @@ static int patch_ad1983(struct hda_codec *codec) + + codec->spec = spec; + ++ err = snd_hda_attach_beep_device(codec, 0x10); ++ if (err < 0) { ++ ad198x_free(codec); ++ return err; ++ } ++ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); ++ + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids); + spec->multiout.dac_nids = ad1983_dac_nids; +@@ -1349,8 +1402,6 @@ static struct snd_kcontrol_new ad1981_mixers[] = { + HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT), +- HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), +- HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), +@@ -1395,8 +1446,8 @@ static struct hda_verb ad1981_init_verbs[] = { + {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, + /* Mic boost: 0dB */ +- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, +- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, ++ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, ++ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Record selector: Front mic */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, +@@ -1661,10 +1712,10 @@ static struct snd_pci_quirk ad1981_cfg_tbl[] = { + SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD), + SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD), + /* All HP models */ +- SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP), ++ SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP), + SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA), + /* Lenovo Thinkpad T60/X60/Z6xx */ +- SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD), ++ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD), + /* HP nx6320 (reversed SSID, H/W bug) */ + SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP), + {} +@@ -1673,7 +1724,7 @@ static struct snd_pci_quirk ad1981_cfg_tbl[] = { + static int patch_ad1981(struct hda_codec *codec) + { + struct ad198x_spec *spec; +- int board_config; ++ int err, board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) +@@ -1681,6 +1732,13 @@ static int patch_ad1981(struct hda_codec *codec) + + codec->spec = spec; + ++ err = snd_hda_attach_beep_device(codec, 0x10); ++ if (err < 0) { ++ ad198x_free(codec); ++ return err; ++ } ++ set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); ++ + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids); + spec->multiout.dac_nids = ad1981_dac_nids; +@@ -1873,8 +1931,8 @@ static hda_nid_t ad1988_capsrc_nids[3] = { + #define AD1988_SPDIF_OUT_HDMI 0x0b + #define AD1988_SPDIF_IN 0x07 + +-static hda_nid_t ad1989b_slave_dig_outs[2] = { +- AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI ++static hda_nid_t ad1989b_slave_dig_outs[] = { ++ AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0 + }; + + static struct hda_input_mux ad1988_6stack_capture_source = { +@@ -1967,9 +2025,6 @@ static struct snd_kcontrol_new ad1988_6stack_mixers2[] = { + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT), + +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT), +- + HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), + +@@ -2013,9 +2068,6 @@ static struct snd_kcontrol_new ad1988_3stack_mixers2[] = { + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT), + +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT), +- + HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), + +@@ -2045,9 +2097,6 @@ static struct snd_kcontrol_new ad1988_laptop_mixers[] = { + HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT), + +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT), +- + HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), + +@@ -2276,10 +2325,6 @@ static struct hda_verb ad1988_capture_init_verbs[] = { + {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, +- /* ADCs; muted */ +- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + { } + }; +@@ -2387,10 +2432,6 @@ static struct hda_verb ad1988_3stack_init_verbs[] = { + {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, +- /* ADCs; muted */ +- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Analog Mix output amp */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ + { } +@@ -2462,10 +2503,6 @@ static struct hda_verb ad1988_laptop_init_verbs[] = { + {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, +- /* ADCs; muted */ +- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Analog Mix output amp */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ + { } +@@ -2495,9 +2532,6 @@ static struct hda_amp_list ad1988_loopbacks[] = { + * Automatic parse of I/O pins from the BIOS configuration + */ + +-#define NUM_CONTROL_ALLOC 32 +-#define NUM_VERB_ALLOC 32 +- + enum { + AD_CTL_WIDGET_VOL, + AD_CTL_WIDGET_MUTE, +@@ -2515,27 +2549,15 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name, + { + struct snd_kcontrol_new *knew; + +- if (spec->num_kctl_used >= spec->num_kctl_alloc) { +- int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; +- +- knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */ +- if (! knew) +- return -ENOMEM; +- if (spec->kctl_alloc) { +- memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc); +- kfree(spec->kctl_alloc); +- } +- spec->kctl_alloc = knew; +- spec->num_kctl_alloc = num; +- } +- +- knew = &spec->kctl_alloc[spec->num_kctl_used]; ++ snd_array_init(&spec->kctls, sizeof(*knew), 32); ++ knew = snd_array_new(&spec->kctls); ++ if (!knew) ++ return -ENOMEM; + *knew = ad1988_control_templates[type]; + knew->name = kstrdup(name, GFP_KERNEL); + if (! knew->name) + return -ENOMEM; + knew->private_value = val; +- spec->num_kctl_used++; + return 0; + } + +@@ -2884,13 +2906,13 @@ static int ad1988_parse_auto_config(struct hda_codec *codec) + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + +- if (spec->autocfg.dig_out_pin) ++ if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = AD1988_SPDIF_IN; + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; ++ if (spec->kctls.list) ++ spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs; + +@@ -2934,7 +2956,7 @@ static struct snd_pci_quirk ad1988_cfg_tbl[] = { + static int patch_ad1988(struct hda_codec *codec) + { + struct ad198x_spec *spec; +- int board_config; ++ int err, board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) +@@ -2954,7 +2976,7 @@ static int patch_ad1988(struct hda_codec *codec) + + if (board_config == AD1988_AUTO) { + /* automatic parse from the BIOS config */ +- int err = ad1988_parse_auto_config(codec); ++ err = ad1988_parse_auto_config(codec); + if (err < 0) { + ad198x_free(codec); + return err; +@@ -2964,6 +2986,13 @@ static int patch_ad1988(struct hda_codec *codec) + } + } + ++ err = snd_hda_attach_beep_device(codec, 0x10); ++ if (err < 0) { ++ ad198x_free(codec); ++ return err; ++ } ++ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); ++ + switch (board_config) { + case AD1988_6STACK: + case AD1988_6STACK_DIG: +@@ -3120,12 +3149,6 @@ static struct snd_kcontrol_new ad1884_base_mixers[] = { + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT), +- /* +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT), +- HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT), +- HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT), +- */ + HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), +@@ -3198,10 +3221,10 @@ static struct hda_verb ad1884_init_verbs[] = { + {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* Port-B (front mic) pin */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, +- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, ++ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Port-C (rear mic) pin */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, +- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, ++ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Analog mixer; mute as default */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, +@@ -3233,8 +3256,8 @@ static const char *ad1884_slave_vols[] = { + "Mic Playback Volume", + "CD Playback Volume", + "Internal Mic Playback Volume", +- "Docking Mic Playback Volume" +- "Beep Playback Volume", ++ "Docking Mic Playback Volume", ++ /* "Beep Playback Volume", */ + "IEC958 Playback Volume", + NULL + }; +@@ -3242,6 +3265,7 @@ static const char *ad1884_slave_vols[] = { + static int patch_ad1884(struct hda_codec *codec) + { + struct ad198x_spec *spec; ++ int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) +@@ -3249,6 +3273,13 @@ static int patch_ad1884(struct hda_codec *codec) + + codec->spec = spec; + ++ err = snd_hda_attach_beep_device(codec, 0x10); ++ if (err < 0) { ++ ad198x_free(codec); ++ return err; ++ } ++ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); ++ + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids); + spec->multiout.dac_nids = ad1884_dac_nids; +@@ -3315,8 +3346,6 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = { + HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT), +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), +@@ -3352,7 +3381,7 @@ static struct hda_verb ad1984_thinkpad_init_verbs[] = { + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* docking mic boost */ +- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, ++ {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* Analog mixer - docking mic; mute as default */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* enable EAPD bit */ +@@ -3373,10 +3402,6 @@ static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = { + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT), +- /* +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT), +- */ + HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), +@@ -3462,7 +3487,7 @@ static const char *ad1984_models[AD1984_MODELS] = { + + static struct snd_pci_quirk ad1984_cfg_tbl[] = { + /* Lenovo Thinkpad T61/X61 */ +- SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD), ++ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD), + SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP), + {} + }; +@@ -3555,8 +3580,6 @@ static struct snd_kcontrol_new ad1884a_base_mixers[] = { + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT), +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT), +@@ -3616,10 +3639,10 @@ static struct hda_verb ad1884a_init_verbs[] = { + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Port-B (front mic) pin */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, +- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, ++ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Port-C (rear line-in) pin */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, ++ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* Port-E (rear mic) pin */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +@@ -3689,8 +3712,6 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT), +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT), +@@ -3718,8 +3739,6 @@ static struct snd_kcontrol_new ad1884a_mobile_mixers[] = { + HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), +@@ -3798,6 +3817,49 @@ static struct hda_verb ad1884a_laptop_verbs[] = { + { } /* end */ + }; + ++static struct hda_verb ad1884a_mobile_verbs[] = { ++ /* DACs; unmute as default */ ++ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ ++ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ ++ /* Port-A (HP) mixer - route only from analog mixer */ ++ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, ++ /* Port-A pin */ ++ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, ++ /* Port-A (HP) pin - always unmuted */ ++ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ /* Port-B (mic jack) pin */ ++ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, ++ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ ++ /* Port-C (int mic) pin */ ++ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, ++ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ ++ /* Port-F (int speaker) mixer - route only from analog mixer */ ++ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, ++ /* Port-F pin */ ++ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, ++ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, ++ /* Analog mixer; mute as default */ ++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, ++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, ++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, ++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, ++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, ++ /* Analog Mix output amp */ ++ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, ++ /* capture sources */ ++ /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */ ++ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, ++ {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, ++ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, ++ /* unsolicited event for pin-sense */ ++ {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, ++ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, ++ { } /* end */ ++}; ++ + /* + * Thinkpad X300 + * 0x11 - HP +@@ -3830,8 +3892,6 @@ static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = { + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), +@@ -3905,16 +3965,9 @@ static struct snd_pci_quirk ad1884a_cfg_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE), + SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP), + SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE), +- SND_PCI_QUIRK(0x103c, 0x3072, "HP", AD1884A_MOBILE), +- SND_PCI_QUIRK(0x103c, 0x3073, "HP", AD1884A_MOBILE), +- SND_PCI_QUIRK(0x103c, 0x3076, "HP", AD1884A_MOBILE), +- SND_PCI_QUIRK(0x103c, 0x3077, "HP", AD1884A_MOBILE), +- SND_PCI_QUIRK(0x103c, 0x3079, "HP", AD1884A_MOBILE), +- SND_PCI_QUIRK(0x103c, 0x307a, "HP", AD1884A_MOBILE), +- SND_PCI_QUIRK(0x103c, 0x30e6, "HP 6730b", AD1884A_LAPTOP), +- SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP), +- SND_PCI_QUIRK(0x103c, 0x30db, "HP EliteBook 6930p", AD1884A_LAPTOP), +- SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP), ++ SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE), ++ SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP), ++ SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP), + SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD), + {} + }; +@@ -3922,7 +3975,7 @@ static struct snd_pci_quirk ad1884a_cfg_tbl[] = { + static int patch_ad1884a(struct hda_codec *codec) + { + struct ad198x_spec *spec; +- int board_config; ++ int err, board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) +@@ -3930,6 +3983,13 @@ static int patch_ad1884a(struct hda_codec *codec) + + codec->spec = spec; + ++ err = snd_hda_attach_beep_device(codec, 0x10); ++ if (err < 0) { ++ ad198x_free(codec); ++ return err; ++ } ++ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); ++ + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids); + spec->multiout.dac_nids = ad1884a_dac_nids; +@@ -3971,10 +4031,18 @@ static int patch_ad1884a(struct hda_codec *codec) + break; + case AD1884A_MOBILE: + spec->mixers[0] = ad1884a_mobile_mixers; +- spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; ++ spec->init_verbs[0] = ad1884a_mobile_verbs; + spec->multiout.dig_out_nid = 0; + codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; + codec->patch_ops.init = ad1884a_hp_init; ++ /* set the upper-limit for mixer amp to 0dB for avoiding the ++ * possible damage by overloading ++ */ ++ snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, ++ (0x17 << AC_AMPCAP_OFFSET_SHIFT) | ++ (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | ++ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | ++ (1 << AC_AMPCAP_MUTE_SHIFT)); + break; + case AD1884A_THINKPAD: + spec->mixers[0] = ad1984a_thinkpad_mixers; +@@ -4092,8 +4160,6 @@ static struct snd_kcontrol_new ad1882_loopback_mixers[] = { + HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT), +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT), + { } /* end */ + }; + +@@ -4106,8 +4172,6 @@ static struct snd_kcontrol_new ad1882a_loopback_mixers[] = { + HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT), +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT), + HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT), + { } /* end */ + }; +@@ -4266,7 +4330,7 @@ static const char *ad1882_models[AD1986A_MODELS] = { + static int patch_ad1882(struct hda_codec *codec) + { + struct ad198x_spec *spec; +- int board_config; ++ int err, board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) +@@ -4274,6 +4338,13 @@ static int patch_ad1882(struct hda_codec *codec) + + codec->spec = spec; + ++ err = snd_hda_attach_beep_device(codec, 0x10); ++ if (err < 0) { ++ ad198x_free(codec); ++ return err; ++ } ++ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); ++ + spec->multiout.max_channels = 6; + spec->multiout.num_dacs = 3; + spec->multiout.dac_nids = ad1882_dac_nids; +@@ -4327,7 +4398,7 @@ static int patch_ad1882(struct hda_codec *codec) + /* + * patch entries + */ +-struct hda_codec_preset snd_hda_preset_analog[] = { ++static struct hda_codec_preset snd_hda_preset_analog[] = { + { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a }, + { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 }, + { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a }, +@@ -4345,3 +4416,26 @@ struct hda_codec_preset snd_hda_preset_analog[] = { + { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 }, + {} /* terminator */ + }; ++ ++MODULE_ALIAS("snd-hda-codec-id:11d4*"); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Analog Devices HD-audio codec"); ++ ++static struct hda_codec_preset_list analog_list = { ++ .preset = snd_hda_preset_analog, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init patch_analog_init(void) ++{ ++ return snd_hda_add_codec_preset(&analog_list); ++} ++ ++static void __exit patch_analog_exit(void) ++{ ++ snd_hda_delete_codec_preset(&analog_list); ++} ++ ++module_init(patch_analog_init) ++module_exit(patch_analog_exit) +diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c +index ba61575..233e477 100644 +--- a/sound/pci/hda/patch_atihdmi.c ++++ b/sound/pci/hda/patch_atihdmi.c +@@ -27,7 +27,6 @@ + #include + #include "hda_codec.h" + #include "hda_local.h" +-#include "hda_patch.h" + + struct atihdmi_spec { + struct hda_multi_out multiout; +@@ -187,13 +186,40 @@ static int patch_atihdmi(struct hda_codec *codec) + /* + * patch entries + */ +-struct hda_codec_preset snd_hda_preset_atihdmi[] = { +- { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, +- { .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, +- { .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi }, +- { .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi }, ++static struct hda_codec_preset snd_hda_preset_atihdmi[] = { ++ { .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi }, ++ { .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi }, ++ { .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi }, ++ { .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi }, + { .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi }, +- { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi }, + { .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi }, + {} /* terminator */ + }; ++ ++MODULE_ALIAS("snd-hda-codec-id:1002793c"); ++MODULE_ALIAS("snd-hda-codec-id:10027919"); ++MODULE_ALIAS("snd-hda-codec-id:1002791a"); ++MODULE_ALIAS("snd-hda-codec-id:1002aa01"); ++MODULE_ALIAS("snd-hda-codec-id:10951390"); ++MODULE_ALIAS("snd-hda-codec-id:17e80047"); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("ATI HDMI HD-audio codec"); ++ ++static struct hda_codec_preset_list atihdmi_list = { ++ .preset = snd_hda_preset_atihdmi, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init patch_atihdmi_init(void) ++{ ++ return snd_hda_add_codec_preset(&atihdmi_list); ++} ++ ++static void __exit patch_atihdmi_exit(void) ++{ ++ snd_hda_delete_codec_preset(&atihdmi_list); ++} ++ ++module_init(patch_atihdmi_init) ++module_exit(patch_atihdmi_exit) +diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c +index 6ef57fb..c921264 100644 +--- a/sound/pci/hda/patch_cmedia.c ++++ b/sound/pci/hda/patch_cmedia.c +@@ -28,7 +28,6 @@ + #include + #include "hda_codec.h" + #include "hda_local.h" +-#include "hda_patch.h" + #define NUM_PINS 11 + + +@@ -681,13 +680,13 @@ static int patch_cmi9880(struct hda_codec *codec) + struct auto_pin_cfg cfg; + + /* collect pin default configuration */ +- port_e = snd_hda_codec_read(codec, 0x0f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); +- port_f = snd_hda_codec_read(codec, 0x10, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); ++ port_e = snd_hda_codec_get_pincfg(codec, 0x0f); ++ port_f = snd_hda_codec_get_pincfg(codec, 0x10); + spec->front_panel = 1; + if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE || + get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) { +- port_g = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); +- port_h = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); ++ port_g = snd_hda_codec_get_pincfg(codec, 0x1f); ++ port_h = snd_hda_codec_get_pincfg(codec, 0x20); + spec->channel_modes = cmi9880_channel_modes; + /* no front panel */ + if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE || +@@ -704,8 +703,8 @@ static int patch_cmi9880(struct hda_codec *codec) + spec->multiout.max_channels = cmi9880_channel_modes[0].channels; + } else { + spec->input_mux = &cmi9880_basic_mux; +- port_spdifi = snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); +- port_spdifo = snd_hda_codec_read(codec, 0x12, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); ++ port_spdifi = snd_hda_codec_get_pincfg(codec, 0x13); ++ port_spdifo = snd_hda_codec_get_pincfg(codec, 0x12); + if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE) + spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; + if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE) +@@ -736,8 +735,32 @@ static int patch_cmi9880(struct hda_codec *codec) + /* + * patch entries + */ +-struct hda_codec_preset snd_hda_preset_cmedia[] = { ++static struct hda_codec_preset snd_hda_preset_cmedia[] = { + { .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 }, + { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 }, + {} /* terminator */ + }; ++ ++MODULE_ALIAS("snd-hda-codec-id:13f69880"); ++MODULE_ALIAS("snd-hda-codec-id:434d4980"); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("C-Media HD-audio codec"); ++ ++static struct hda_codec_preset_list cmedia_list = { ++ .preset = snd_hda_preset_cmedia, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init patch_cmedia_init(void) ++{ ++ return snd_hda_add_codec_preset(&cmedia_list); ++} ++ ++static void __exit patch_cmedia_exit(void) ++{ ++ snd_hda_delete_codec_preset(&cmedia_list); ++} ++ ++module_init(patch_cmedia_init) ++module_exit(patch_cmedia_exit) +diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c +index a50089f..4fcbe21 100644 +--- a/sound/pci/hda/patch_conexant.c ++++ b/sound/pci/hda/patch_conexant.c +@@ -25,9 +25,10 @@ + #include + #include + #include ++#include ++ + #include "hda_codec.h" + #include "hda_local.h" +-#include "hda_patch.h" + + #define CXT_PIN_DIR_IN 0x00 + #define CXT_PIN_DIR_OUT 0x01 +@@ -38,12 +39,26 @@ + #define CONEXANT_HP_EVENT 0x37 + #define CONEXANT_MIC_EVENT 0x38 + ++/* Conexant 5051 specific */ + ++#define CXT5051_SPDIF_OUT 0x1C ++#define CXT5051_PORTB_EVENT 0x38 ++#define CXT5051_PORTC_EVENT 0x39 ++ ++ ++struct conexant_jack { ++ ++ hda_nid_t nid; ++ int type; ++ struct snd_jack *jack; ++ ++}; + + struct conexant_spec { + + struct snd_kcontrol_new *mixers[5]; + int num_mixers; ++ hda_nid_t vmaster_nid; + + const struct hda_verb *init_verbs[5]; /* initialization verbs + * don't forget NULL +@@ -58,6 +73,7 @@ struct conexant_spec { + */ + unsigned int cur_eapd; + unsigned int hp_present; ++ unsigned int no_auto_mic; + unsigned int need_dac_fix; + + /* capture */ +@@ -84,10 +100,11 @@ struct conexant_spec { + + unsigned int spdif_route; + ++ /* jack detection */ ++ struct snd_array jacks; ++ + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; +- unsigned int num_kctl_alloc, num_kctl_used; +- struct snd_kcontrol_new *kctl_alloc; + struct hda_input_mux private_imux; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + +@@ -332,6 +349,108 @@ static int conexant_mux_enum_put(struct snd_kcontrol *kcontrol, + &spec->cur_mux[adc_idx]); + } + ++#ifdef CONFIG_SND_JACK ++static void conexant_free_jack_priv(struct snd_jack *jack) ++{ ++ struct conexant_jack *jacks = jack->private_data; ++ jacks->nid = 0; ++ jacks->jack = NULL; ++} ++ ++static int conexant_add_jack(struct hda_codec *codec, ++ hda_nid_t nid, int type) ++{ ++ struct conexant_spec *spec; ++ struct conexant_jack *jack; ++ const char *name; ++ int err; ++ ++ spec = codec->spec; ++ snd_array_init(&spec->jacks, sizeof(*jack), 32); ++ jack = snd_array_new(&spec->jacks); ++ name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ; ++ ++ if (!jack) ++ return -ENOMEM; ++ ++ jack->nid = nid; ++ jack->type = type; ++ ++ err = snd_jack_new(codec->bus->card, name, type, &jack->jack); ++ if (err < 0) ++ return err; ++ jack->jack->private_data = jack; ++ jack->jack->private_free = conexant_free_jack_priv; ++ return 0; ++} ++ ++static void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid) ++{ ++ struct conexant_spec *spec = codec->spec; ++ struct conexant_jack *jacks = spec->jacks.list; ++ ++ if (jacks) { ++ int i; ++ for (i = 0; i < spec->jacks.used; i++) { ++ if (jacks->nid == nid) { ++ unsigned int present; ++ present = snd_hda_codec_read(codec, nid, 0, ++ AC_VERB_GET_PIN_SENSE, 0) & ++ AC_PINSENSE_PRESENCE; ++ ++ present = (present) ? jacks->type : 0 ; ++ ++ snd_jack_report(jacks->jack, ++ present); ++ } ++ jacks++; ++ } ++ } ++} ++ ++static int conexant_init_jacks(struct hda_codec *codec) ++{ ++ struct conexant_spec *spec = codec->spec; ++ int i; ++ ++ for (i = 0; i < spec->num_init_verbs; i++) { ++ const struct hda_verb *hv; ++ ++ hv = spec->init_verbs[i]; ++ while (hv->nid) { ++ int err = 0; ++ switch (hv->param ^ AC_USRSP_EN) { ++ case CONEXANT_HP_EVENT: ++ err = conexant_add_jack(codec, hv->nid, ++ SND_JACK_HEADPHONE); ++ conexant_report_jack(codec, hv->nid); ++ break; ++ case CXT5051_PORTC_EVENT: ++ case CONEXANT_MIC_EVENT: ++ err = conexant_add_jack(codec, hv->nid, ++ SND_JACK_MICROPHONE); ++ conexant_report_jack(codec, hv->nid); ++ break; ++ } ++ if (err < 0) ++ return err; ++ ++hv; ++ } ++ } ++ return 0; ++ ++} ++#else ++static inline void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid) ++{ ++} ++ ++static inline int conexant_init_jacks(struct hda_codec *codec) ++{ ++ return 0; ++} ++#endif ++ + static int conexant_init(struct hda_codec *codec) + { + struct conexant_spec *spec = codec->spec; +@@ -344,18 +463,44 @@ static int conexant_init(struct hda_codec *codec) + + static void conexant_free(struct hda_codec *codec) + { +- struct conexant_spec *spec = codec->spec; +- unsigned int i; +- +- if (spec->kctl_alloc) { +- for (i = 0; i < spec->num_kctl_used; i++) +- kfree(spec->kctl_alloc[i].name); +- kfree(spec->kctl_alloc); +- } +- ++#ifdef CONFIG_SND_JACK ++ struct conexant_spec *spec = codec->spec; ++ if (spec->jacks.list) { ++ struct conexant_jack *jacks = spec->jacks.list; ++ int i; ++ for (i = 0; i < spec->jacks.used; i++, jacks++) { ++ if (jacks->jack) ++ snd_device_free(codec->bus->card, jacks->jack); ++ } ++ snd_array_free(&spec->jacks); ++ } ++#endif + kfree(codec->spec); + } + ++static struct snd_kcontrol_new cxt_capture_mixers[] = { ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "Capture Source", ++ .info = conexant_mux_enum_info, ++ .get = conexant_mux_enum_get, ++ .put = conexant_mux_enum_put ++ }, ++ {} ++}; ++ ++static const char *slave_vols[] = { ++ "Headphone Playback Volume", ++ "Speaker Playback Volume", ++ NULL ++}; ++ ++static const char *slave_sws[] = { ++ "Headphone Playback Switch", ++ "Speaker Playback Switch", ++ NULL ++}; ++ + static int conexant_build_controls(struct hda_codec *codec) + { + struct conexant_spec *spec = codec->spec; +@@ -383,6 +528,32 @@ static int conexant_build_controls(struct hda_codec *codec) + if (err < 0) + return err; + } ++ ++ /* if we have no master control, let's create it */ ++ if (spec->vmaster_nid && ++ !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { ++ unsigned int vmaster_tlv[4]; ++ snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, ++ HDA_OUTPUT, vmaster_tlv); ++ err = snd_hda_add_vmaster(codec, "Master Playback Volume", ++ vmaster_tlv, slave_vols); ++ if (err < 0) ++ return err; ++ } ++ if (spec->vmaster_nid && ++ !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { ++ err = snd_hda_add_vmaster(codec, "Master Playback Switch", ++ NULL, slave_sws); ++ if (err < 0) ++ return err; ++ } ++ ++ if (spec->input_mux) { ++ err = snd_hda_add_new_ctls(codec, cxt_capture_mixers); ++ if (err < 0) ++ return err; ++ } ++ + return 0; + } + +@@ -614,13 +785,6 @@ static void cxt5045_hp_unsol_event(struct hda_codec *codec, + } + + static struct snd_kcontrol_new cxt5045_mixers[] = { +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "Capture Source", +- .info = conexant_mux_enum_info, +- .get = conexant_mux_enum_get, +- .put = conexant_mux_enum_put +- }, + HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x02, HDA_INPUT), +@@ -654,13 +818,6 @@ static struct snd_kcontrol_new cxt5045_benq_mixers[] = { + }; + + static struct snd_kcontrol_new cxt5045_mixers_hp530[] = { +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "Capture Source", +- .info = conexant_mux_enum_info, +- .get = conexant_mux_enum_get, +- .put = conexant_mux_enum_put +- }, + HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT), +@@ -897,15 +1054,9 @@ static const char *cxt5045_models[CXT5045_MODELS] = { + }; + + static struct snd_pci_quirk cxt5045_cfg_tbl[] = { +- SND_PCI_QUIRK(0x103c, 0x30a5, "HP", CXT5045_LAPTOP_HPSENSE), +- SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP_HPSENSE), +- SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP_HPSENSE), +- SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP_HPSENSE), +- SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE), +- SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE), +- SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530), +- SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE), ++ SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series", ++ CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE), + SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ), + SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE), +@@ -915,8 +1066,8 @@ static struct snd_pci_quirk cxt5045_cfg_tbl[] = { + SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE), +- SND_PCI_QUIRK(0x1631, 0xc106, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE), +- SND_PCI_QUIRK(0x1631, 0xc107, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE), ++ SND_PCI_QUIRK_MASK(0x1631, 0xff00, 0xc100, "Packard Bell", ++ CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE), + {} + }; +@@ -930,6 +1081,7 @@ static int patch_cxt5045(struct hda_codec *codec) + if (!spec) + return -ENOMEM; + codec->spec = spec; ++ codec->pin_amp_workaround = 1; + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids); +@@ -1029,7 +1181,7 @@ static int patch_cxt5045(struct hda_codec *codec) + /* Conexant 5047 specific */ + #define CXT5047_SPDIF_OUT 0x11 + +-static hda_nid_t cxt5047_dac_nids[2] = { 0x10, 0x1c }; ++static hda_nid_t cxt5047_dac_nids[1] = { 0x10 }; /* 0x1c */ + static hda_nid_t cxt5047_adc_nids[1] = { 0x12 }; + static hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a }; + +@@ -1037,20 +1189,6 @@ static struct hda_channel_mode cxt5047_modes[1] = { + { 2, NULL }, + }; + +-static struct hda_input_mux cxt5047_capture_source = { +- .num_items = 1, +- .items = { +- { "Mic", 0x2 }, +- } +-}; +- +-static struct hda_input_mux cxt5047_hp_capture_source = { +- .num_items = 1, +- .items = { +- { "ExtMic", 0x2 }, +- } +-}; +- + static struct hda_input_mux cxt5047_toshiba_capture_source = { + .num_items = 2, + .items = { +@@ -1074,7 +1212,11 @@ static int cxt5047_hp_master_sw_put(struct snd_kcontrol *kcontrol, + * the headphone jack + */ + bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE; +- snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0, ++ /* NOTE: Conexat codec needs the index for *OUTPUT* amp of ++ * pin widgets unlike other codecs. In this case, we need to ++ * set index 0x01 for the volume from the mixer amp 0x19. ++ */ ++ snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01, + HDA_AMP_MUTE, bits); + bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE; + snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0, +@@ -1082,16 +1224,6 @@ static int cxt5047_hp_master_sw_put(struct snd_kcontrol *kcontrol, + return 1; + } + +-/* bind volumes of both NID 0x13 (Headphones) and 0x1d (Speakers) */ +-static struct hda_bind_ctls cxt5047_bind_master_vol = { +- .ops = &snd_hda_bind_vol, +- .values = { +- HDA_COMPOSE_AMP_VAL(0x13, 3, 0, HDA_OUTPUT), +- HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), +- 0 +- }, +-}; +- + /* mute internal speaker if HP is plugged */ + static void cxt5047_hp_automute(struct hda_codec *codec) + { +@@ -1102,27 +1234,8 @@ static void cxt5047_hp_automute(struct hda_codec *codec) + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; +- snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0, +- HDA_AMP_MUTE, bits); +- /* Mute/Unmute PCM 2 for good measure - some systems need this */ +- snd_hda_codec_amp_stereo(codec, 0x1c, HDA_OUTPUT, 0, +- HDA_AMP_MUTE, bits); +-} +- +-/* mute internal speaker if HP is plugged */ +-static void cxt5047_hp2_automute(struct hda_codec *codec) +-{ +- struct conexant_spec *spec = codec->spec; +- unsigned int bits; +- +- spec->hp_present = snd_hda_codec_read(codec, 0x13, 0, +- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; +- +- bits = spec->hp_present ? HDA_AMP_MUTE : 0; +- snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0, +- HDA_AMP_MUTE, bits); +- /* Mute/Unmute PCM 2 for good measure - some systems need this */ +- snd_hda_codec_amp_stereo(codec, 0x1c, HDA_OUTPUT, 0, ++ /* See the note in cxt5047_hp_master_sw_put */ ++ snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01, + HDA_AMP_MUTE, bits); + } + +@@ -1163,55 +1276,14 @@ static void cxt5047_hp_unsol_event(struct hda_codec *codec, + } + } + +-/* unsolicited event for HP jack sensing - non-EAPD systems */ +-static void cxt5047_hp2_unsol_event(struct hda_codec *codec, +- unsigned int res) +-{ +- res >>= 26; +- switch (res) { +- case CONEXANT_HP_EVENT: +- cxt5047_hp2_automute(codec); +- break; +- case CONEXANT_MIC_EVENT: +- cxt5047_hp_automic(codec); +- break; +- } +-} +- +-static struct snd_kcontrol_new cxt5047_mixers[] = { +- HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT), +- HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19, 0x02, HDA_INPUT), +- HDA_CODEC_VOLUME("Mic Gain Volume", 0x1a, 0x0, HDA_OUTPUT), +- HDA_CODEC_MUTE("Mic Gain Switch", 0x1a, 0x0, HDA_OUTPUT), ++static struct snd_kcontrol_new cxt5047_base_mixers[] = { ++ HDA_CODEC_VOLUME("Mic Playback Volume", 0x19, 0x02, HDA_INPUT), ++ HDA_CODEC_MUTE("Mic Playback Switch", 0x19, 0x02, HDA_INPUT), ++ HDA_CODEC_VOLUME("Mic Boost", 0x1a, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT), + HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT), +- HDA_CODEC_VOLUME("PCM-2 Volume", 0x1c, 0x00, HDA_OUTPUT), +- HDA_CODEC_MUTE("PCM-2 Switch", 0x1c, 0x00, HDA_OUTPUT), +- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x00, HDA_OUTPUT), +- HDA_CODEC_MUTE("Speaker Playback Switch", 0x1d, 0x00, HDA_OUTPUT), +- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x13, 0x00, HDA_OUTPUT), +- HDA_CODEC_MUTE("Headphone Playback Switch", 0x13, 0x00, HDA_OUTPUT), +- +- {} +-}; +- +-static struct snd_kcontrol_new cxt5047_toshiba_mixers[] = { +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "Capture Source", +- .info = conexant_mux_enum_info, +- .get = conexant_mux_enum_get, +- .put = conexant_mux_enum_put +- }, +- HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT), +- HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19, 0x02, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT), +- HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT), +- HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT), +- HDA_BIND_VOL("Master Playback Volume", &cxt5047_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", +@@ -1224,29 +1296,15 @@ static struct snd_kcontrol_new cxt5047_toshiba_mixers[] = { + {} + }; + +-static struct snd_kcontrol_new cxt5047_hp_mixers[] = { +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "Capture Source", +- .info = conexant_mux_enum_info, +- .get = conexant_mux_enum_get, +- .put = conexant_mux_enum_put +- }, +- HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT), +- HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19,0x02,HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT), +- HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT), +- HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT), ++static struct snd_kcontrol_new cxt5047_hp_spk_mixers[] = { ++ /* See the note in cxt5047_hp_master_sw_put */ ++ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x01, HDA_OUTPUT), ++ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x13, 0x00, HDA_OUTPUT), ++ {} ++}; ++ ++static struct snd_kcontrol_new cxt5047_hp_only_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "Master Playback Switch", +- .info = cxt_eapd_info, +- .get = cxt_eapd_get, +- .put = cxt5047_hp_master_sw_put, +- .private_value = 0x13, +- }, + { } /* end */ + }; + +@@ -1257,8 +1315,8 @@ static struct hda_verb cxt5047_init_verbs[] = { + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 }, + /* HP, Speaker */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, +- {0x13, AC_VERB_SET_CONNECT_SEL,0x1}, +- {0x1d, AC_VERB_SET_CONNECT_SEL,0x0}, ++ {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, /* mixer(0x19) */ ++ {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mixer(0x19) */ + /* Record selector: Mic */ + {0x12, AC_VERB_SET_CONNECT_SEL,0x03}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, +@@ -1278,30 +1336,7 @@ static struct hda_verb cxt5047_init_verbs[] = { + + /* configuration for Toshiba Laptops */ + static struct hda_verb cxt5047_toshiba_init_verbs[] = { +- {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0 }, /* default on */ +- /* pin sensing on HP and Mic jacks */ +- {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, +- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, +- /* Speaker routing */ +- {0x1d, AC_VERB_SET_CONNECT_SEL,0x1}, +- {} +-}; +- +-/* configuration for HP Laptops */ +-static struct hda_verb cxt5047_hp_init_verbs[] = { +- /* pin sensing on HP jack */ +- {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, +- /* 0x13 is actually shared by both HP and speaker; +- * setting the connection to 0 (=0x19) makes the master volume control +- * working mysteriouslly... +- */ +- {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, +- /* Record selector: Ext Mic */ +- {0x12, AC_VERB_SET_CONNECT_SEL,0x03}, +- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, +- AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17}, +- /* Speaker routing */ +- {0x1d, AC_VERB_SET_CONNECT_SEL,0x1}, ++ {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0}, /* default off */ + {} + }; + +@@ -1466,11 +1501,9 @@ static const char *cxt5047_models[CXT5047_MODELS] = { + }; + + static struct snd_pci_quirk cxt5047_cfg_tbl[] = { +- SND_PCI_QUIRK(0x103c, 0x30a0, "HP DV1000", CXT5047_LAPTOP), + SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP), +- SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV2000T/DV3000T", CXT5047_LAPTOP), +- SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2000Z", CXT5047_LAPTOP), +- SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6700", CXT5047_LAPTOP), ++ SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series", ++ CXT5047_LAPTOP), + SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD), + {} + }; +@@ -1484,6 +1517,7 @@ static int patch_cxt5047(struct hda_codec *codec) + if (!spec) + return -ENOMEM; + codec->spec = spec; ++ codec->pin_amp_workaround = 1; + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(cxt5047_dac_nids); +@@ -1492,9 +1526,8 @@ static int patch_cxt5047(struct hda_codec *codec) + spec->num_adc_nids = 1; + spec->adc_nids = cxt5047_adc_nids; + spec->capsrc_nids = cxt5047_capsrc_nids; +- spec->input_mux = &cxt5047_capture_source; + spec->num_mixers = 1; +- spec->mixers[0] = cxt5047_mixers; ++ spec->mixers[0] = cxt5047_base_mixers; + spec->num_init_verbs = 1; + spec->init_verbs[0] = cxt5047_init_verbs; + spec->spdif_route = 0; +@@ -1508,21 +1541,22 @@ static int patch_cxt5047(struct hda_codec *codec) + cxt5047_cfg_tbl); + switch (board_config) { + case CXT5047_LAPTOP: +- codec->patch_ops.unsol_event = cxt5047_hp2_unsol_event; ++ spec->num_mixers = 2; ++ spec->mixers[1] = cxt5047_hp_spk_mixers; ++ codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; + break; + case CXT5047_LAPTOP_HP: +- spec->input_mux = &cxt5047_hp_capture_source; +- spec->num_init_verbs = 2; +- spec->init_verbs[1] = cxt5047_hp_init_verbs; +- spec->mixers[0] = cxt5047_hp_mixers; ++ spec->num_mixers = 2; ++ spec->mixers[1] = cxt5047_hp_only_mixers; + codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; + codec->patch_ops.init = cxt5047_hp_init; + break; + case CXT5047_LAPTOP_EAPD: + spec->input_mux = &cxt5047_toshiba_capture_source; ++ spec->num_mixers = 2; ++ spec->mixers[1] = cxt5047_hp_spk_mixers; + spec->num_init_verbs = 2; + spec->init_verbs[1] = cxt5047_toshiba_init_verbs; +- spec->mixers[0] = cxt5047_toshiba_mixers; + codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; + break; + #ifdef CONFIG_SND_DEBUG +@@ -1533,15 +1567,13 @@ static int patch_cxt5047(struct hda_codec *codec) + codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; + #endif + } ++ spec->vmaster_nid = 0x13; + return 0; + } + + /* Conexant 5051 specific */ + static hda_nid_t cxt5051_dac_nids[1] = { 0x10 }; + static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 }; +-#define CXT5051_SPDIF_OUT 0x1C +-#define CXT5051_PORTB_EVENT 0x38 +-#define CXT5051_PORTC_EVENT 0x39 + + static struct hda_channel_mode cxt5051_modes[1] = { + { 2, NULL }, +@@ -1571,8 +1603,11 @@ static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol, + /* toggle input of built-in and mic jack appropriately */ + static void cxt5051_portb_automic(struct hda_codec *codec) + { ++ struct conexant_spec *spec = codec->spec; + unsigned int present; + ++ if (spec->no_auto_mic) ++ return; + present = snd_hda_codec_read(codec, 0x17, 0, + AC_VERB_GET_PIN_SENSE, 0) & + AC_PINSENSE_PRESENCE; +@@ -1588,6 +1623,8 @@ static void cxt5051_portc_automic(struct hda_codec *codec) + unsigned int present; + hda_nid_t new_adc; + ++ if (spec->no_auto_mic) ++ return; + present = snd_hda_codec_read(codec, 0x18, 0, + AC_VERB_GET_PIN_SENSE, 0) & + AC_PINSENSE_PRESENCE; +@@ -1621,6 +1658,7 @@ static void cxt5051_hp_automute(struct hda_codec *codec) + static void cxt5051_hp_unsol_event(struct hda_codec *codec, + unsigned int res) + { ++ int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20; + switch (res >> 26) { + case CONEXANT_HP_EVENT: + cxt5051_hp_automute(codec); +@@ -1632,6 +1670,7 @@ static void cxt5051_hp_unsol_event(struct hda_codec *codec, + cxt5051_portc_automic(codec); + break; + } ++ conexant_report_jack(codec, nid); + } + + static struct snd_kcontrol_new cxt5051_mixers[] = { +@@ -1672,6 +1711,22 @@ static struct snd_kcontrol_new cxt5051_hp_mixers[] = { + {} + }; + ++static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = { ++ HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x00, HDA_INPUT), ++ HDA_CODEC_MUTE("Mic Switch", 0x14, 0x00, HDA_INPUT), ++ HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "Master Playback Switch", ++ .info = cxt_eapd_info, ++ .get = cxt_eapd_get, ++ .put = cxt5051_hp_master_sw_put, ++ .private_value = 0x1a, ++ }, ++ ++ {} ++}; ++ + static struct hda_verb cxt5051_init_verbs[] = { + /* Line in, Mic */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, +@@ -1702,10 +1757,71 @@ static struct hda_verb cxt5051_init_verbs[] = { + { } /* end */ + }; + ++static struct hda_verb cxt5051_hp_dv6736_init_verbs[] = { ++ /* Line in, Mic */ ++ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, ++ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, ++ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, ++ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, ++ /* SPK */ ++ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, ++ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, ++ /* HP, Amp */ ++ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, ++ {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, ++ /* DAC1 */ ++ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ /* Record selector: Int mic */ ++ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, ++ {0x14, AC_VERB_SET_CONNECT_SEL, 0x1}, ++ /* SPDIF route: PCM */ ++ {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, ++ /* EAPD */ ++ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ ++ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, ++ {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, ++ { } /* end */ ++}; ++ ++static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = { ++ /* Line in, Mic */ ++ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, ++ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, ++ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, ++ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, ++ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, ++ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, ++ /* SPK */ ++ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, ++ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, ++ /* HP, Amp */ ++ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, ++ {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, ++ /* Docking HP */ ++ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, ++ {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, ++ /* DAC1 */ ++ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ /* Record selector: Int mic */ ++ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, ++ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, ++ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, ++ /* SPDIF route: PCM */ ++ {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, ++ /* EAPD */ ++ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ ++ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, ++ {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, ++ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT}, ++ {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, ++ { } /* end */ ++}; ++ + /* initialize jack-sensing, too */ + static int cxt5051_init(struct hda_codec *codec) + { + conexant_init(codec); ++ conexant_init_jacks(codec); + if (codec->patch_ops.unsol_event) { + cxt5051_hp_automute(codec); + cxt5051_portb_automic(codec); +@@ -1718,18 +1834,25 @@ static int cxt5051_init(struct hda_codec *codec) + enum { + CXT5051_LAPTOP, /* Laptops w/ EAPD support */ + CXT5051_HP, /* no docking */ ++ CXT5051_HP_DV6736, /* HP without mic switch */ ++ CXT5051_LENOVO_X200, /* Lenovo X200 laptop */ + CXT5051_MODELS + }; + + static const char *cxt5051_models[CXT5051_MODELS] = { + [CXT5051_LAPTOP] = "laptop", + [CXT5051_HP] = "hp", ++ [CXT5051_HP_DV6736] = "hp-dv6736", ++ [CXT5051_LENOVO_X200] = "lenovo-x200", + }; + + static struct snd_pci_quirk cxt5051_cfg_tbl[] = { ++ SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736), ++ SND_PCI_QUIRK(0x103c, 0x360b, "Compaq Presario CQ60", CXT5051_HP), + SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", + CXT5051_LAPTOP), + SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), ++ SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200), + {} + }; + +@@ -1742,6 +1865,7 @@ static int patch_cxt5051(struct hda_codec *codec) + if (!spec) + return -ENOMEM; + codec->spec = spec; ++ codec->pin_amp_workaround = 1; + + codec->patch_ops = conexant_patch_ops; + codec->patch_ops.init = cxt5051_init; +@@ -1762,17 +1886,22 @@ static int patch_cxt5051(struct hda_codec *codec) + spec->cur_adc = 0; + spec->cur_adc_idx = 0; + ++ codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; ++ + board_config = snd_hda_check_board_config(codec, CXT5051_MODELS, + cxt5051_models, + cxt5051_cfg_tbl); + switch (board_config) { + case CXT5051_HP: +- codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; + spec->mixers[0] = cxt5051_hp_mixers; + break; +- default: +- case CXT5051_LAPTOP: +- codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; ++ case CXT5051_HP_DV6736: ++ spec->init_verbs[0] = cxt5051_hp_dv6736_init_verbs; ++ spec->mixers[0] = cxt5051_hp_dv6736_mixers; ++ spec->no_auto_mic = 1; ++ break; ++ case CXT5051_LENOVO_X200: ++ spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs; + break; + } + +@@ -1783,7 +1912,7 @@ static int patch_cxt5051(struct hda_codec *codec) + /* + */ + +-struct hda_codec_preset snd_hda_preset_conexant[] = { ++static struct hda_codec_preset snd_hda_preset_conexant[] = { + { .id = 0x14f15045, .name = "CX20549 (Venice)", + .patch = patch_cxt5045 }, + { .id = 0x14f15047, .name = "CX20551 (Waikiki)", +@@ -1792,3 +1921,28 @@ struct hda_codec_preset snd_hda_preset_conexant[] = { + .patch = patch_cxt5051 }, + {} /* terminator */ + }; ++ ++MODULE_ALIAS("snd-hda-codec-id:14f15045"); ++MODULE_ALIAS("snd-hda-codec-id:14f15047"); ++MODULE_ALIAS("snd-hda-codec-id:14f15051"); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Conexant HD-audio codec"); ++ ++static struct hda_codec_preset_list conexant_list = { ++ .preset = snd_hda_preset_conexant, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init patch_conexant_init(void) ++{ ++ return snd_hda_add_codec_preset(&conexant_list); ++} ++ ++static void __exit patch_conexant_exit(void) ++{ ++ snd_hda_delete_codec_preset(&conexant_list); ++} ++ ++module_init(patch_conexant_init) ++module_exit(patch_conexant_exit) +diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c +new file mode 100644 +index 0000000..fcc77fe +--- /dev/null ++++ b/sound/pci/hda/patch_intelhdmi.c +@@ -0,0 +1,718 @@ ++/* ++ * ++ * patch_intelhdmi.c - Patch for Intel HDMI codecs ++ * ++ * Copyright(c) 2008 Intel Corporation. All rights reserved. ++ * ++ * Authors: ++ * Jiang Zhe ++ * Wu Fengguang ++ * ++ * Maintained by: ++ * Wu Fengguang ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the Free ++ * Software Foundation; either version 2 of the License, or (at your option) ++ * any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "hda_codec.h" ++#include "hda_local.h" ++ ++#define CVT_NID 0x02 /* audio converter */ ++#define PIN_NID 0x03 /* HDMI output pin */ ++ ++#define INTEL_HDMI_EVENT_TAG 0x08 ++ ++struct intel_hdmi_spec { ++ struct hda_multi_out multiout; ++ struct hda_pcm pcm_rec; ++ struct hdmi_eld sink_eld; ++}; ++ ++static struct hda_verb pinout_enable_verb[] = { ++ {PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, ++ {} /* terminator */ ++}; ++ ++static struct hda_verb unsolicited_response_verb[] = { ++ {PIN_NID, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ++ INTEL_HDMI_EVENT_TAG}, ++ {} ++}; ++ ++static struct hda_verb def_chan_map[] = { ++ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x00}, ++ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x11}, ++ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x22}, ++ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x33}, ++ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x44}, ++ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x55}, ++ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x66}, ++ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x77}, ++ {} ++}; ++ ++ ++struct hdmi_audio_infoframe { ++ u8 type; /* 0x84 */ ++ u8 ver; /* 0x01 */ ++ u8 len; /* 0x0a */ ++ ++ u8 checksum; /* PB0 */ ++ u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ ++ u8 SS01_SF24; ++ u8 CXT04; ++ u8 CA; ++ u8 LFEPBL01_LSV36_DM_INH7; ++ u8 reserved[5]; /* PB6 - PB10 */ ++}; ++ ++/* ++ * CEA speaker placement: ++ * ++ * FLH FCH FRH ++ * FLW FL FLC FC FRC FR FRW ++ * ++ * LFE ++ * TC ++ * ++ * RL RLC RC RRC RR ++ * ++ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to ++ * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. ++ */ ++enum cea_speaker_placement { ++ FL = (1 << 0), /* Front Left */ ++ FC = (1 << 1), /* Front Center */ ++ FR = (1 << 2), /* Front Right */ ++ FLC = (1 << 3), /* Front Left Center */ ++ FRC = (1 << 4), /* Front Right Center */ ++ RL = (1 << 5), /* Rear Left */ ++ RC = (1 << 6), /* Rear Center */ ++ RR = (1 << 7), /* Rear Right */ ++ RLC = (1 << 8), /* Rear Left Center */ ++ RRC = (1 << 9), /* Rear Right Center */ ++ LFE = (1 << 10), /* Low Frequency Effect */ ++ FLW = (1 << 11), /* Front Left Wide */ ++ FRW = (1 << 12), /* Front Right Wide */ ++ FLH = (1 << 13), /* Front Left High */ ++ FCH = (1 << 14), /* Front Center High */ ++ FRH = (1 << 15), /* Front Right High */ ++ TC = (1 << 16), /* Top Center */ ++}; ++ ++/* ++ * ELD SA bits in the CEA Speaker Allocation data block ++ */ ++static int eld_speaker_allocation_bits[] = { ++ [0] = FL | FR, ++ [1] = LFE, ++ [2] = FC, ++ [3] = RL | RR, ++ [4] = RC, ++ [5] = FLC | FRC, ++ [6] = RLC | RRC, ++ /* the following are not defined in ELD yet */ ++ [7] = FLW | FRW, ++ [8] = FLH | FRH, ++ [9] = TC, ++ [10] = FCH, ++}; ++ ++struct cea_channel_speaker_allocation { ++ int ca_index; ++ int speakers[8]; ++ ++ /* derived values, just for convenience */ ++ int channels; ++ int spk_mask; ++}; ++ ++/* ++ * This is an ordered list! ++ * ++ * The preceding ones have better chances to be selected by ++ * hdmi_setup_channel_allocation(). ++ */ ++static struct cea_channel_speaker_allocation channel_allocations[] = { ++/* channel: 8 7 6 5 4 3 2 1 */ ++{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, ++ /* 2.1 */ ++{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, ++ /* Dolby Surround */ ++{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, ++{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, ++{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, ++{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, ++{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, ++{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, ++{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, ++{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, ++{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, ++ /* 5.1 */ ++{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, ++{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, ++{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, ++ /* 6.1 */ ++{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, ++{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, ++{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, ++ /* 7.1 */ ++{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, ++{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, ++{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, ++{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, ++{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, ++{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, ++{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, ++{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, ++{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, ++{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, ++{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, ++{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, ++{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, ++{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, ++{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, ++}; ++ ++/* ++ * HDMI routines ++ */ ++ ++#ifdef BE_PARANOID ++static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid, ++ int *packet_index, int *byte_index) ++{ ++ int val; ++ ++ val = snd_hda_codec_read(codec, 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, ++ 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); ++} ++ ++static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid, ++ unsigned char val) ++{ ++ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); ++} ++ ++static void hdmi_enable_output(struct hda_codec *codec) ++{ ++ /* Unmute */ ++ if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP) ++ snd_hda_codec_write(codec, PIN_NID, 0, ++ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); ++ /* Enable pin out */ ++ snd_hda_sequence_write(codec, pinout_enable_verb); ++} ++ ++/* ++ * Enable Audio InfoFrame Transmission ++ */ ++static void hdmi_start_infoframe_trans(struct hda_codec *codec) ++{ ++ hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0); ++ snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT, ++ AC_DIPXMIT_BEST); ++} ++ ++/* ++ * Disable Audio InfoFrame Transmission ++ */ ++static void hdmi_stop_infoframe_trans(struct hda_codec *codec) ++{ ++ hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0); ++ snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT, ++ AC_DIPXMIT_DISABLE); ++} ++ ++static int hdmi_get_channel_count(struct hda_codec *codec) ++{ ++ return 1 + snd_hda_codec_read(codec, CVT_NID, 0, ++ AC_VERB_GET_CVT_CHAN_COUNT, 0); ++} ++ ++static void hdmi_set_channel_count(struct hda_codec *codec, int chs) ++{ ++ snd_hda_codec_write(codec, CVT_NID, 0, ++ AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); ++ ++ if (chs != hdmi_get_channel_count(codec)) ++ snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n", ++ chs, hdmi_get_channel_count(codec)); ++} ++ ++static void hdmi_debug_channel_mapping(struct hda_codec *codec) ++{ ++#ifdef CONFIG_SND_DEBUG_VERBOSE ++ int i; ++ int slot; ++ ++ for (i = 0; i < 8; i++) { ++ slot = snd_hda_codec_read(codec, CVT_NID, 0, ++ AC_VERB_GET_HDMI_CHAN_SLOT, i); ++ printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", ++ slot >> 4, slot & 0x7); ++ } ++#endif ++} ++ ++static void hdmi_parse_eld(struct hda_codec *codec) ++{ ++ struct intel_hdmi_spec *spec = codec->spec; ++ struct hdmi_eld *eld = &spec->sink_eld; ++ ++ if (!snd_hdmi_get_eld(eld, codec, PIN_NID)) ++ snd_hdmi_show_eld(eld); ++} ++ ++ ++/* ++ * Audio InfoFrame routines ++ */ ++ ++static void hdmi_debug_dip_size(struct hda_codec *codec) ++{ ++#ifdef CONFIG_SND_DEBUG_VERBOSE ++ int i; ++ int size; ++ ++ size = snd_hdmi_get_eld_size(codec, PIN_NID); ++ printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size); ++ ++ for (i = 0; i < 8; i++) { ++ size = snd_hda_codec_read(codec, PIN_NID, 0, ++ AC_VERB_GET_HDMI_DIP_SIZE, i); ++ printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size); ++ } ++#endif ++} ++ ++static void hdmi_clear_dip_buffers(struct hda_codec *codec) ++{ ++#ifdef BE_PARANOID ++ int i, j; ++ int size; ++ int pi, bi; ++ for (i = 0; i < 8; i++) { ++ size = snd_hda_codec_read(codec, PIN_NID, 0, ++ AC_VERB_GET_HDMI_DIP_SIZE, i); ++ if (size == 0) ++ continue; ++ ++ hdmi_set_dip_index(codec, PIN_NID, i, 0x0); ++ for (j = 1; j < 1000; j++) { ++ hdmi_write_dip_byte(codec, PIN_NID, 0x0); ++ hdmi_get_dip_index(codec, PIN_NID, &pi, &bi); ++ if (pi != i) ++ snd_printd(KERN_INFO "dip index %d: %d != %d\n", ++ bi, pi, i); ++ if (bi == 0) /* byte index wrapped around */ ++ break; ++ } ++ snd_printd(KERN_INFO ++ "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n", ++ i, size, j); ++ } ++#endif ++} ++ ++static void hdmi_fill_audio_infoframe(struct hda_codec *codec, ++ 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 */ ++ ++ for (i = 0; i < sizeof(ai); i++) ++ sum += params[i]; ++ ai->checksum = - sum; ++ ++ hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0); ++ for (i = 0; i < sizeof(ai); i++) ++ hdmi_write_dip_byte(codec, PIN_NID, params[i]); ++} ++ ++/* ++ * Compute derived values in channel_allocations[]. ++ */ ++static void init_channel_allocations(void) ++{ ++ int i, j; ++ struct cea_channel_speaker_allocation *p; ++ ++ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { ++ p = channel_allocations + i; ++ p->channels = 0; ++ p->spk_mask = 0; ++ for (j = 0; j < ARRAY_SIZE(p->speakers); j++) ++ if (p->speakers[j]) { ++ p->channels++; ++ p->spk_mask |= p->speakers[j]; ++ } ++ } ++} ++ ++/* ++ * The transformation takes two steps: ++ * ++ * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask ++ * spk_mask => (channel_allocations[]) => ai->CA ++ * ++ * TODO: it could select the wrong CA from multiple candidates. ++*/ ++static int hdmi_setup_channel_allocation(struct hda_codec *codec, ++ struct hdmi_audio_infoframe *ai) ++{ ++ struct intel_hdmi_spec *spec = codec->spec; ++ struct hdmi_eld *eld = &spec->sink_eld; ++ int i; ++ int spk_mask = 0; ++ int channels = 1 + (ai->CC02_CT47 & 0x7); ++ char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; ++ ++ /* ++ * CA defaults to 0 for basic stereo audio ++ */ ++ if (channels <= 2) ++ return 0; ++ ++ /* ++ * HDMI sink's ELD info cannot always be retrieved for now, e.g. ++ * in console or for audio devices. Assume the highest speakers ++ * configuration, to _not_ prohibit multi-channel audio playback. ++ */ ++ if (!eld->spk_alloc) ++ eld->spk_alloc = 0xffff; ++ ++ /* ++ * expand ELD's speaker allocation mask ++ * ++ * ELD tells the speaker mask in a compact(paired) form, ++ * expand ELD's notions to match the ones used by Audio InfoFrame. ++ */ ++ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { ++ if (eld->spk_alloc & (1 << i)) ++ spk_mask |= eld_speaker_allocation_bits[i]; ++ } ++ ++ /* search for the first working match in the CA table */ ++ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { ++ if (channels == channel_allocations[i].channels && ++ (spk_mask & channel_allocations[i].spk_mask) == ++ channel_allocations[i].spk_mask) { ++ ai->CA = channel_allocations[i].ca_index; ++ break; ++ } ++ } ++ ++ snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf)); ++ snd_printdd(KERN_INFO ++ "HDMI: select CA 0x%x for %d-channel allocation: %s\n", ++ ai->CA, channels, buf); ++ ++ return ai->CA; ++} ++ ++static void hdmi_setup_channel_mapping(struct hda_codec *codec, ++ struct hdmi_audio_infoframe *ai) ++{ ++ if (!ai->CA) ++ return; ++ ++ /* ++ * TODO: adjust channel mapping if necessary ++ * ALSA sequence is front/surr/clfe/side? ++ */ ++ ++ snd_hda_sequence_write(codec, def_chan_map); ++ hdmi_debug_channel_mapping(codec); ++} ++ ++ ++static void hdmi_setup_audio_infoframe(struct hda_codec *codec, ++ struct snd_pcm_substream *substream) ++{ ++ struct hdmi_audio_infoframe ai = { ++ .type = 0x84, ++ .ver = 0x01, ++ .len = 0x0a, ++ .CC02_CT47 = substream->runtime->channels - 1, ++ }; ++ ++ hdmi_setup_channel_allocation(codec, &ai); ++ hdmi_setup_channel_mapping(codec, &ai); ++ ++ hdmi_fill_audio_infoframe(codec, &ai); ++ hdmi_start_infoframe_trans(codec); ++} ++ ++ ++/* ++ * Unsolicited events ++ */ ++ ++static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) ++{ ++ int pind = !!(res & AC_UNSOL_RES_PD); ++ int eldv = !!(res & AC_UNSOL_RES_ELDV); ++ ++ printk(KERN_INFO ++ "HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n", ++ pind, eldv); ++ ++ if (pind && eldv) { ++ hdmi_parse_eld(codec); ++ /* TODO: do real things about ELD */ ++ } ++} ++ ++static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) ++{ ++ 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", ++ subtag, ++ cp_state, ++ cp_ready); ++ ++ /* TODO */ ++ if (cp_state) ++ ; ++ if (cp_ready) ++ ; ++} ++ ++ ++static void intel_hdmi_unsol_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; ++ ++ if (tag != INTEL_HDMI_EVENT_TAG) { ++ snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag); ++ return; ++ } ++ ++ if (subtag == 0) ++ hdmi_intrinsic_event(codec, res); ++ else ++ hdmi_non_intrinsic_event(codec, 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) ++{ ++ struct intel_hdmi_spec *spec = codec->spec; ++ ++ hdmi_stop_infoframe_trans(codec); ++ ++ return snd_hda_multi_out_dig_close(codec, &spec->multiout); ++} ++ ++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; ++ ++ snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, ++ format, substream); ++ ++ hdmi_set_channel_count(codec, substream->runtime->channels); ++ ++ hdmi_setup_audio_infoframe(codec, substream); ++ ++ return 0; ++} ++ ++static struct hda_pcm_stream intel_hdmi_pcm_playback = { ++ .substreams = 1, ++ .channels_min = 2, ++ .channels_max = 8, ++ .nid = CVT_NID, /* NID to query formats and rates and setup streams */ ++ .ops = { ++ .open = intel_hdmi_playback_pcm_open, ++ .close = intel_hdmi_playback_pcm_close, ++ .prepare = intel_hdmi_playback_pcm_prepare ++ }, ++}; ++ ++static int intel_hdmi_build_pcms(struct hda_codec *codec) ++{ ++ struct intel_hdmi_spec *spec = codec->spec; ++ struct hda_pcm *info = &spec->pcm_rec; ++ ++ codec->num_pcms = 1; ++ codec->pcm_info = info; ++ ++ info->name = "INTEL HDMI"; ++ info->pcm_type = HDA_PCM_TYPE_HDMI; ++ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback; ++ ++ return 0; ++} ++ ++static int intel_hdmi_build_controls(struct hda_codec *codec) ++{ ++ struct intel_hdmi_spec *spec = codec->spec; ++ int err; ++ ++ err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); ++ if (err < 0) ++ return err; ++ ++ return 0; ++} ++ ++static int intel_hdmi_init(struct hda_codec *codec) ++{ ++ hdmi_enable_output(codec); ++ ++ snd_hda_sequence_write(codec, unsolicited_response_verb); ++ ++ return 0; ++} ++ ++static void intel_hdmi_free(struct hda_codec *codec) ++{ ++ struct intel_hdmi_spec *spec = codec->spec; ++ ++ snd_hda_eld_proc_free(codec, &spec->sink_eld); ++ kfree(spec); ++} ++ ++static struct hda_codec_ops intel_hdmi_patch_ops = { ++ .init = intel_hdmi_init, ++ .free = intel_hdmi_free, ++ .build_pcms = intel_hdmi_build_pcms, ++ .build_controls = intel_hdmi_build_controls, ++ .unsol_event = intel_hdmi_unsol_event, ++}; ++ ++static int patch_intel_hdmi(struct hda_codec *codec) ++{ ++ struct intel_hdmi_spec *spec; ++ ++ 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; ++ codec->patch_ops = intel_hdmi_patch_ops; ++ ++ snd_hda_eld_proc_new(codec, &spec->sink_eld); ++ ++ init_channel_allocations(); ++ ++ return 0; ++} ++ ++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 = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, ++ {} /* terminator */ ++}; ++ ++MODULE_ALIAS("snd-hda-codec-id:808629fb"); ++MODULE_ALIAS("snd-hda-codec-id:80862801"); ++MODULE_ALIAS("snd-hda-codec-id:80862802"); ++MODULE_ALIAS("snd-hda-codec-id:80862803"); ++MODULE_ALIAS("snd-hda-codec-id:80862804"); ++MODULE_ALIAS("snd-hda-codec-id:10951392"); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Intel HDMI HD-audio codec"); ++ ++static struct hda_codec_preset_list intel_list = { ++ .preset = snd_hda_preset_intelhdmi, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init patch_intelhdmi_init(void) ++{ ++ return snd_hda_add_codec_preset(&intel_list); ++} ++ ++static void __exit patch_intelhdmi_exit(void) ++{ ++ snd_hda_delete_codec_preset(&intel_list); ++} ++ ++module_init(patch_intelhdmi_init) ++module_exit(patch_intelhdmi_exit) +diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c +index 2eed2c8..d57d813 100644 +--- a/sound/pci/hda/patch_nvhdmi.c ++++ b/sound/pci/hda/patch_nvhdmi.c +@@ -158,8 +158,38 @@ static int patch_nvhdmi(struct hda_codec *codec) + /* + * patch entries + */ +-struct hda_codec_preset snd_hda_preset_nvhdmi[] = { +- { .id = 0x10de0002, .name = "NVIDIA MCP78 HDMI", .patch = patch_nvhdmi }, +- { .id = 0x10de0007, .name = "NVIDIA MCP7A HDMI", .patch = patch_nvhdmi }, ++static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { ++ { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi }, ++ { .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi }, ++ { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi }, ++ { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi }, ++ { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi }, + {} /* terminator */ + }; ++ ++MODULE_ALIAS("snd-hda-codec-id:10de0002"); ++MODULE_ALIAS("snd-hda-codec-id:10de0006"); ++MODULE_ALIAS("snd-hda-codec-id:10de0007"); ++MODULE_ALIAS("snd-hda-codec-id:10de0067"); ++MODULE_ALIAS("snd-hda-codec-id:10de8001"); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec"); ++ ++static struct hda_codec_preset_list nvhdmi_list = { ++ .preset = snd_hda_preset_nvhdmi, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init patch_nvhdmi_init(void) ++{ ++ return snd_hda_add_codec_preset(&nvhdmi_list); ++} ++ ++static void __exit patch_nvhdmi_exit(void) ++{ ++ snd_hda_delete_codec_preset(&nvhdmi_list); ++} ++ ++module_init(patch_nvhdmi_init) ++module_exit(patch_nvhdmi_exit) +diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c +index 067e6ed..0fd258e 100644 +--- a/sound/pci/hda/patch_realtek.c ++++ b/sound/pci/hda/patch_realtek.c +@@ -30,7 +30,7 @@ + #include + #include "hda_codec.h" + #include "hda_local.h" +-#include "hda_patch.h" ++#include "hda_beep.h" + + #define ALC880_FRONT_EVENT 0x01 + #define ALC880_DCVOL_EVENT 0x02 +@@ -78,6 +78,7 @@ enum { + ALC260_ACER, + ALC260_WILL, + ALC260_REPLACER_672V, ++ ALC260_FAVORIT100, + #ifdef CONFIG_SND_DEBUG + ALC260_TEST, + #endif +@@ -104,6 +105,7 @@ enum { + ALC262_NEC, + ALC262_TOSHIBA_S06, + ALC262_TOSHIBA_RX1, ++ ALC262_TYAN, + ALC262_AUTO, + ALC262_MODEL_LAST /* last tag */ + }; +@@ -132,6 +134,7 @@ enum { + ALC269_ASUS_EEEPC_P703, + ALC269_ASUS_EEEPC_P901, + ALC269_FUJITSU, ++ ALC269_LIFEBOOK, + ALC269_AUTO, + ALC269_MODEL_LAST /* last tag */ + }; +@@ -154,6 +157,7 @@ enum { + enum { + ALC660VD_3ST, + ALC660VD_3ST_DIG, ++ ALC660VD_ASUS_V1S, + ALC861VD_3ST, + ALC861VD_3ST_DIG, + ALC861VD_6ST_DIG, +@@ -184,6 +188,8 @@ enum { + ALC663_ASUS_MODE4, + ALC663_ASUS_MODE5, + ALC663_ASUS_MODE6, ++ ALC272_DELL, ++ ALC272_DELL_ZM1, + ALC662_AUTO, + ALC662_MODEL_LAST, + }; +@@ -214,6 +220,7 @@ enum { + ALC883_TARGA_2ch_DIG, + ALC883_ACER, + ALC883_ACER_ASPIRE, ++ ALC888_ACER_ASPIRE_4930G, + ALC883_MEDION, + ALC883_MEDION_MD2, + ALC883_LAPTOP_EAPD, +@@ -227,13 +234,22 @@ enum { + ALC883_MITAC, + ALC883_CLEVO_M720, + ALC883_FUJITSU_PI2515, ++ ALC888_FUJITSU_XA3530, + ALC883_3ST_6ch_INTEL, + ALC888_ASUS_M90V, + ALC888_ASUS_EEE1601, ++ ALC1200_ASUS_P5Q, + ALC883_AUTO, + ALC883_MODEL_LAST, + }; + ++/* styles of capture selection */ ++enum { ++ CAPT_MUX = 0, /* only mux based */ ++ CAPT_MIX, /* only mixer based */ ++ CAPT_1MUX_MIX, /* first mux and other mixers */ ++}; ++ + /* for GPIO Poll */ + #define GPIO_MASK 0x03 + +@@ -241,6 +257,8 @@ struct alc_spec { + /* codec parameterization */ + struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ + unsigned int num_mixers; ++ struct snd_kcontrol_new *cap_mixer; /* capture mixer */ ++ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ + + const struct hda_verb *init_verbs[5]; /* initialization verbs + * don't forget NULL +@@ -264,12 +282,15 @@ struct alc_spec { + * dig_out_nid and hp_nid are optional + */ + hda_nid_t alt_dac_nid; ++ hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */ ++ int dig_out_type; + + /* capture */ + unsigned int num_adc_nids; + hda_nid_t *adc_nids; + hda_nid_t *capsrc_nids; + hda_nid_t dig_in_nid; /* digital-in NID; optional */ ++ int capture_style; /* capture style (CAPT_*) */ + + /* capture source */ + unsigned int num_mux_defs; +@@ -286,9 +307,8 @@ struct alc_spec { + + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; +- unsigned int num_kctl_alloc, num_kctl_used; +- struct snd_kcontrol_new *kctl_alloc; +- struct hda_input_mux private_imux; ++ struct snd_array kctls; ++ struct hda_input_mux private_imux[3]; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + + /* hooks */ +@@ -300,6 +320,9 @@ struct alc_spec { + unsigned int jack_present: 1; + unsigned int master_sw: 1; + ++ /* other flags */ ++ unsigned int no_analog :1; /* digital I/O only */ ++ + /* for virtual master */ + hda_nid_t vmaster_nid; + #ifdef CONFIG_SND_HDA_POWER_SAVE +@@ -309,13 +332,6 @@ struct alc_spec { + /* for PLL fix */ + hda_nid_t pll_nid; + unsigned int pll_coef_idx, pll_coef_bit; +- +-#ifdef SND_HDA_NEEDS_RESUME +-#define ALC_MAX_PINS 16 +- unsigned int num_pins; +- hda_nid_t pin_nids[ALC_MAX_PINS]; +- unsigned int pin_cfgs[ALC_MAX_PINS]; +-#endif + }; + + /* +@@ -325,11 +341,13 @@ struct alc_config_preset { + struct snd_kcontrol_new *mixers[5]; /* should be identical size + * with spec + */ ++ struct snd_kcontrol_new *cap_mixer; /* capture mixer */ + const struct hda_verb *init_verbs[5]; + unsigned int num_dacs; + hda_nid_t *dac_nids; + hda_nid_t dig_out_nid; /* optional */ + hda_nid_t hp_nid; /* optional */ ++ hda_nid_t *slave_dig_outs; + unsigned int num_adc_nids; + hda_nid_t *adc_nids; + hda_nid_t *capsrc_nids; +@@ -377,14 +395,40 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, + { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; ++ const struct hda_input_mux *imux; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); +- unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; ++ unsigned int mux_idx; + hda_nid_t nid = spec->capsrc_nids ? + spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; +- return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol, +- nid, &spec->cur_mux[adc_idx]); +-} + ++ mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; ++ imux = &spec->input_mux[mux_idx]; ++ ++ if (spec->capture_style && ++ !(spec->capture_style == CAPT_1MUX_MIX && !adc_idx)) { ++ /* Matrix-mixer style (e.g. ALC882) */ ++ unsigned int *cur_val = &spec->cur_mux[adc_idx]; ++ unsigned int i, idx; ++ ++ idx = ucontrol->value.enumerated.item[0]; ++ if (idx >= imux->num_items) ++ idx = imux->num_items - 1; ++ if (*cur_val == idx) ++ return 0; ++ for (i = 0; i < imux->num_items; i++) { ++ unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; ++ snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, ++ imux->items[i].index, ++ HDA_AMP_MUTE, v); ++ } ++ *cur_val = idx; ++ return 1; ++ } else { ++ /* MUX style (e.g. ALC880) */ ++ return snd_hda_input_mux_put(codec, imux, ucontrol, nid, ++ &spec->cur_mux[adc_idx]); ++ } ++} + + /* + * channel mode setting +@@ -719,6 +763,67 @@ static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol, + #endif /* CONFIG_SND_DEBUG */ + + /* ++ * set up the input pin config (depending on the given auto-pin type) ++ */ ++static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid, ++ int auto_pin_type) ++{ ++ unsigned int val = PIN_IN; ++ ++ if (auto_pin_type <= AUTO_PIN_FRONT_MIC) { ++ unsigned int pincap; ++ pincap = snd_hda_query_pin_caps(codec, nid); ++ pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; ++ if (pincap & AC_PINCAP_VREF_80) ++ val = PIN_VREF80; ++ else if (pincap & AC_PINCAP_VREF_50) ++ val = PIN_VREF50; ++ else if (pincap & AC_PINCAP_VREF_100) ++ val = PIN_VREF100; ++ else if (pincap & AC_PINCAP_VREF_GRD) ++ val = PIN_VREFGRD; ++ } ++ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); ++} ++ ++/* ++ */ ++static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix) ++{ ++ if (snd_BUG_ON(spec->num_mixers >= ARRAY_SIZE(spec->mixers))) ++ return; ++ spec->mixers[spec->num_mixers++] = mix; ++} ++ ++static void add_verb(struct alc_spec *spec, const struct hda_verb *verb) ++{ ++ if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs))) ++ return; ++ spec->init_verbs[spec->num_init_verbs++] = verb; ++} ++ ++#ifdef CONFIG_PROC_FS ++/* ++ * hook for proc ++ */ ++static void print_realtek_coef(struct snd_info_buffer *buffer, ++ struct hda_codec *codec, hda_nid_t nid) ++{ ++ int coeff; ++ ++ if (nid != 0x20) ++ return; ++ coeff = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0); ++ snd_iprintf(buffer, " Processing Coefficient: 0x%02x\n", coeff); ++ coeff = snd_hda_codec_read(codec, nid, 0, ++ AC_VERB_GET_COEF_INDEX, 0); ++ snd_iprintf(buffer, " Coefficient Index: 0x%02x\n", coeff); ++} ++#else ++#define print_realtek_coef NULL ++#endif ++ ++/* + * set up from the preset table + */ + static void setup_preset(struct alc_spec *spec, +@@ -727,11 +832,11 @@ static void setup_preset(struct alc_spec *spec, + int i; + + for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++) +- spec->mixers[spec->num_mixers++] = preset->mixers[i]; ++ add_mixer(spec, preset->mixers[i]); ++ spec->cap_mixer = preset->cap_mixer; + for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; + i++) +- spec->init_verbs[spec->num_init_verbs++] = +- preset->init_verbs[i]; ++ add_verb(spec, preset->init_verbs[i]); + + spec->channel_mode = preset->channel_mode; + spec->num_channel_mode = preset->num_channel_mode; +@@ -742,6 +847,7 @@ static void setup_preset(struct alc_spec *spec, + spec->multiout.num_dacs = preset->num_dacs; + spec->multiout.dac_nids = preset->dac_nids; + spec->multiout.dig_out_nid = preset->dig_out_nid; ++ spec->multiout.slave_dig_outs = preset->slave_dig_outs; + spec->multiout.hp_nid = preset->hp_nid; + + spec->num_mux_defs = preset->num_mux_defs; +@@ -853,7 +959,7 @@ static void alc_mic_automute(struct hda_codec *codec) + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + } + #else +-#define alc_mic_automute(codec) /* NOP */ ++#define alc_mic_automute(codec) do {} while(0) /* NOP */ + #endif /* disabled */ + + /* unsolicited event for HP jack sensing */ +@@ -884,7 +990,7 @@ static void alc888_coef_init(struct hda_codec *codec) + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0); + tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7); +- if ((tmp & 0xf0) == 2) ++ if ((tmp & 0xf0) == 0x20) + /* alc888S-VC */ + snd_hda_codec_read(codec, 0x20, 0, + AC_VERB_SET_PROC_COEF, 0x830); +@@ -923,8 +1029,7 @@ static void alc_subsystem_id(struct hda_codec *codec, + nid = 0x1d; + if (codec->vendor_id == 0x10ec0260) + nid = 0x17; +- ass = snd_hda_codec_read(codec, nid, 0, +- AC_VERB_GET_CONFIG_DEFAULT, 0); ++ ass = snd_hda_codec_get_pincfg(codec, nid); + if (!(ass & 1) && !(ass & 0x100000)) + return; + if ((ass >> 30) != 1) /* no physical connection */ +@@ -1098,16 +1203,226 @@ static void alc_fix_pincfg(struct hda_codec *codec, + return; + + cfg = pinfix[quirk->value]; +- for (; cfg->nid; cfg++) { +- int i; +- u32 val = cfg->val; +- for (i = 0; i < 4; i++) { +- snd_hda_codec_write(codec, cfg->nid, 0, +- AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i, +- val & 0xff); +- val >>= 8; +- } ++ for (; cfg->nid; cfg++) ++ snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); ++} ++ ++/* ++ * ALC888 ++ */ ++ ++/* ++ * 2ch mode ++ */ ++static struct hda_verb alc888_4ST_ch2_intel_init[] = { ++/* Mic-in jack as mic in */ ++ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, ++ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, ++/* Line-in jack as Line in */ ++ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, ++ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, ++/* Line-Out as Front */ ++ { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, ++ { } /* end */ ++}; ++ ++/* ++ * 4ch mode ++ */ ++static struct hda_verb alc888_4ST_ch4_intel_init[] = { ++/* Mic-in jack as mic in */ ++ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, ++ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, ++/* Line-in jack as Surround */ ++ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, ++ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, ++/* Line-Out as Front */ ++ { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, ++ { } /* end */ ++}; ++ ++/* ++ * 6ch mode ++ */ ++static struct hda_verb alc888_4ST_ch6_intel_init[] = { ++/* Mic-in jack as CLFE */ ++ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, ++ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, ++/* Line-in jack as Surround */ ++ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, ++ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, ++/* Line-Out as CLFE (workaround because Mic-in is not loud enough) */ ++ { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, ++ { } /* end */ ++}; ++ ++/* ++ * 8ch mode ++ */ ++static struct hda_verb alc888_4ST_ch8_intel_init[] = { ++/* Mic-in jack as CLFE */ ++ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, ++ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, ++/* Line-in jack as Surround */ ++ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, ++ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, ++/* Line-Out as Side */ ++ { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, ++ { } /* end */ ++}; ++ ++static struct hda_channel_mode alc888_4ST_8ch_intel_modes[4] = { ++ { 2, alc888_4ST_ch2_intel_init }, ++ { 4, alc888_4ST_ch4_intel_init }, ++ { 6, alc888_4ST_ch6_intel_init }, ++ { 8, alc888_4ST_ch8_intel_init }, ++}; ++ ++/* ++ * ALC888 Fujitsu Siemens Amillo xa3530 ++ */ ++ ++static struct hda_verb alc888_fujitsu_xa3530_verbs[] = { ++/* Front Mic: set to PIN_IN (empty by default) */ ++ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, ++/* Connect Internal HP to Front */ ++ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, ++ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, ++/* Connect Bass HP to Front */ ++ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, ++ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, ++/* Connect Line-Out side jack (SPDIF) to Side */ ++ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, ++ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, ++/* Connect Mic jack to CLFE */ ++ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, ++ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, ++/* Connect Line-in jack to Surround */ ++ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, ++ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, ++/* Connect HP out jack to Front */ ++ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, ++ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, ++/* Enable unsolicited event for HP jack and Line-out jack */ ++ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, ++ {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, ++ {} ++}; ++ ++static void alc888_fujitsu_xa3530_automute(struct hda_codec *codec) ++{ ++ unsigned int present; ++ unsigned int bits; ++ /* Line out presence */ ++ present = snd_hda_codec_read(codec, 0x17, 0, ++ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; ++ /* HP out presence */ ++ present = present || snd_hda_codec_read(codec, 0x1b, 0, ++ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; ++ bits = present ? HDA_AMP_MUTE : 0; ++ /* Toggle internal speakers muting */ ++ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, ++ HDA_AMP_MUTE, bits); ++ /* Toggle internal bass muting */ ++ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, ++ HDA_AMP_MUTE, bits); ++} ++ ++static void alc888_fujitsu_xa3530_unsol_event(struct hda_codec *codec, ++ unsigned int res) ++{ ++ if (res >> 26 == ALC880_HP_EVENT) ++ alc888_fujitsu_xa3530_automute(codec); ++} ++ ++ ++/* ++ * ALC888 Acer Aspire 4930G model ++ */ ++ ++static struct hda_verb alc888_acer_aspire_4930g_verbs[] = { ++/* Front Mic: set to PIN_IN (empty by default) */ ++ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, ++/* Unselect Front Mic by default in input mixer 3 */ ++ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, ++/* Enable unsolicited event for HP jack */ ++ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, ++/* Connect Internal HP to front */ ++ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, ++ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, ++/* Connect HP out to front */ ++ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, ++ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, ++ { } ++}; ++ ++static struct hda_input_mux alc888_2_capture_sources[2] = { ++ /* Front mic only available on one ADC */ ++ { ++ .num_items = 4, ++ .items = { ++ { "Mic", 0x0 }, ++ { "Line", 0x2 }, ++ { "CD", 0x4 }, ++ { "Front Mic", 0xb }, ++ }, ++ }, ++ { ++ .num_items = 3, ++ .items = { ++ { "Mic", 0x0 }, ++ { "Line", 0x2 }, ++ { "CD", 0x4 }, ++ }, + } ++}; ++ ++static struct snd_kcontrol_new alc888_base_mixer[] = { ++ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), ++ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), ++ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), ++ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), ++ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, ++ HDA_OUTPUT), ++ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), ++ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), ++ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), ++ HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), ++ HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), ++ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), ++ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), ++ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), ++ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), ++ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), ++ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), ++ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), ++ { } /* end */ ++}; ++ ++static void alc888_acer_aspire_4930g_automute(struct hda_codec *codec) ++{ ++ unsigned int present; ++ unsigned int bits; ++ present = snd_hda_codec_read(codec, 0x15, 0, ++ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; ++ bits = present ? HDA_AMP_MUTE : 0; ++ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, ++ HDA_AMP_MUTE, bits); ++} ++ ++static void alc888_acer_aspire_4930g_unsol_event(struct hda_codec *codec, ++ unsigned int res) ++{ ++ if (res >> 26 == ALC880_HP_EVENT) ++ alc888_acer_aspire_4930g_automute(codec); + } + + /* +@@ -1195,8 +1510,6 @@ static struct snd_kcontrol_new alc880_three_stack_mixer[] = { + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +@@ -1209,50 +1522,141 @@ static struct snd_kcontrol_new alc880_three_stack_mixer[] = { + }; + + /* capture mixer elements */ +-static struct snd_kcontrol_new alc880_capture_mixer[] = { +- HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 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 = 3, +- .info = alc_mux_enum_info, +- .get = alc_mux_enum_get, +- .put = alc_mux_enum_put, +- }, +- { } /* end */ +-}; ++static int alc_cap_vol_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); ++ struct alc_spec *spec = codec->spec; ++ int err; + +-/* capture mixer elements (in case NID 0x07 not available) */ +-static struct snd_kcontrol_new alc880_capture_alt_mixer[] = { +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* The multiple "Capture Source" controls confuse alsamixer +- * So call somewhat different.. +- */ +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc_mux_enum_info, +- .get = alc_mux_enum_get, +- .put = alc_mux_enum_put, +- }, +- { } /* end */ +-}; ++ mutex_lock(&codec->control_mutex); ++ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, ++ HDA_INPUT); ++ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); ++ mutex_unlock(&codec->control_mutex); ++ return err; ++} ++ ++static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, ++ unsigned int size, unsigned int __user *tlv) ++{ ++ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); ++ struct alc_spec *spec = codec->spec; ++ int err; ++ ++ mutex_lock(&codec->control_mutex); ++ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, ++ HDA_INPUT); ++ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); ++ mutex_unlock(&codec->control_mutex); ++ return err; ++} ++ ++typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol); ++ ++static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol, ++ getput_call_t func) ++{ ++ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); ++ struct alc_spec *spec = codec->spec; ++ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); ++ int err; ++ ++ mutex_lock(&codec->control_mutex); ++ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[adc_idx], ++ 3, 0, HDA_INPUT); ++ err = func(kcontrol, ucontrol); ++ mutex_unlock(&codec->control_mutex); ++ return err; ++} ++ ++static int alc_cap_vol_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ return alc_cap_getput_caller(kcontrol, ucontrol, ++ snd_hda_mixer_amp_volume_get); ++} ++ ++static int alc_cap_vol_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ return alc_cap_getput_caller(kcontrol, ucontrol, ++ snd_hda_mixer_amp_volume_put); ++} ++ ++/* capture mixer elements */ ++#define alc_cap_sw_info snd_ctl_boolean_stereo_info ++ ++static int alc_cap_sw_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ return alc_cap_getput_caller(kcontrol, ucontrol, ++ snd_hda_mixer_amp_switch_get); ++} ++ ++static int alc_cap_sw_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ return alc_cap_getput_caller(kcontrol, ucontrol, ++ snd_hda_mixer_amp_switch_put); ++} ++ ++#define _DEFINE_CAPMIX(num) \ ++ { \ ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ ++ .name = "Capture Switch", \ ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ ++ .count = num, \ ++ .info = alc_cap_sw_info, \ ++ .get = alc_cap_sw_get, \ ++ .put = alc_cap_sw_put, \ ++ }, \ ++ { \ ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ ++ .name = "Capture Volume", \ ++ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \ ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ ++ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \ ++ .count = num, \ ++ .info = alc_cap_vol_info, \ ++ .get = alc_cap_vol_get, \ ++ .put = alc_cap_vol_put, \ ++ .tlv = { .c = alc_cap_vol_tlv }, \ ++ } ++ ++#define _DEFINE_CAPSRC(num) \ ++ { \ ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ ++ /* .name = "Capture Source", */ \ ++ .name = "Input Source", \ ++ .count = num, \ ++ .info = alc_mux_enum_info, \ ++ .get = alc_mux_enum_get, \ ++ .put = alc_mux_enum_put, \ ++ } + ++#define DEFINE_CAPMIX(num) \ ++static struct snd_kcontrol_new alc_capture_mixer ## num[] = { \ ++ _DEFINE_CAPMIX(num), \ ++ _DEFINE_CAPSRC(num), \ ++ { } /* end */ \ ++} + ++#define DEFINE_CAPMIX_NOSRC(num) \ ++static struct snd_kcontrol_new alc_capture_mixer_nosrc ## num[] = { \ ++ _DEFINE_CAPMIX(num), \ ++ { } /* end */ \ ++} ++ ++/* up to three ADCs */ ++DEFINE_CAPMIX(1); ++DEFINE_CAPMIX(2); ++DEFINE_CAPMIX(3); ++DEFINE_CAPMIX_NOSRC(1); ++DEFINE_CAPMIX_NOSRC(2); ++DEFINE_CAPMIX_NOSRC(3); + + /* + * ALC880 5-stack model +@@ -1341,8 +1745,6 @@ static struct snd_kcontrol_new alc880_six_stack_mixer[] = { + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", +@@ -1519,13 +1921,6 @@ static struct snd_kcontrol_new alc880_asus_w1v_mixer[] = { + { } /* end */ + }; + +-/* additional mixers to alc880_asus_mixer */ +-static struct snd_kcontrol_new alc880_pcbeep_mixer[] = { +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), +- { } /* end */ +-}; +- + /* TCL S700 */ + static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), +@@ -1537,18 +1932,6 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = { + HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* The multiple "Capture Source" controls confuse alsamixer +- * So call somewhat different.. +- */ +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 1, +- .info = alc_mux_enum_info, +- .get = alc_mux_enum_get, +- .put = alc_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -1570,8 +1953,6 @@ static struct snd_kcontrol_new alc880_uniwill_mixer[] = { + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", +@@ -1643,6 +2024,16 @@ static const char *alc_slave_sws[] = { + /* + * build control elements + */ ++ ++static void alc_free_kctls(struct hda_codec *codec); ++ ++/* 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), ++ { } /* end */ ++}; ++ + static int alc_build_controls(struct hda_codec *codec) + { + struct alc_spec *spec = codec->spec; +@@ -1654,17 +2045,23 @@ static int alc_build_controls(struct hda_codec *codec) + if (err < 0) + return err; + } +- ++ if (spec->cap_mixer) { ++ err = snd_hda_add_new_ctls(codec, spec->cap_mixer); ++ if (err < 0) ++ return err; ++ } + if (spec->multiout.dig_out_nid) { + err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid); + if (err < 0) + return err; +- err = snd_hda_create_spdif_share_sw(codec, +- &spec->multiout); +- if (err < 0) +- return err; +- spec->multiout.share_spdif = 1; ++ if (!spec->no_analog) { ++ err = snd_hda_create_spdif_share_sw(codec, ++ &spec->multiout); ++ if (err < 0) ++ return err; ++ spec->multiout.share_spdif = 1; ++ } + } + if (spec->dig_in_nid) { + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); +@@ -1672,8 +2069,24 @@ static int alc_build_controls(struct hda_codec *codec) + return err; + } + ++ /* create beep controls if needed */ ++ if (spec->beep_amp) { ++ struct snd_kcontrol_new *knew; ++ for (knew = alc_beep_mixer; knew->name; knew++) { ++ struct snd_kcontrol *kctl; ++ kctl = snd_ctl_new1(knew, codec); ++ if (!kctl) ++ return -ENOMEM; ++ kctl->private_value = spec->beep_amp; ++ err = snd_hda_ctl_add(codec, kctl); ++ if (err < 0) ++ return err; ++ } ++ } ++ + /* if we have no master control, let's create it */ +- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { ++ if (!spec->no_analog && ++ !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + unsigned int vmaster_tlv[4]; + snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, + HDA_OUTPUT, vmaster_tlv); +@@ -1682,13 +2095,15 @@ static int alc_build_controls(struct hda_codec *codec) + if (err < 0) + return err; + } +- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { ++ if (!spec->no_analog && ++ !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { + err = snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, alc_slave_sws); + if (err < 0) + return err; + } + ++ alc_free_kctls(codec); /* no longer needed */ + return 0; + } + +@@ -2590,6 +3005,14 @@ static int alc880_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + stream_tag, format, substream); + } + ++static int alc880_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, ++ struct hda_codec *codec, ++ struct snd_pcm_substream *substream) ++{ ++ struct alc_spec *spec = codec->spec; ++ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); ++} ++ + static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +@@ -2673,7 +3096,8 @@ static struct hda_pcm_stream alc880_pcm_digital_playback = { + .ops = { + .open = alc880_dig_playback_pcm_open, + .close = alc880_dig_playback_pcm_close, +- .prepare = alc880_dig_playback_pcm_prepare ++ .prepare = alc880_dig_playback_pcm_prepare, ++ .cleanup = alc880_dig_playback_pcm_cleanup + }, + }; + +@@ -2700,14 +3124,19 @@ static int alc_build_pcms(struct hda_codec *codec) + codec->num_pcms = 1; + codec->pcm_info = info; + ++ if (spec->no_analog) ++ goto skip_analog; ++ + info->name = spec->stream_name_analog; + if (spec->stream_analog_playback) { +- snd_assert(spec->multiout.dac_nids, return -EINVAL); ++ if (snd_BUG_ON(!spec->multiout.dac_nids)) ++ return -EINVAL; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback); + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + } + if (spec->stream_analog_capture) { +- snd_assert(spec->adc_nids, return -EINVAL); ++ if (snd_BUG_ON(!spec->adc_nids)) ++ return -EINVAL; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; + } +@@ -2721,12 +3150,17 @@ static int alc_build_pcms(struct hda_codec *codec) + } + } + ++ skip_analog: + /* SPDIF for stream index #1 */ + if (spec->multiout.dig_out_nid || spec->dig_in_nid) { + codec->num_pcms = 2; ++ codec->slave_dig_outs = spec->multiout.slave_dig_outs; + info = spec->pcm_rec + 1; + info->name = spec->stream_name_digital; +- info->pcm_type = HDA_PCM_TYPE_SPDIF; ++ if (spec->dig_out_type) ++ info->pcm_type = spec->dig_out_type; ++ else ++ info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->multiout.dig_out_nid && + spec->stream_digital_playback) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback); +@@ -2741,6 +3175,9 @@ static int alc_build_pcms(struct hda_codec *codec) + codec->spdif_status_reset = 1; + } + ++ if (spec->no_analog) ++ return 0; ++ + /* If the use of more than one ADC is requested for the current + * model, configure a second analog capture-only PCM. + */ +@@ -2777,79 +3214,39 @@ static int alc_build_pcms(struct hda_codec *codec) + return 0; + } + +-static void alc_free(struct hda_codec *codec) ++static void alc_free_kctls(struct hda_codec *codec) + { + struct alc_spec *spec = codec->spec; +- unsigned int i; + +- if (!spec) +- return; +- +- if (spec->kctl_alloc) { +- for (i = 0; i < spec->num_kctl_used; i++) +- kfree(spec->kctl_alloc[i].name); +- kfree(spec->kctl_alloc); ++ if (spec->kctls.list) { ++ struct snd_kcontrol_new *kctl = spec->kctls.list; ++ int i; ++ for (i = 0; i < spec->kctls.used; i++) ++ kfree(kctl[i].name); + } +- kfree(spec); +- codec->spec = NULL; /* to be sure */ ++ snd_array_free(&spec->kctls); + } + +-#ifdef SND_HDA_NEEDS_RESUME +-static void store_pin_configs(struct hda_codec *codec) ++static void alc_free(struct hda_codec *codec) + { + struct alc_spec *spec = codec->spec; +- hda_nid_t nid, end_nid; +- +- end_nid = codec->start_nid + codec->num_nodes; +- for (nid = codec->start_nid; nid < end_nid; nid++) { +- unsigned int wid_caps = get_wcaps(codec, nid); +- unsigned int wid_type = +- (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; +- if (wid_type != AC_WID_PIN) +- continue; +- if (spec->num_pins >= ARRAY_SIZE(spec->pin_nids)) +- break; +- spec->pin_nids[spec->num_pins] = nid; +- spec->pin_cfgs[spec->num_pins] = +- snd_hda_codec_read(codec, nid, 0, +- AC_VERB_GET_CONFIG_DEFAULT, 0); +- spec->num_pins++; +- } +-} + +-static void resume_pin_configs(struct hda_codec *codec) +-{ +- struct alc_spec *spec = codec->spec; +- int i; ++ if (!spec) ++ return; + +- for (i = 0; i < spec->num_pins; i++) { +- hda_nid_t pin_nid = spec->pin_nids[i]; +- unsigned int pin_config = spec->pin_cfgs[i]; +- snd_hda_codec_write(codec, pin_nid, 0, +- AC_VERB_SET_CONFIG_DEFAULT_BYTES_0, +- pin_config & 0x000000ff); +- snd_hda_codec_write(codec, pin_nid, 0, +- AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, +- (pin_config & 0x0000ff00) >> 8); +- snd_hda_codec_write(codec, pin_nid, 0, +- AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, +- (pin_config & 0x00ff0000) >> 16); +- snd_hda_codec_write(codec, pin_nid, 0, +- AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, +- pin_config >> 24); +- } ++ alc_free_kctls(codec); ++ kfree(spec); ++ snd_hda_detach_beep_device(codec); + } + ++#ifdef SND_HDA_NEEDS_RESUME + static int alc_resume(struct hda_codec *codec) + { +- resume_pin_configs(codec); + codec->patch_ops.init(codec); + snd_hda_codec_resume_amp(codec); + snd_hda_codec_resume_cache(codec); + return 0; + } +-#else +-#define store_pin_configs(codec) + #endif + + /* +@@ -3188,7 +3585,7 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG), + SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST), + SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST), +- SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS), /* default ASUS */ ++ SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_ASUS), /* default ASUS */ + SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST), + SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST), + SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST), +@@ -3231,7 +3628,8 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = { + SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG), +- SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST), /* default Intel */ ++ /* default Intel */ ++ SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_3ST), + SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG), + SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG), + {} +@@ -3271,6 +3669,8 @@ static struct alc_config_preset alc880_presets[] = { + alc880_gpio2_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), + .dac_nids = alc880_dac_nids, ++ .adc_nids = alc880_adc_nids_alt, /* FIXME: correct? */ ++ .num_adc_nids = 1, /* single ADC */ + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), + .channel_mode = alc880_2_jack_modes, +@@ -3409,7 +3809,7 @@ static struct alc_config_preset alc880_presets[] = { + .input_mux = &alc880_capture_source, + }, + [ALC880_UNIWILL_DIG] = { +- .mixers = { alc880_asus_mixer, alc880_pcbeep_mixer }, ++ .mixers = { alc880_asus_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_pin_asus_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), +@@ -3447,8 +3847,7 @@ static struct alc_config_preset alc880_presets[] = { + .init_hook = alc880_uniwill_p53_hp_automute, + }, + [ALC880_FUJITSU] = { +- .mixers = { alc880_fujitsu_mixer, +- alc880_pcbeep_mixer, }, ++ .mixers = { alc880_fujitsu_mixer }, + .init_verbs = { alc880_volume_init_verbs, + alc880_uniwill_p53_init_verbs, + alc880_beep_init_verbs }, +@@ -3535,9 +3934,6 @@ static struct alc_config_preset alc880_presets[] = { + * Automatic parse of I/O pins from the BIOS configuration + */ + +-#define NUM_CONTROL_ALLOC 32 +-#define NUM_VERB_ALLOC 32 +- + enum { + ALC_CTL_WIDGET_VOL, + ALC_CTL_WIDGET_MUTE, +@@ -3555,29 +3951,15 @@ static int add_control(struct alc_spec *spec, int type, const char *name, + { + struct snd_kcontrol_new *knew; + +- if (spec->num_kctl_used >= spec->num_kctl_alloc) { +- int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; +- +- /* array + terminator */ +- knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); +- if (!knew) +- return -ENOMEM; +- if (spec->kctl_alloc) { +- memcpy(knew, spec->kctl_alloc, +- sizeof(*knew) * spec->num_kctl_alloc); +- kfree(spec->kctl_alloc); +- } +- spec->kctl_alloc = knew; +- spec->num_kctl_alloc = num; +- } +- +- knew = &spec->kctl_alloc[spec->num_kctl_used]; ++ snd_array_init(&spec->kctls, sizeof(*knew), 32); ++ knew = snd_array_new(&spec->kctls); ++ if (!knew) ++ return -ENOMEM; + *knew = alc880_control_templates[type]; + knew->name = kstrdup(name, GFP_KERNEL); + if (!knew->name) + return -ENOMEM; + knew->private_value = val; +- spec->num_kctl_used++; + return 0; + } + +@@ -3758,7 +4140,7 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, + static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec, + const struct auto_pin_cfg *cfg) + { +- struct hda_input_mux *imux = &spec->private_imux; ++ struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx; + + for (i = 0; i < AUTO_PIN_LAST; i++) { +@@ -3846,11 +4228,9 @@ static void alc880_auto_init_analog_input(struct hda_codec *codec) + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = spec->autocfg.input_pins[i]; + if (alc880_is_input_pin(nid)) { +- snd_hda_codec_write(codec, nid, 0, +- AC_VERB_SET_PIN_WIDGET_CONTROL, +- i <= AUTO_PIN_FRONT_MIC ? +- PIN_VREF80 : PIN_IN); +- if (nid != ALC880_PIN_CD_NID) ++ alc_set_input_pin(codec, nid, i); ++ if (nid != ALC880_PIN_CD_NID && ++ (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); +@@ -3865,7 +4245,7 @@ static void alc880_auto_init_analog_input(struct hda_codec *codec) + static int alc880_parse_auto_config(struct hda_codec *codec) + { + struct alc_spec *spec = codec->spec; +- int err; ++ int i, err; + static hda_nid_t alc880_ignore[] = { 0x1d, 0 }; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, +@@ -3896,20 +4276,34 @@ static int alc880_parse_auto_config(struct hda_codec *codec) + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + +- if (spec->autocfg.dig_out_pin) +- spec->multiout.dig_out_nid = ALC880_DIGOUT_NID; ++ /* check multiple SPDIF-out (for recent codecs) */ ++ for (i = 0; i < spec->autocfg.dig_outs; i++) { ++ hda_nid_t dig_nid; ++ err = snd_hda_get_connections(codec, ++ spec->autocfg.dig_out_pins[i], ++ &dig_nid, 1); ++ if (err < 0) ++ continue; ++ if (!i) ++ spec->multiout.dig_out_nid = dig_nid; ++ else { ++ spec->multiout.slave_dig_outs = spec->slave_dig_outs; ++ spec->slave_dig_outs[i - 1] = dig_nid; ++ if (i == ARRAY_SIZE(spec->slave_dig_outs) - 1) ++ break; ++ } ++ } + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = ALC880_DIGIN_NID; + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; ++ if (spec->kctls.list) ++ add_mixer(spec, spec->kctls.list); + +- spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs; ++ add_verb(spec, alc880_volume_init_verbs); + + spec->num_mux_defs = 1; +- spec->input_mux = &spec->private_imux; ++ spec->input_mux = &spec->private_imux[0]; + +- store_pin_configs(codec); + return 1; + } + +@@ -3924,6 +4318,29 @@ static void alc880_auto_init(struct hda_codec *codec) + alc_inithook(codec); + } + ++static void set_capture_mixer(struct alc_spec *spec) ++{ ++ static struct snd_kcontrol_new *caps[2][3] = { ++ { alc_capture_mixer_nosrc1, ++ alc_capture_mixer_nosrc2, ++ alc_capture_mixer_nosrc3 }, ++ { alc_capture_mixer1, ++ alc_capture_mixer2, ++ alc_capture_mixer3 }, ++ }; ++ if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) { ++ int mux; ++ if (spec->input_mux && spec->input_mux->num_items > 1) ++ mux = 1; ++ else ++ mux = 0; ++ spec->cap_mixer = caps[mux][spec->num_adc_nids - 1]; ++ } ++} ++ ++#define set_beep_amp(spec, nid, idx, dir) \ ++ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir)) ++ + /* + * OK, here we have finally the patch for ALC880 + */ +@@ -3963,6 +4380,12 @@ static int patch_alc880(struct hda_codec *codec) + } + } + ++ err = snd_hda_attach_beep_device(codec, 0x1); ++ if (err < 0) { ++ alc_free(codec); ++ return err; ++ } ++ + if (board_config != ALC880_AUTO) + setup_preset(spec, &alc880_presets[board_config]); + +@@ -3983,16 +4406,13 @@ static int patch_alc880(struct hda_codec *codec) + if (wcap != AC_WID_AUD_IN) { + spec->adc_nids = alc880_adc_nids_alt; + spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt); +- spec->mixers[spec->num_mixers] = +- alc880_capture_alt_mixer; +- spec->num_mixers++; + } else { + spec->adc_nids = alc880_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids); +- spec->mixers[spec->num_mixers] = alc880_capture_mixer; +- spec->num_mixers++; + } + } ++ set_capture_mixer(spec); ++ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + + spec->vmaster_nid = 0x0c; + +@@ -4003,6 +4423,7 @@ static int patch_alc880(struct hda_codec *codec) + if (!spec->loopback.amplist) + spec->loopback.amplist = alc880_loopbacks; + #endif ++ codec->proc_widget_hook = print_realtek_coef; + + return 0; + } +@@ -4027,11 +4448,6 @@ static hda_nid_t alc260_adc_nids_alt[1] = { + 0x05, + }; + +-static hda_nid_t alc260_hp_adc_nids[2] = { +- /* ADC1, 0 */ +- 0x05, 0x04 +-}; +- + /* NIDs used when simultaneous access to both ADCs makes sense. Note that + * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC. + */ +@@ -4104,6 +4520,26 @@ static struct hda_input_mux alc260_acer_capture_sources[2] = { + }, + }, + }; ++ ++/* Maxdata Favorit 100XS */ ++static struct hda_input_mux alc260_favorit100_capture_sources[2] = { ++ { ++ .num_items = 2, ++ .items = { ++ { "Line/Mic", 0x0 }, ++ { "CD", 0x4 }, ++ }, ++ }, ++ { ++ .num_items = 3, ++ .items = { ++ { "Line/Mic", 0x0 }, ++ { "CD", 0x4 }, ++ { "Mixer", 0x5 }, ++ }, ++ }, ++}; ++ + /* + * This is just place-holder, so there's something for alc_build_pcms to look + * at when it calculates the maximum number of channels. ALC260 has no mixer +@@ -4146,12 +4582,6 @@ static struct snd_kcontrol_new alc260_input_mixer[] = { + { } /* end */ + }; + +-static struct snd_kcontrol_new alc260_pc_beep_mixer[] = { +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x07, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x07, 0x05, HDA_INPUT), +- { } /* end */ +-}; +- + /* update HP, line and mono out pins according to the master switch */ + static void alc260_hp_master_update(struct hda_codec *codec, + hda_nid_t hp, hda_nid_t line, +@@ -4343,8 +4773,6 @@ static struct snd_kcontrol_new alc260_fujitsu_mixer[] = { + HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT), + ALC_PIN_MODE("Mic/Line Jack Mode", 0x12, ALC_PIN_DIR_IN), +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x09, 2, HDA_INPUT), + { } /* end */ +@@ -4389,8 +4817,18 @@ static struct snd_kcontrol_new alc260_acer_mixer[] = { + HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), + ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT), ++ { } /* end */ ++}; ++ ++/* Maxdata Favorit 100XS: one output and one input (0x12) jack ++ */ ++static struct snd_kcontrol_new alc260_favorit100_mixer[] = { ++ HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), ++ HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT), ++ ALC_PIN_MODE("Output Jack Mode", 0x0f, ALC_PIN_DIR_INOUT), ++ HDA_CODEC_VOLUME("Line/Mic Playback Volume", 0x07, 0x0, HDA_INPUT), ++ HDA_CODEC_MUTE("Line/Mic Playback Switch", 0x07, 0x0, HDA_INPUT), ++ ALC_PIN_MODE("Line/Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), + { } /* end */ + }; + +@@ -4408,8 +4846,6 @@ static struct snd_kcontrol_new alc260_will_mixer[] = { + ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT), + { } /* end */ + }; + +@@ -4430,45 +4866,6 @@ static struct snd_kcontrol_new alc260_replacer_672v_mixer[] = { + { } /* end */ + }; + +-/* capture mixer elements */ +-static struct snd_kcontrol_new alc260_capture_mixer[] = { +- HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x05, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x05, 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 = alc_mux_enum_info, +- .get = alc_mux_enum_get, +- .put = alc_mux_enum_put, +- }, +- { } /* end */ +-}; +- +-static struct snd_kcontrol_new alc260_capture_alt_mixer[] = { +- HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x05, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* The multiple "Capture Source" controls confuse alsamixer +- * So call somewhat different.. +- */ +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 1, +- .info = alc_mux_enum_info, +- .get = alc_mux_enum_get, +- .put = alc_mux_enum_put, +- }, +- { } /* end */ +-}; +- + /* + * initialization verbs + */ +@@ -4806,6 +5203,89 @@ static struct hda_verb alc260_acer_init_verbs[] = { + { } + }; + ++/* Initialisation sequence for Maxdata Favorit 100XS ++ * (adapted from Acer init verbs). ++ */ ++static struct hda_verb alc260_favorit100_init_verbs[] = { ++ /* GPIO 0 enables the output jack. ++ * Turn this on and rely on the standard mute ++ * methods whenever the user wants to turn these outputs off. ++ */ ++ {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, ++ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, ++ {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, ++ /* Line/Mic input jack is connected to Mic1 pin */ ++ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, ++ /* Ensure all other unused pins are disabled and muted. */ ++ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, ++ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, ++ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, ++ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, ++ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, ++ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ /* Disable digital (SPDIF) pins */ ++ {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0}, ++ {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, ++ ++ /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum ++ * bus when acting as outputs. ++ */ ++ {0x0b, AC_VERB_SET_CONNECT_SEL, 0}, ++ {0x0d, AC_VERB_SET_CONNECT_SEL, 0}, ++ ++ /* Start with output sum widgets muted and their output gains at min */ ++ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, ++ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, ++ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, ++ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, ++ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, ++ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, ++ ++ /* Unmute Line-out pin widget amp left and right ++ * (no equiv mixer ctrl) ++ */ ++ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ /* Unmute Mic1 and Line1 pin widget input buffers since they start as ++ * inputs. If the pin mode is changed by the user the pin mode control ++ * will take care of enabling the pin's input/output buffers as needed. ++ * Therefore there's no need to enable the input buffer at this ++ * stage. ++ */ ++ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, ++ ++ /* Mute capture amp left and right */ ++ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ /* Set ADC connection select to match default mixer setting - mic ++ * (on mic1 pin) ++ */ ++ {0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, ++ ++ /* Do similar with the second ADC: mute capture input amp and ++ * set ADC connection to mic to match ALSA's default state. ++ */ ++ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ {0x05, AC_VERB_SET_CONNECT_SEL, 0x00}, ++ ++ /* Mute all inputs to mixer widget (even unconnected ones) */ ++ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */ ++ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */ ++ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */ ++ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */ ++ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */ ++ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */ ++ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */ ++ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */ ++ ++ { } ++}; ++ + static struct hda_verb alc260_will_verbs[] = { + {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x0b, AC_VERB_SET_CONNECT_SEL, 0x00}, +@@ -4952,8 +5432,6 @@ static struct snd_kcontrol_new alc260_test_mixer[] = { + HDA_CODEC_MUTE("LINE2 Playback Switch", 0x07, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("LINE-OUT loopback Playback Volume", 0x07, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("LINE-OUT loopback Playback Switch", 0x07, 0x06, HDA_INPUT), + HDA_CODEC_VOLUME("HP-OUT loopback Playback Volume", 0x07, 0x7, HDA_INPUT), +@@ -5151,7 +5629,7 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec, + static int alc260_auto_create_analog_input_ctls(struct alc_spec *spec, + const struct auto_pin_cfg *cfg) + { +- struct hda_input_mux *imux = &spec->private_imux; ++ struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx; + + for (i = 0; i < AUTO_PIN_LAST; i++) { +@@ -5226,11 +5704,9 @@ static void alc260_auto_init_analog_input(struct hda_codec *codec) + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = spec->autocfg.input_pins[i]; + if (nid >= 0x12) { +- snd_hda_codec_write(codec, nid, 0, +- AC_VERB_SET_PIN_WIDGET_CONTROL, +- i <= AUTO_PIN_FRONT_MIC ? +- PIN_VREF80 : PIN_IN); +- if (nid != ALC260_PIN_CD_NID) ++ alc_set_input_pin(codec, nid, i); ++ if (nid != ALC260_PIN_CD_NID && ++ (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); +@@ -5285,7 +5761,6 @@ static struct hda_verb alc260_volume_init_verbs[] = { + static int alc260_parse_auto_config(struct hda_codec *codec) + { + struct alc_spec *spec = codec->spec; +- unsigned int wcap; + int err; + static hda_nid_t alc260_ignore[] = { 0x17, 0 }; + +@@ -5296,7 +5771,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec) + err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; +- if (!spec->kctl_alloc) ++ if (!spec->kctls.list) + return 0; /* can't find valid BIOS pin config */ + err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) +@@ -5304,31 +5779,16 @@ static int alc260_parse_auto_config(struct hda_codec *codec) + + spec->multiout.max_channels = 2; + +- if (spec->autocfg.dig_out_pin) ++ if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = ALC260_DIGOUT_NID; +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; ++ if (spec->kctls.list) ++ add_mixer(spec, spec->kctls.list); + +- spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs; ++ add_verb(spec, alc260_volume_init_verbs); + + spec->num_mux_defs = 1; +- spec->input_mux = &spec->private_imux; +- +- /* check whether NID 0x04 is valid */ +- wcap = get_wcaps(codec, 0x04); +- wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */ +- if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { +- spec->adc_nids = alc260_adc_nids_alt; +- spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt); +- spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer; +- } else { +- spec->adc_nids = alc260_adc_nids; +- spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids); +- spec->mixers[spec->num_mixers] = alc260_capture_mixer; +- } +- spec->num_mixers++; ++ spec->input_mux = &spec->private_imux[0]; + +- store_pin_configs(codec); + return 1; + } + +@@ -5365,6 +5825,7 @@ static const char *alc260_models[ALC260_MODEL_LAST] = { + [ALC260_ACER] = "acer", + [ALC260_WILL] = "will", + [ALC260_REPLACER_672V] = "replacer", ++ [ALC260_FAVORIT100] = "favorit100", + #ifdef CONFIG_SND_DEBUG + [ALC260_TEST] = "test", + #endif +@@ -5374,6 +5835,7 @@ static const char *alc260_models[ALC260_MODEL_LAST] = { + static struct snd_pci_quirk alc260_cfg_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_ACER), + SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER), ++ SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FAVORIT100), + SND_PCI_QUIRK(0x103c, 0x2808, "HP d5700", ALC260_HP_3013), + SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_HP_3013), + SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013), +@@ -5396,13 +5858,11 @@ static struct snd_pci_quirk alc260_cfg_tbl[] = { + static struct alc_config_preset alc260_presets[] = { + [ALC260_BASIC] = { + .mixers = { alc260_base_output_mixer, +- alc260_input_mixer, +- alc260_pc_beep_mixer, +- alc260_capture_mixer }, ++ alc260_input_mixer }, + .init_verbs = { alc260_init_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, +- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), ++ .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), + .adc_nids = alc260_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, +@@ -5410,14 +5870,13 @@ static struct alc_config_preset alc260_presets[] = { + }, + [ALC260_HP] = { + .mixers = { alc260_hp_output_mixer, +- alc260_input_mixer, +- alc260_capture_alt_mixer }, ++ alc260_input_mixer }, + .init_verbs = { alc260_init_verbs, + alc260_hp_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, +- .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), +- .adc_nids = alc260_hp_adc_nids, ++ .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), ++ .adc_nids = alc260_adc_nids_alt, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .input_mux = &alc260_capture_source, +@@ -5426,14 +5885,13 @@ static struct alc_config_preset alc260_presets[] = { + }, + [ALC260_HP_DC7600] = { + .mixers = { alc260_hp_dc7600_mixer, +- alc260_input_mixer, +- alc260_capture_alt_mixer }, ++ alc260_input_mixer }, + .init_verbs = { alc260_init_verbs, + alc260_hp_dc7600_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, +- .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), +- .adc_nids = alc260_hp_adc_nids, ++ .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), ++ .adc_nids = alc260_adc_nids_alt, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .input_mux = &alc260_capture_source, +@@ -5442,14 +5900,13 @@ static struct alc_config_preset alc260_presets[] = { + }, + [ALC260_HP_3013] = { + .mixers = { alc260_hp_3013_mixer, +- alc260_input_mixer, +- alc260_capture_alt_mixer }, ++ alc260_input_mixer }, + .init_verbs = { alc260_hp_3013_init_verbs, + alc260_hp_3013_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, +- .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), +- .adc_nids = alc260_hp_adc_nids, ++ .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), ++ .adc_nids = alc260_adc_nids_alt, + .num_channel_mode = ARRAY_SIZE(alc260_modes), + .channel_mode = alc260_modes, + .input_mux = &alc260_capture_source, +@@ -5457,8 +5914,7 @@ static struct alc_config_preset alc260_presets[] = { + .init_hook = alc260_hp_3013_automute, + }, + [ALC260_FUJITSU_S702X] = { +- .mixers = { alc260_fujitsu_mixer, +- alc260_capture_mixer }, ++ .mixers = { alc260_fujitsu_mixer }, + .init_verbs = { alc260_fujitsu_init_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, +@@ -5470,8 +5926,7 @@ static struct alc_config_preset alc260_presets[] = { + .input_mux = alc260_fujitsu_capture_sources, + }, + [ALC260_ACER] = { +- .mixers = { alc260_acer_mixer, +- alc260_capture_mixer }, ++ .mixers = { alc260_acer_mixer }, + .init_verbs = { alc260_acer_init_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, +@@ -5482,9 +5937,20 @@ static struct alc_config_preset alc260_presets[] = { + .num_mux_defs = ARRAY_SIZE(alc260_acer_capture_sources), + .input_mux = alc260_acer_capture_sources, + }, ++ [ALC260_FAVORIT100] = { ++ .mixers = { alc260_favorit100_mixer }, ++ .init_verbs = { alc260_favorit100_init_verbs }, ++ .num_dacs = ARRAY_SIZE(alc260_dac_nids), ++ .dac_nids = alc260_dac_nids, ++ .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), ++ .adc_nids = alc260_dual_adc_nids, ++ .num_channel_mode = ARRAY_SIZE(alc260_modes), ++ .channel_mode = alc260_modes, ++ .num_mux_defs = ARRAY_SIZE(alc260_favorit100_capture_sources), ++ .input_mux = alc260_favorit100_capture_sources, ++ }, + [ALC260_WILL] = { +- .mixers = { alc260_will_mixer, +- alc260_capture_mixer }, ++ .mixers = { alc260_will_mixer }, + .init_verbs = { alc260_init_verbs, alc260_will_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, +@@ -5496,8 +5962,7 @@ static struct alc_config_preset alc260_presets[] = { + .input_mux = &alc260_capture_source, + }, + [ALC260_REPLACER_672V] = { +- .mixers = { alc260_replacer_672v_mixer, +- alc260_capture_mixer }, ++ .mixers = { alc260_replacer_672v_mixer }, + .init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs }, + .num_dacs = ARRAY_SIZE(alc260_dac_nids), + .dac_nids = alc260_dac_nids, +@@ -5512,8 +5977,7 @@ static struct alc_config_preset alc260_presets[] = { + }, + #ifdef CONFIG_SND_DEBUG + [ALC260_TEST] = { +- .mixers = { alc260_test_mixer, +- alc260_capture_mixer }, ++ .mixers = { alc260_test_mixer }, + .init_verbs = { alc260_test_init_verbs }, + .num_dacs = ARRAY_SIZE(alc260_test_dac_nids), + .dac_nids = alc260_test_dac_nids, +@@ -5561,6 +6025,12 @@ static int patch_alc260(struct hda_codec *codec) + } + } + ++ err = snd_hda_attach_beep_device(codec, 0x1); ++ if (err < 0) { ++ alc_free(codec); ++ return err; ++ } ++ + if (board_config != ALC260_AUTO) + setup_preset(spec, &alc260_presets[board_config]); + +@@ -5572,6 +6042,22 @@ static int patch_alc260(struct hda_codec *codec) + spec->stream_digital_playback = &alc260_pcm_digital_playback; + spec->stream_digital_capture = &alc260_pcm_digital_capture; + ++ if (!spec->adc_nids && spec->input_mux) { ++ /* check whether NID 0x04 is valid */ ++ unsigned int wcap = get_wcaps(codec, 0x04); ++ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; ++ /* get type */ ++ if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { ++ spec->adc_nids = alc260_adc_nids_alt; ++ spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt); ++ } else { ++ spec->adc_nids = alc260_adc_nids; ++ spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids); ++ } ++ } ++ set_capture_mixer(spec); ++ set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); ++ + spec->vmaster_nid = 0x08; + + codec->patch_ops = alc_patch_ops; +@@ -5581,6 +6067,7 @@ static int patch_alc260(struct hda_codec *codec) + if (!spec->loopback.amplist) + spec->loopback.amplist = alc260_loopbacks; + #endif ++ codec->proc_widget_hook = print_realtek_coef; + + return 0; + } +@@ -5628,36 +6115,6 @@ static struct hda_input_mux alc882_capture_source = { + { "CD", 0x4 }, + }, + }; +-#define alc882_mux_enum_info alc_mux_enum_info +-#define alc882_mux_enum_get alc_mux_enum_get +- +-static int alc882_mux_enum_put(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +- struct alc_spec *spec = codec->spec; +- const struct hda_input_mux *imux = spec->input_mux; +- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); +- hda_nid_t nid = spec->capsrc_nids ? +- spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; +- unsigned int *cur_val = &spec->cur_mux[adc_idx]; +- unsigned int i, idx; +- +- idx = ucontrol->value.enumerated.item[0]; +- if (idx >= imux->num_items) +- idx = imux->num_items - 1; +- if (*cur_val == idx) +- return 0; +- for (i = 0; i < imux->num_items; i++) { +- unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; +- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, +- imux->items[i].index, +- HDA_AMP_MUTE, v); +- } +- *cur_val = idx; +- return 1; +-} +- + /* + * 2ch mode + */ +@@ -5771,8 +6228,6 @@ static struct snd_kcontrol_new alc882_base_mixer[] = { + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + { } /* end */ + }; + +@@ -5799,8 +6254,6 @@ static struct snd_kcontrol_new alc882_w2jc_mixer[] = { + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + { } /* end */ + }; + +@@ -5852,8 +6305,6 @@ static struct snd_kcontrol_new alc882_asus_a7m_mixer[] = { + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + { } /* end */ + }; + +@@ -5962,8 +6413,10 @@ static struct snd_kcontrol_new alc882_macpro_mixer[] = { + HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT), + 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), ++ */ + { } /* end */ + }; + +@@ -6340,49 +6793,6 @@ static struct hda_verb alc882_auto_init_verbs[] = { + { } + }; + +-/* capture mixer elements */ +-static struct snd_kcontrol_new alc882_capture_alt_mixer[] = { +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* The multiple "Capture Source" controls confuse alsamixer +- * So call somewhat different.. +- */ +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc882_mux_enum_info, +- .get = alc882_mux_enum_get, +- .put = alc882_mux_enum_put, +- }, +- { } /* end */ +-}; +- +-static struct snd_kcontrol_new alc882_capture_mixer[] = { +- HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 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 = 3, +- .info = alc882_mux_enum_info, +- .get = alc882_mux_enum_get, +- .put = alc882_mux_enum_put, +- }, +- { } /* end */ +-}; +- + #ifdef CONFIG_SND_HDA_POWER_SAVE + #define alc882_loopbacks alc880_loopbacks + #endif +@@ -6511,8 +6921,7 @@ static struct alc_config_preset alc882_presets[] = { + .init_hook = alc885_imac24_init_hook, + }, + [ALC882_TARGA] = { +- .mixers = { alc882_targa_mixer, alc882_chmode_mixer, +- alc882_capture_mixer }, ++ .mixers = { alc882_targa_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_init_verbs, alc882_targa_verbs}, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, +@@ -6528,8 +6937,7 @@ static struct alc_config_preset alc882_presets[] = { + .init_hook = alc882_targa_automute, + }, + [ALC882_ASUS_A7J] = { +- .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer, +- alc882_capture_mixer }, ++ .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer }, + .init_verbs = { alc882_init_verbs, alc882_asus_a7j_verbs}, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, +@@ -6640,19 +7048,9 @@ static void alc882_auto_init_analog_input(struct hda_codec *codec) + + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = spec->autocfg.input_pins[i]; +- unsigned int vref; + if (!nid) + continue; +- vref = PIN_IN; +- if (1 /*i <= AUTO_PIN_FRONT_MIC*/) { +- unsigned int pincap; +- pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); +- if ((pincap >> AC_PINCAP_VREF_SHIFT) & +- AC_PINCAP_VREF_80) +- vref = PIN_VREF80; +- } +- snd_hda_codec_write(codec, nid, 0, +- AC_VERB_SET_PIN_WIDGET_CONTROL, vref); ++ alc_set_input_pin(codec, nid, AUTO_PIN_FRONT_MIC /*i*/); + if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, +@@ -6663,18 +7061,21 @@ static void alc882_auto_init_analog_input(struct hda_codec *codec) + static void alc882_auto_init_input_src(struct hda_codec *codec) + { + struct alc_spec *spec = codec->spec; +- const struct hda_input_mux *imux = spec->input_mux; + int c; + + for (c = 0; c < spec->num_adc_nids; c++) { + hda_nid_t conn_list[HDA_MAX_NUM_INPUTS]; + hda_nid_t nid = spec->capsrc_nids[c]; ++ unsigned int mux_idx; ++ const struct hda_input_mux *imux; + int conns, mute, idx, item; + + conns = snd_hda_get_connections(codec, nid, conn_list, + ARRAY_SIZE(conn_list)); + if (conns < 0) + continue; ++ mux_idx = c >= spec->num_mux_defs ? 0 : c; ++ imux = &spec->input_mux[mux_idx]; + for (idx = 0; idx < conns; idx++) { + /* if the current connection is the selected one, + * unmute it as default - otherwise mute it +@@ -6687,8 +7088,20 @@ static void alc882_auto_init_input_src(struct hda_codec *codec) + break; + } + } +- snd_hda_codec_write(codec, nid, 0, +- AC_VERB_SET_AMP_GAIN_MUTE, mute); ++ /* check if we have a selector or mixer ++ * we could check for the widget type instead, but ++ * just check for Amp-In presence (in case of mixer ++ * without amp-in there is something wrong, this ++ * function shouldn't be used or capsrc nid is wrong) ++ */ ++ if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) ++ snd_hda_codec_write(codec, nid, 0, ++ AC_VERB_SET_AMP_GAIN_MUTE, ++ mute); ++ else if (mute != AMP_IN_MUTE(idx)) ++ snd_hda_codec_write(codec, nid, 0, ++ AC_VERB_SET_CONNECT_SEL, ++ idx); + } + } + } +@@ -6777,8 +7190,10 @@ static int patch_alc882(struct hda_codec *codec) + break; + case 0x106b1000: /* iMac 24 */ + case 0x106b2800: /* AppleTV */ ++ case 0x106b3e00: /* iMac 24 Aluminium */ + board_config = ALC885_IMAC24; + break; ++ case 0x106b00a0: /* MacBookPro3,1 - Another revision */ + case 0x106b00a1: /* Macbook (might be wrong - PCI SSID?) */ + case 0x106b00a4: /* MacbookPro4,1 */ + case 0x106b2c00: /* Macbook Pro rev3 */ +@@ -6815,6 +7230,12 @@ static int patch_alc882(struct hda_codec *codec) + } + } + ++ err = snd_hda_attach_beep_device(codec, 0x1); ++ if (err < 0) { ++ alc_free(codec); ++ return err; ++ } ++ + if (board_config != ALC882_AUTO) + setup_preset(spec, &alc882_presets[board_config]); + +@@ -6835,6 +7256,7 @@ static int patch_alc882(struct hda_codec *codec) + spec->stream_digital_playback = &alc882_pcm_digital_playback; + spec->stream_digital_capture = &alc882_pcm_digital_capture; + ++ spec->capture_style = CAPT_MIX; /* matrix-style capture */ + if (!spec->adc_nids && spec->input_mux) { + /* check whether NID 0x07 is valid */ + unsigned int wcap = get_wcaps(codec, 0x07); +@@ -6844,17 +7266,14 @@ static int patch_alc882(struct hda_codec *codec) + spec->adc_nids = alc882_adc_nids_alt; + spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt); + spec->capsrc_nids = alc882_capsrc_nids_alt; +- spec->mixers[spec->num_mixers] = +- alc882_capture_alt_mixer; +- spec->num_mixers++; + } else { + spec->adc_nids = alc882_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids); + spec->capsrc_nids = alc882_capsrc_nids; +- spec->mixers[spec->num_mixers] = alc882_capture_mixer; +- spec->num_mixers++; + } + } ++ set_capture_mixer(spec); ++ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + + spec->vmaster_nid = 0x0c; + +@@ -6865,6 +7284,7 @@ static int patch_alc882(struct hda_codec *codec) + if (!spec->loopback.amplist) + spec->loopback.amplist = alc882_loopbacks; + #endif ++ codec->proc_widget_hook = print_realtek_coef; + + return 0; + } +@@ -6883,6 +7303,8 @@ static int patch_alc882(struct hda_codec *codec) + #define ALC883_DIGOUT_NID 0x06 + #define ALC883_DIGIN_NID 0x0a + ++#define ALC1200_DIGOUT_NID 0x10 ++ + static hda_nid_t alc883_dac_nids[4] = { + /* front, rear, clfe, rear_surr */ + 0x02, 0x03, 0x04, 0x05 +@@ -6893,8 +7315,24 @@ static hda_nid_t alc883_adc_nids[2] = { + 0x08, 0x09, + }; + ++static hda_nid_t alc883_adc_nids_alt[1] = { ++ /* ADC1 */ ++ 0x08, ++}; ++ ++static hda_nid_t alc883_adc_nids_rev[2] = { ++ /* ADC2-1 */ ++ 0x09, 0x08 ++}; ++ ++#define alc889_adc_nids alc880_adc_nids ++ + static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 }; + ++static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 }; ++ ++#define alc889_capsrc_nids alc882_capsrc_nids ++ + /* input MUX */ + /* FIXME: should be a matrix-type input source selection */ + +@@ -6961,11 +7399,6 @@ static struct hda_input_mux alc883_asus_eee1601_capture_source = { + }, + }; + +-#define alc883_mux_enum_info alc_mux_enum_info +-#define alc883_mux_enum_get alc_mux_enum_get +-/* ALC883 has the ALC882-type input selection */ +-#define alc883_mux_enum_put alc882_mux_enum_put +- + /* + * 2ch mode + */ +@@ -7117,21 +7550,6 @@ static struct snd_kcontrol_new alc883_base_mixer[] = { + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7149,19 +7567,6 @@ static struct snd_kcontrol_new alc883_mitac_mixer[] = { + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7176,19 +7581,6 @@ static struct snd_kcontrol_new alc883_clevo_m720_mixer[] = { + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7203,19 +7595,6 @@ static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = { + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7233,21 +7612,6 @@ static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7271,19 +7635,6 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = { + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 1, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7308,21 +7659,6 @@ static struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = { + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7346,20 +7682,6 @@ static struct snd_kcontrol_new alc883_fivestack_mixer[] = { + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 1, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7380,19 +7702,6 @@ static struct snd_kcontrol_new alc883_tagra_mixer[] = { + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7408,19 +7717,6 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = { + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7433,17 +7729,6 @@ static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = { + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 1, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7457,19 +7742,6 @@ static struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = { + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("iMic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("iMic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7483,19 +7755,6 @@ static struct snd_kcontrol_new alc883_medion_md2_mixer[] = { + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7508,19 +7767,6 @@ static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7548,19 +7794,6 @@ static struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = { + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -7591,6 +7824,10 @@ static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = { + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), ++ { } /* end */ ++}; ++ ++static struct snd_kcontrol_new alc883_asus_eee1601_cap_mixer[] = { + HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol), + HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch), + { +@@ -7598,9 +7835,9 @@ static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = { + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 1, +- .info = alc883_mux_enum_info, +- .get = alc883_mux_enum_get, +- .put = alc883_mux_enum_put, ++ .info = alc_mux_enum_info, ++ .get = alc_mux_enum_get, ++ .put = alc_mux_enum_put, + }, + { } /* end */ + }; +@@ -7852,36 +8089,83 @@ static struct hda_verb alc888_lenovo_sky_verbs[] = { + { } /* end */ + }; + ++static struct hda_verb alc888_6st_dell_verbs[] = { ++ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, ++ { } ++}; ++ ++static void alc888_3st_hp_front_automute(struct hda_codec *codec) ++{ ++ unsigned int present, bits; ++ ++ present = snd_hda_codec_read(codec, 0x1b, 0, ++ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; ++ bits = present ? HDA_AMP_MUTE : 0; ++ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, ++ HDA_AMP_MUTE, bits); ++ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, ++ HDA_AMP_MUTE, bits); ++ snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0, ++ HDA_AMP_MUTE, bits); ++} ++ ++static void alc888_3st_hp_unsol_event(struct hda_codec *codec, ++ unsigned int res) ++{ ++ switch (res >> 26) { ++ case ALC880_HP_EVENT: ++ alc888_3st_hp_front_automute(codec); ++ break; ++ } ++} ++ + static struct hda_verb alc888_3st_hp_verbs[] = { + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ + {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */ + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */ +- { } +-}; +- +-static struct hda_verb alc888_6st_dell_verbs[] = { + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, +- { } ++ { } /* end */ + }; + ++/* ++ * 2ch mode ++ */ + static struct hda_verb alc888_3st_hp_2ch_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, +- { } ++ { } /* end */ + }; + ++/* ++ * 4ch mode ++ */ ++static struct hda_verb alc888_3st_hp_4ch_init[] = { ++ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, ++ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, ++ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, ++ { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, ++ { 0x16, AC_VERB_SET_CONNECT_SEL, 0x01 }, ++ { } /* end */ ++}; ++ ++/* ++ * 6ch mode ++ */ + static struct hda_verb alc888_3st_hp_6ch_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, ++ { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +- { } ++ { 0x16, AC_VERB_SET_CONNECT_SEL, 0x01 }, ++ { } /* end */ + }; + +-static struct hda_channel_mode alc888_3st_hp_modes[2] = { ++static struct hda_channel_mode alc888_3st_hp_modes[3] = { + { 2, alc888_3st_hp_2ch_init }, ++ { 4, alc888_3st_hp_4ch_init }, + { 6, alc888_3st_hp_6ch_init }, + }; + +@@ -8142,7 +8426,7 @@ static void alc888_6st_dell_unsol_event(struct hda_codec *codec, + { + switch (res >> 26) { + case ALC880_HP_EVENT: +- printk("hp_event\n"); ++ /* printk(KERN_DEBUG "hp_event\n"); */ + alc888_6st_dell_front_automute(codec); + break; + } +@@ -8255,27 +8539,6 @@ static struct hda_verb alc883_auto_init_verbs[] = { + { } + }; + +-/* capture mixer elements */ +-static struct snd_kcontrol_new alc883_capture_mixer[] = { +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* The multiple "Capture Source" controls confuse alsamixer +- * So call somewhat different.. +- */ +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 2, +- .info = alc882_mux_enum_info, +- .get = alc882_mux_enum_get, +- .put = alc882_mux_enum_put, +- }, +- { } /* end */ +-}; +- + static struct hda_verb alc888_asus_m90v_verbs[] = { + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, +@@ -8398,6 +8661,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { + [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig", + [ALC883_ACER] = "acer", + [ALC883_ACER_ASPIRE] = "acer-aspire", ++ [ALC888_ACER_ASPIRE_4930G] = "acer-aspire-4930g", + [ALC883_MEDION] = "medion", + [ALC883_MEDION_MD2] = "medion-md2", + [ALC883_LAPTOP_EAPD] = "laptop-eapd", +@@ -8411,7 +8675,9 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { + [ALC883_MITAC] = "mitac", + [ALC883_CLEVO_M720] = "clevo-m720", + [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515", ++ [ALC888_FUJITSU_XA3530] = "fujitsu-xa3530", + [ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel", ++ [ALC1200_ASUS_P5Q] = "asus-p5q", + [ALC883_AUTO] = "auto", + }; + +@@ -8419,20 +8685,37 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_ACER_ASPIRE), ++ SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE), +- SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */ ++ SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", ++ ALC888_ACER_ASPIRE_4930G), ++ SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", ++ ALC888_ACER_ASPIRE_4930G), ++ SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC883_AUTO), ++ SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC883_AUTO), ++ SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G", ++ ALC888_ACER_ASPIRE_4930G), ++ SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G", ++ ALC888_ACER_ASPIRE_4930G), ++ /* default Acer */ ++ SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER), + SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL), + SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG), ++ SND_PCI_QUIRK(0x103c, 0x2a66, "HP Acacia", ALC888_3ST_HP), ++ SND_PCI_QUIRK(0x103c, 0x2a72, "HP Educ.ar", ALC888_3ST_HP), + SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V), + SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), ++ SND_PCI_QUIRK(0x1043, 0x8284, "Asus Z37E", ALC883_6ST_DIG), ++ SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q), + SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601), + SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG), ++ SND_PCI_QUIRK(0x1071, 0x8227, "Mitac 82801H", ALC883_MITAC), + SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC), + SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), + SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL), +@@ -8456,6 +8739,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { + SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), ++ SND_PCI_QUIRK(0x1462, 0x7260, "MSI 7260", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), +@@ -8463,12 +8747,13 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { + SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720), + SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720), +- SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD), ++ SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC883_LAPTOP_EAPD), + SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch), + SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), +- SND_PCI_QUIRK(0x1734, 0x1107, "FSC AMILO Xi2550", ++ SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1100, "FSC AMILO Xi/Pi25xx", + ALC883_FUJITSU_PI2515), +- SND_PCI_QUIRK(0x1734, 0x1108, "Fujitsu AMILO Pi2515", ALC883_FUJITSU_PI2515), ++ SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1130, "Fujitsu AMILO Xa35xx", ++ ALC888_FUJITSU_XA3530), + SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch), + SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763), + SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763), +@@ -8480,10 +8765,20 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { + SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66), + SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL), + SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL), ++ SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC), ++ SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC883_3ST_6ch_INTEL), + SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch), + {} + }; + ++static hda_nid_t alc883_slave_dig_outs[] = { ++ ALC1200_DIGOUT_NID, 0, ++}; ++ ++static hda_nid_t alc1200_slave_dig_outs[] = { ++ ALC883_DIGOUT_NID, 0, ++}; ++ + static struct alc_config_preset alc883_presets[] = { + [ALC883_3ST_2ch_DIG] = { + .mixers = { alc883_3ST_2ch_mixer }, +@@ -8525,6 +8820,7 @@ static struct alc_config_preset alc883_presets[] = { + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, ++ .slave_dig_outs = alc883_slave_dig_outs, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_intel_modes), + .channel_mode = alc883_3ST_6ch_intel_modes, + .need_dac_fix = 1, +@@ -8559,6 +8855,8 @@ static struct alc_config_preset alc883_presets[] = { + .init_verbs = { alc883_init_verbs, alc883_tagra_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, ++ .adc_nids = alc883_adc_nids_alt, ++ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, +@@ -8592,6 +8890,26 @@ static struct alc_config_preset alc883_presets[] = { + .unsol_event = alc883_acer_aspire_unsol_event, + .init_hook = alc883_acer_aspire_automute, + }, ++ [ALC888_ACER_ASPIRE_4930G] = { ++ .mixers = { alc888_base_mixer, ++ alc883_chmode_mixer }, ++ .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, ++ alc888_acer_aspire_4930g_verbs }, ++ .num_dacs = ARRAY_SIZE(alc883_dac_nids), ++ .dac_nids = alc883_dac_nids, ++ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), ++ .adc_nids = alc883_adc_nids_rev, ++ .capsrc_nids = alc883_capsrc_nids_rev, ++ .dig_out_nid = ALC883_DIGOUT_NID, ++ .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), ++ .channel_mode = alc883_3ST_6ch_modes, ++ .need_dac_fix = 1, ++ .num_mux_defs = ++ ARRAY_SIZE(alc888_2_capture_sources), ++ .input_mux = alc888_2_capture_sources, ++ .unsol_event = alc888_acer_aspire_4930g_unsol_event, ++ .init_hook = alc888_acer_aspire_4930g_automute, ++ }, + [ALC883_MEDION] = { + .mixers = { alc883_fivestack_mixer, + alc883_chmode_mixer }, +@@ -8599,6 +8917,8 @@ static struct alc_config_preset alc883_presets[] = { + alc883_medion_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, ++ .adc_nids = alc883_adc_nids_alt, ++ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, +@@ -8641,6 +8961,8 @@ static struct alc_config_preset alc883_presets[] = { + .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, ++ .adc_nids = alc883_adc_nids_alt, ++ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_lenovo_101e_capture_source, +@@ -8693,6 +9015,8 @@ static struct alc_config_preset alc883_presets[] = { + .channel_mode = alc888_3st_hp_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, ++ .unsol_event = alc888_3st_hp_unsol_event, ++ .init_hook = alc888_3st_hp_front_automute, + }, + [ALC888_6ST_DELL] = { + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, +@@ -8731,14 +9055,30 @@ static struct alc_config_preset alc883_presets[] = { + .unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event, + .init_hook = alc883_2ch_fujitsu_pi2515_automute, + }, ++ [ALC888_FUJITSU_XA3530] = { ++ .mixers = { alc888_base_mixer, alc883_chmode_mixer }, ++ .init_verbs = { alc883_init_verbs, ++ alc888_fujitsu_xa3530_verbs }, ++ .num_dacs = ARRAY_SIZE(alc883_dac_nids), ++ .dac_nids = alc883_dac_nids, ++ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), ++ .adc_nids = alc883_adc_nids_rev, ++ .capsrc_nids = alc883_capsrc_nids_rev, ++ .dig_out_nid = ALC883_DIGOUT_NID, ++ .num_channel_mode = ARRAY_SIZE(alc888_4ST_8ch_intel_modes), ++ .channel_mode = alc888_4ST_8ch_intel_modes, ++ .num_mux_defs = ++ ARRAY_SIZE(alc888_2_capture_sources), ++ .input_mux = alc888_2_capture_sources, ++ .unsol_event = alc888_fujitsu_xa3530_unsol_event, ++ .init_hook = alc888_fujitsu_xa3530_automute, ++ }, + [ALC888_LENOVO_SKY] = { + .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_lenovo_sky_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, +- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), +- .adc_nids = alc883_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .need_dac_fix = 1, +@@ -8762,6 +9102,7 @@ static struct alc_config_preset alc883_presets[] = { + }, + [ALC888_ASUS_EEE1601] = { + .mixers = { alc883_asus_eee1601_mixer }, ++ .cap_mixer = alc883_asus_eee1601_cap_mixer, + .init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, +@@ -8774,6 +9115,18 @@ static struct alc_config_preset alc883_presets[] = { + .unsol_event = alc883_eee1601_unsol_event, + .init_hook = alc883_eee1601_inithook, + }, ++ [ALC1200_ASUS_P5Q] = { ++ .mixers = { alc883_base_mixer, alc883_chmode_mixer }, ++ .init_verbs = { alc883_init_verbs }, ++ .num_dacs = ARRAY_SIZE(alc883_dac_nids), ++ .dac_nids = alc883_dac_nids, ++ .dig_out_nid = ALC1200_DIGOUT_NID, ++ .dig_in_nid = ALC883_DIGIN_NID, ++ .slave_dig_outs = alc1200_slave_dig_outs, ++ .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), ++ .channel_mode = alc883_sixstack_modes, ++ .input_mux = &alc883_capture_source, ++ }, + }; + + +@@ -8837,11 +9190,9 @@ static void alc883_auto_init_analog_input(struct hda_codec *codec) + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = spec->autocfg.input_pins[i]; + if (alc883_is_input_pin(nid)) { +- snd_hda_codec_write(codec, nid, 0, +- AC_VERB_SET_PIN_WIDGET_CONTROL, +- (i <= AUTO_PIN_FRONT_MIC ? +- PIN_VREF80 : PIN_IN)); +- if (nid != ALC883_PIN_CD_NID) ++ alc_set_input_pin(codec, nid, i); ++ if (nid != ALC883_PIN_CD_NID && ++ (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); +@@ -8856,6 +9207,8 @@ static int alc883_parse_auto_config(struct hda_codec *codec) + { + struct alc_spec *spec = codec->spec; + int err = alc880_parse_auto_config(codec); ++ struct auto_pin_cfg *cfg = &spec->autocfg; ++ int i; + + if (err < 0) + return err; +@@ -8868,8 +9221,26 @@ static int alc883_parse_auto_config(struct hda_codec *codec) + + /* hack - override the init verbs */ + spec->init_verbs[0] = alc883_auto_init_verbs; +- spec->mixers[spec->num_mixers] = alc883_capture_mixer; +- spec->num_mixers++; ++ ++ /* setup input_mux for ALC889 */ ++ if (codec->vendor_id == 0x10ec0889) { ++ /* digital-mic input pin is excluded in alc880_auto_create..() ++ * because it's under 0x18 ++ */ ++ if (cfg->input_pins[AUTO_PIN_MIC] == 0x12 || ++ cfg->input_pins[AUTO_PIN_FRONT_MIC] == 0x12) { ++ struct hda_input_mux *imux = &spec->private_imux[0]; ++ for (i = 1; i < 3; i++) ++ memcpy(&spec->private_imux[i], ++ &spec->private_imux[0], ++ sizeof(spec->private_imux[0])); ++ imux->items[imux->num_items].label = "Int DMic"; ++ imux->items[imux->num_items].index = 0x0b; ++ imux->num_items++; ++ spec->num_mux_defs = 3; ++ spec->input_mux = spec->private_imux; ++ } ++ } + + return 1; /* config found */ + } +@@ -8922,6 +9293,12 @@ static int patch_alc883(struct hda_codec *codec) + } + } + ++ err = snd_hda_attach_beep_device(codec, 0x1); ++ if (err < 0) { ++ alc_free(codec); ++ return err; ++ } ++ + if (board_config != ALC883_AUTO) + setup_preset(spec, &alc883_presets[board_config]); + +@@ -8934,14 +9311,36 @@ static int patch_alc883(struct hda_codec *codec) + spec->stream_name_analog = "ALC888 Analog"; + spec->stream_name_digital = "ALC888 Digital"; + } ++ if (!spec->num_adc_nids) { ++ spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); ++ spec->adc_nids = alc883_adc_nids; ++ } ++ if (!spec->capsrc_nids) ++ spec->capsrc_nids = alc883_capsrc_nids; ++ spec->capture_style = CAPT_MIX; /* matrix-style capture */ + break; + case 0x10ec0889: + spec->stream_name_analog = "ALC889 Analog"; + spec->stream_name_digital = "ALC889 Digital"; ++ if (!spec->num_adc_nids) { ++ spec->num_adc_nids = ARRAY_SIZE(alc889_adc_nids); ++ spec->adc_nids = alc889_adc_nids; ++ } ++ if (!spec->capsrc_nids) ++ spec->capsrc_nids = alc889_capsrc_nids; ++ spec->capture_style = CAPT_1MUX_MIX; /* 1mux/Nmix-style ++ capture */ + break; + default: + spec->stream_name_analog = "ALC883 Analog"; + spec->stream_name_digital = "ALC883 Digital"; ++ if (!spec->num_adc_nids) { ++ spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); ++ spec->adc_nids = alc883_adc_nids; ++ } ++ if (!spec->capsrc_nids) ++ spec->capsrc_nids = alc883_capsrc_nids; ++ spec->capture_style = CAPT_MIX; /* matrix-style capture */ + break; + } + +@@ -8952,9 +9351,9 @@ static int patch_alc883(struct hda_codec *codec) + spec->stream_digital_playback = &alc883_pcm_digital_playback; + spec->stream_digital_capture = &alc883_pcm_digital_capture; + +- spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); +- spec->adc_nids = alc883_adc_nids; +- spec->capsrc_nids = alc883_capsrc_nids; ++ if (!spec->cap_mixer) ++ set_capture_mixer(spec); ++ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + + spec->vmaster_nid = 0x0c; + +@@ -8966,6 +9365,7 @@ static int patch_alc883(struct hda_codec *codec) + if (!spec->loopback.amplist) + spec->loopback.amplist = alc883_loopbacks; + #endif ++ codec->proc_widget_hook = print_realtek_coef; + + return 0; + } +@@ -9006,8 +9406,6 @@ static struct snd_kcontrol_new alc262_base_mixer[] = { + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), +- /* HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT), */ + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), +@@ -9028,8 +9426,6 @@ static struct snd_kcontrol_new alc262_hippo1_mixer[] = { + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), +- /* HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT), */ + /*HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),*/ + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + { } /* end */ +@@ -9138,8 +9534,6 @@ static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT), + { } /* end */ +@@ -9168,8 +9562,6 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = { + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT), + { } /* end */ + }; + +@@ -9317,6 +9709,67 @@ static struct snd_kcontrol_new alc262_benq_t31_mixer[] = { + { } /* end */ + }; + ++static struct snd_kcontrol_new alc262_tyan_mixer[] = { ++ HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), ++ HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT), ++ HDA_CODEC_VOLUME("Aux Playback Volume", 0x0b, 0x06, HDA_INPUT), ++ HDA_CODEC_MUTE("Aux Playback Switch", 0x0b, 0x06, HDA_INPUT), ++ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), ++ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), ++ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), ++ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), ++ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), ++ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), ++ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), ++ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), ++ { } /* end */ ++}; ++ ++static struct hda_verb alc262_tyan_verbs[] = { ++ /* Headphone automute */ ++ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, ++ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, ++ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, ++ ++ /* P11 AUX_IN, white 4-pin connector */ ++ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, ++ {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, 0xe1}, ++ {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, 0x93}, ++ {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0x19}, ++ ++ {} ++}; ++ ++/* unsolicited event for HP jack sensing */ ++static void alc262_tyan_automute(struct hda_codec *codec) ++{ ++ unsigned int mute; ++ unsigned int present; ++ ++ snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); ++ present = snd_hda_codec_read(codec, 0x1b, 0, ++ AC_VERB_GET_PIN_SENSE, 0); ++ present = (present & 0x80000000) != 0; ++ if (present) { ++ /* mute line output on ATX panel */ ++ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, ++ HDA_AMP_MUTE, HDA_AMP_MUTE); ++ } else { ++ /* unmute line output if necessary */ ++ mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0); ++ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, ++ HDA_AMP_MUTE, mute); ++ } ++} ++ ++static void alc262_tyan_unsol_event(struct hda_codec *codec, ++ unsigned int res) ++{ ++ if ((res >> 26) != ALC880_HP_EVENT) ++ return; ++ alc262_tyan_automute(codec); ++} ++ + #define alc262_capture_mixer alc882_capture_mixer + #define alc262_capture_alt_mixer alc882_capture_alt_mixer + +@@ -9445,20 +9898,6 @@ static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = { + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* The multiple "Capture Source" controls confuse alsamixer +- * So call somewhat different.. +- */ +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 1, +- .info = alc_mux_enum_info, +- .get = alc_mux_enum_get, +- .put = alc_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -9797,8 +10236,6 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = { + }, + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +@@ -9975,7 +10412,7 @@ static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol, + struct alc_spec *spec = codec->spec; + int ret; + +- ret = alc882_mux_enum_put(kcontrol, ucontrol); ++ ret = alc_mux_enum_put(kcontrol, ucontrol); + if (!ret) + return 0; + /* reprogram the HP pin as mic or HP according to the input source */ +@@ -9992,8 +10429,8 @@ static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", +- .info = alc882_mux_enum_info, +- .get = alc882_mux_enum_get, ++ .info = alc_mux_enum_info, ++ .get = alc_mux_enum_get, + .put = alc262_ultra_mux_enum_put, + }, + { } /* end */ +@@ -10370,8 +10807,14 @@ static int alc262_parse_auto_config(struct hda_codec *codec) + alc262_ignore); + if (err < 0) + return err; +- if (!spec->autocfg.line_outs) ++ if (!spec->autocfg.line_outs) { ++ if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) { ++ spec->multiout.max_channels = 2; ++ spec->no_analog = 1; ++ goto dig_only; ++ } + return 0; /* can't find valid BIOS pin config */ ++ } + err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; +@@ -10381,23 +10824,25 @@ static int alc262_parse_auto_config(struct hda_codec *codec) + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + +- if (spec->autocfg.dig_out_pin) ++ dig_only: ++ if (spec->autocfg.dig_outs) { + spec->multiout.dig_out_nid = ALC262_DIGOUT_NID; ++ spec->dig_out_type = spec->autocfg.dig_out_type[0]; ++ } + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = ALC262_DIGIN_NID; + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; ++ if (spec->kctls.list) ++ add_mixer(spec, spec->kctls.list); + +- spec->init_verbs[spec->num_init_verbs++] = alc262_volume_init_verbs; ++ add_verb(spec, alc262_volume_init_verbs); + spec->num_mux_defs = 1; +- spec->input_mux = &spec->private_imux; ++ spec->input_mux = &spec->private_imux[0]; + + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; + +- store_pin_configs(codec); + return 1; + } + +@@ -10439,20 +10884,19 @@ static const char *alc262_models[ALC262_MODEL_LAST] = { + [ALC262_ULTRA] = "ultra", + [ALC262_LENOVO_3000] = "lenovo-3000", + [ALC262_NEC] = "nec", ++ [ALC262_TYAN] = "tyan", + [ALC262_AUTO] = "auto", + }; + + static struct snd_pci_quirk alc262_cfg_tbl[] = { + SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO), + SND_PCI_QUIRK(0x1033, 0x8895, "NEC Versa S9100", ALC262_NEC), +- SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC), +- SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC), +- SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC), +- SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC), +- SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC), +- SND_PCI_QUIRK(0x103c, 0x1309, "HP xw4*00", ALC262_HP_BPC), +- SND_PCI_QUIRK(0x103c, 0x130a, "HP xw6*00", ALC262_HP_BPC), +- SND_PCI_QUIRK(0x103c, 0x130b, "HP xw8*00", ALC262_HP_BPC), ++ SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1200, "HP xw series", ++ ALC262_HP_BPC), ++ SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1300, "HP xw series", ++ ALC262_HP_BPC), ++ SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1700, "HP xw series", ++ ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL), + SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL), +@@ -10470,17 +10914,17 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = { + SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD), + SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO), + SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD), +- SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD), +- SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD), +- SND_PCI_QUIRK(0x104d, 0x9033, "Sony VAIO VGN-SR19XN", +- ALC262_SONY_ASSAMD), ++ SND_PCI_QUIRK(0x104d, 0x9016, "Sony VAIO", ALC262_AUTO), /* dig-only */ ++ SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO", ++ ALC262_SONY_ASSAMD), + SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1", + ALC262_TOSHIBA_RX1), + SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06), + SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), + SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU), +- SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA), +- SND_PCI_QUIRK(0x144d, 0xc039, "Samsung Q1U EL", ALC262_ULTRA), ++ SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_TYAN), ++ SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc032, "Samsung Q1", ++ ALC262_ULTRA), + SND_PCI_QUIRK(0x144d, 0xc510, "Samsung Q45", ALC262_HIPPO), + SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 y410", ALC262_LENOVO_3000), + SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8), +@@ -10633,7 +11077,8 @@ static struct alc_config_preset alc262_presets[] = { + .init_hook = alc262_hippo_automute, + }, + [ALC262_ULTRA] = { +- .mixers = { alc262_ultra_mixer, alc262_ultra_capture_mixer }, ++ .mixers = { alc262_ultra_mixer }, ++ .cap_mixer = alc262_ultra_capture_mixer, + .init_verbs = { alc262_ultra_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, +@@ -10669,16 +11114,6 @@ static struct alc_config_preset alc262_presets[] = { + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + }, +- [ALC262_NEC] = { +- .mixers = { alc262_nec_mixer }, +- .init_verbs = { alc262_nec_verbs }, +- .num_dacs = ARRAY_SIZE(alc262_dac_nids), +- .dac_nids = alc262_dac_nids, +- .hp_nid = 0x03, +- .num_channel_mode = ARRAY_SIZE(alc262_modes), +- .channel_mode = alc262_modes, +- .input_mux = &alc262_capture_source, +- }, + [ALC262_TOSHIBA_S06] = { + .mixers = { alc262_toshiba_s06_mixer }, + .init_verbs = { alc262_init_verbs, alc262_toshiba_s06_verbs, +@@ -10706,6 +11141,19 @@ static struct alc_config_preset alc262_presets[] = { + .unsol_event = alc262_hippo_unsol_event, + .init_hook = alc262_hippo_automute, + }, ++ [ALC262_TYAN] = { ++ .mixers = { alc262_tyan_mixer }, ++ .init_verbs = { alc262_init_verbs, alc262_tyan_verbs}, ++ .num_dacs = ARRAY_SIZE(alc262_dac_nids), ++ .dac_nids = alc262_dac_nids, ++ .hp_nid = 0x02, ++ .dig_out_nid = ALC262_DIGOUT_NID, ++ .num_channel_mode = ARRAY_SIZE(alc262_modes), ++ .channel_mode = alc262_modes, ++ .input_mux = &alc262_capture_source, ++ .unsol_event = alc262_tyan_unsol_event, ++ .init_hook = alc262_tyan_automute, ++ }, + }; + + static int patch_alc262(struct hda_codec *codec) +@@ -10758,6 +11206,14 @@ static int patch_alc262(struct hda_codec *codec) + } + } + ++ if (!spec->no_analog) { ++ err = snd_hda_attach_beep_device(codec, 0x1); ++ if (err < 0) { ++ alc_free(codec); ++ return err; ++ } ++ } ++ + if (board_config != ALC262_AUTO) + setup_preset(spec, &alc262_presets[board_config]); + +@@ -10769,6 +11225,7 @@ static int patch_alc262(struct hda_codec *codec) + spec->stream_digital_playback = &alc262_pcm_digital_playback; + spec->stream_digital_capture = &alc262_pcm_digital_capture; + ++ spec->capture_style = CAPT_MIX; + if (!spec->adc_nids && spec->input_mux) { + /* check whether NID 0x07 is valid */ + unsigned int wcap = get_wcaps(codec, 0x07); +@@ -10779,17 +11236,16 @@ static int patch_alc262(struct hda_codec *codec) + spec->adc_nids = alc262_adc_nids_alt; + spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt); + spec->capsrc_nids = alc262_capsrc_nids_alt; +- spec->mixers[spec->num_mixers] = +- alc262_capture_alt_mixer; +- spec->num_mixers++; + } else { + spec->adc_nids = alc262_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids); + spec->capsrc_nids = alc262_capsrc_nids; +- spec->mixers[spec->num_mixers] = alc262_capture_mixer; +- spec->num_mixers++; + } + } ++ if (!spec->cap_mixer && !spec->no_analog) ++ set_capture_mixer(spec); ++ if (!spec->no_analog) ++ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + + spec->vmaster_nid = 0x0c; + +@@ -10800,6 +11256,7 @@ static int patch_alc262(struct hda_codec *codec) + if (!spec->loopback.amplist) + spec->loopback.amplist = alc262_loopbacks; + #endif ++ codec->proc_widget_hook = print_realtek_coef; + + return 0; + } +@@ -11168,19 +11625,13 @@ static void alc267_quanta_il1_unsol_event(struct hda_codec *codec, + static struct hda_verb alc268_base_init_verbs[] = { + /* Unmute DAC0-1 and set vol = 0 */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, +- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, +- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* + * Set up output mixers (0x0c - 0x0e) + */ + /* set vol=0 to output mixers */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +@@ -11199,9 +11650,7 @@ static struct hda_verb alc268_base_init_verbs[] = { + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + /* set PCBEEP vol = 0, mute connections */ + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +@@ -11223,10 +11672,8 @@ static struct hda_verb alc268_base_init_verbs[] = { + */ + static struct hda_verb alc268_volume_init_verbs[] = { + /* set output DAC */ +- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, ++ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, ++ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, +@@ -11234,16 +11681,12 @@ static struct hda_verb alc268_volume_init_verbs[] = { + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + +- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + /* set PCBEEP vol = 0, mute connections */ + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +@@ -11253,10 +11696,6 @@ static struct hda_verb alc268_volume_init_verbs[] = { + { } + }; + +-#define alc268_mux_enum_info alc_mux_enum_info +-#define alc268_mux_enum_get alc_mux_enum_get +-#define alc268_mux_enum_put alc_mux_enum_put +- + static struct snd_kcontrol_new alc268_capture_alt_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), +@@ -11268,9 +11707,9 @@ static struct snd_kcontrol_new alc268_capture_alt_mixer[] = { + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 1, +- .info = alc268_mux_enum_info, +- .get = alc268_mux_enum_get, +- .put = alc268_mux_enum_put, ++ .info = alc_mux_enum_info, ++ .get = alc_mux_enum_get, ++ .put = alc_mux_enum_put, + }, + { } /* end */ + }; +@@ -11288,9 +11727,9 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = { + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, +- .info = alc268_mux_enum_info, +- .get = alc268_mux_enum_get, +- .put = alc268_mux_enum_put, ++ .info = alc_mux_enum_info, ++ .get = alc_mux_enum_get, ++ .put = alc_mux_enum_put, + }, + { } /* end */ + }; +@@ -11446,7 +11885,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, + static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec, + const struct auto_pin_cfg *cfg) + { +- struct hda_input_mux *imux = &spec->private_imux; ++ struct hda_input_mux *imux = &spec->private_imux[0]; + int i, idx1; + + for (i = 0; i < AUTO_PIN_LAST; i++) { +@@ -11540,9 +11979,14 @@ static int alc268_parse_auto_config(struct hda_codec *codec) + alc268_ignore); + if (err < 0) + return err; +- if (!spec->autocfg.line_outs) ++ if (!spec->autocfg.line_outs) { ++ if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) { ++ spec->multiout.max_channels = 2; ++ spec->no_analog = 1; ++ goto dig_only; ++ } + return 0; /* can't find valid BIOS pin config */ +- ++ } + err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; +@@ -11552,25 +11996,26 @@ static int alc268_parse_auto_config(struct hda_codec *codec) + + spec->multiout.max_channels = 2; + ++ dig_only: + /* digital only support output */ +- if (spec->autocfg.dig_out_pin) ++ if (spec->autocfg.dig_outs) { + spec->multiout.dig_out_nid = ALC268_DIGOUT_NID; ++ spec->dig_out_type = spec->autocfg.dig_out_type[0]; ++ } ++ if (spec->kctls.list) ++ add_mixer(spec, spec->kctls.list); + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; +- +- if (spec->autocfg.speaker_pins[0] != 0x1d) +- spec->mixers[spec->num_mixers++] = alc268_beep_mixer; ++ if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) ++ add_mixer(spec, alc268_beep_mixer); + +- spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs; ++ add_verb(spec, alc268_volume_init_verbs); + spec->num_mux_defs = 1; +- spec->input_mux = &spec->private_imux; ++ spec->input_mux = &spec->private_imux[0]; + + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; + +- store_pin_configs(codec); + return 1; + } + +@@ -11617,7 +12062,9 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x015b, "Acer Aspire One", + ALC268_ACER_ASPIRE_ONE), + SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL), ++ SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron Mini9", ALC268_DELL), + SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA), ++ SND_PCI_QUIRK(0x103c, 0x30f1, "HP TX25xx series", ALC268_TOSHIBA), + SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST), + SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA), + SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA), +@@ -11631,7 +12078,7 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = { + + static struct alc_config_preset alc268_presets[] = { + [ALC267_QUANTA_IL1] = { +- .mixers = { alc267_quanta_il1_mixer }, ++ .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc267_quanta_il1_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), +@@ -11713,7 +12160,8 @@ static struct alc_config_preset alc268_presets[] = { + }, + [ALC268_ACER_ASPIRE_ONE] = { + .mixers = { alc268_acer_aspire_one_mixer, +- alc268_capture_alt_mixer }, ++ alc268_beep_mixer, ++ alc268_capture_alt_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_acer_aspire_one_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), +@@ -11782,7 +12230,7 @@ static int patch_alc268(struct hda_codec *codec) + { + struct alc_spec *spec; + int board_config; +- int err; ++ int i, has_beep, err; + + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) +@@ -11831,15 +12279,30 @@ static int patch_alc268(struct hda_codec *codec) + + spec->stream_digital_playback = &alc268_pcm_digital_playback; + +- if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) +- /* override the amp caps for beep generator */ +- snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, ++ has_beep = 0; ++ for (i = 0; i < spec->num_mixers; i++) { ++ if (spec->mixers[i] == alc268_beep_mixer) { ++ has_beep = 1; ++ break; ++ } ++ } ++ ++ if (has_beep) { ++ err = snd_hda_attach_beep_device(codec, 0x1); ++ if (err < 0) { ++ alc_free(codec); ++ return err; ++ } ++ if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) ++ /* override the amp caps for beep generator */ ++ snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, + (0x0c << AC_AMPCAP_OFFSET_SHIFT) | + (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); ++ } + +- if (!spec->adc_nids && spec->input_mux) { ++ if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { + /* check whether NID 0x07 is valid */ + unsigned int wcap = get_wcaps(codec, 0x07); + int i; +@@ -11849,15 +12312,11 @@ static int patch_alc268(struct hda_codec *codec) + if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { + spec->adc_nids = alc268_adc_nids_alt; + spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt); +- spec->mixers[spec->num_mixers] = +- alc268_capture_alt_mixer; +- spec->num_mixers++; ++ add_mixer(spec, alc268_capture_alt_mixer); + } else { + spec->adc_nids = alc268_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids); +- spec->mixers[spec->num_mixers] = +- alc268_capture_mixer; +- spec->num_mixers++; ++ add_mixer(spec, alc268_capture_mixer); + } + spec->capsrc_nids = alc268_capsrc_nids; + /* set default input source */ +@@ -11873,6 +12332,8 @@ static int patch_alc268(struct hda_codec *codec) + if (board_config == ALC268_AUTO) + spec->init_hook = alc268_auto_init; + ++ codec->proc_widget_hook = print_realtek_coef; ++ + return 0; + } + +@@ -11922,8 +12383,6 @@ static struct snd_kcontrol_new alc269_base_mixer[] = { + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x4, HDA_INPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x4, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), +@@ -11950,8 +12409,29 @@ static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = { + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x04, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x04, HDA_INPUT), ++ { } ++}; ++ ++static struct snd_kcontrol_new alc269_lifebook_mixer[] = { ++ /* output mixer control */ ++ HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "Master Playback Switch", ++ .info = snd_hda_mixer_amp_switch_info, ++ .get = snd_hda_mixer_amp_switch_get, ++ .put = alc268_acer_master_sw_put, ++ .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), ++ }, ++ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), ++ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), ++ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), ++ HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), ++ HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), ++ HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT), ++ HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x0b, 0x03, HDA_INPUT), ++ HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x0b, 0x03, HDA_INPUT), ++ HDA_CODEC_VOLUME("Dock Mic Boost", 0x1b, 0, HDA_INPUT), + { } + }; + +@@ -11973,25 +12453,6 @@ static struct snd_kcontrol_new alc269_eeepc_mixer[] = { + }; + + /* capture mixer elements */ +-static struct snd_kcontrol_new alc269_capture_mixer[] = { +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* The multiple "Capture Source" controls confuse alsamixer +- * So call somewhat different.. +- */ +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 1, +- .info = alc_mux_enum_info, +- .get = alc_mux_enum_get, +- .put = alc_mux_enum_put, +- }, +- { } /* end */ +-}; +- +-/* capture mixer elements */ + static struct snd_kcontrol_new alc269_epc_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +@@ -12007,18 +12468,25 @@ static struct snd_kcontrol_new alc269_fujitsu_mixer[] = { + { } /* end */ + }; + +-/* beep control */ +-static struct snd_kcontrol_new alc269_beep_mixer[] = { +- HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x4, HDA_INPUT), +- HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x4, HDA_INPUT), +- { } /* end */ ++static struct hda_verb alc269_quanta_fl1_verbs[] = { ++ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, ++ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, ++ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, ++ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, ++ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, ++ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, ++ { } + }; + +-static struct hda_verb alc269_quanta_fl1_verbs[] = { ++static struct hda_verb alc269_lifebook_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, ++ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, ++ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, ++ {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + { } +@@ -12049,6 +12517,37 @@ static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec) + AC_VERB_SET_PROC_COEF, 0x480); + } + ++/* toggle speaker-output according to the hp-jacks state */ ++static void alc269_lifebook_speaker_automute(struct hda_codec *codec) ++{ ++ unsigned int present; ++ unsigned char bits; ++ ++ /* Check laptop headphone socket */ ++ present = snd_hda_codec_read(codec, 0x15, 0, ++ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; ++ ++ /* Check port replicator headphone socket */ ++ present |= snd_hda_codec_read(codec, 0x1a, 0, ++ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; ++ ++ bits = present ? AMP_IN_MUTE(0) : 0; ++ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, ++ AMP_IN_MUTE(0), bits); ++ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, ++ AMP_IN_MUTE(0), bits); ++ ++ snd_hda_codec_write(codec, 0x20, 0, ++ AC_VERB_SET_COEF_INDEX, 0x0c); ++ snd_hda_codec_write(codec, 0x20, 0, ++ AC_VERB_SET_PROC_COEF, 0x680); ++ ++ snd_hda_codec_write(codec, 0x20, 0, ++ AC_VERB_SET_COEF_INDEX, 0x0c); ++ snd_hda_codec_write(codec, 0x20, 0, ++ AC_VERB_SET_PROC_COEF, 0x480); ++} ++ + static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec) + { + unsigned int present; +@@ -12059,6 +12558,29 @@ static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec) + AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x1); + } + ++static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec) ++{ ++ unsigned int present_laptop; ++ unsigned int present_dock; ++ ++ present_laptop = snd_hda_codec_read(codec, 0x18, 0, ++ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; ++ ++ present_dock = snd_hda_codec_read(codec, 0x1b, 0, ++ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; ++ ++ /* Laptop mic port overrides dock mic port, design decision */ ++ if (present_dock) ++ snd_hda_codec_write(codec, 0x23, 0, ++ AC_VERB_SET_CONNECT_SEL, 0x3); ++ if (present_laptop) ++ snd_hda_codec_write(codec, 0x23, 0, ++ AC_VERB_SET_CONNECT_SEL, 0x0); ++ if (!present_dock && !present_laptop) ++ snd_hda_codec_write(codec, 0x23, 0, ++ AC_VERB_SET_CONNECT_SEL, 0x1); ++} ++ + static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec, + unsigned int res) + { +@@ -12068,12 +12590,27 @@ static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec, + alc269_quanta_fl1_mic_automute(codec); + } + ++static void alc269_lifebook_unsol_event(struct hda_codec *codec, ++ unsigned int res) ++{ ++ if ((res >> 26) == ALC880_HP_EVENT) ++ alc269_lifebook_speaker_automute(codec); ++ if ((res >> 26) == ALC880_MIC_EVENT) ++ alc269_lifebook_mic_autoswitch(codec); ++} ++ + static void alc269_quanta_fl1_init_hook(struct hda_codec *codec) + { + alc269_quanta_fl1_speaker_automute(codec); + alc269_quanta_fl1_mic_automute(codec); + } + ++static void alc269_lifebook_init_hook(struct hda_codec *codec) ++{ ++ alc269_lifebook_speaker_automute(codec); ++ alc269_lifebook_mic_autoswitch(codec); ++} ++ + static struct hda_verb alc269_eeepc_dmic_init_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x23, AC_VERB_SET_CONNECT_SEL, 0x05}, +@@ -12330,7 +12867,7 @@ static int alc269_auto_create_analog_input_ctls(struct alc_spec *spec, + */ + if (cfg->input_pins[AUTO_PIN_MIC] == 0x12 || + cfg->input_pins[AUTO_PIN_FRONT_MIC] == 0x12) { +- struct hda_input_mux *imux = &spec->private_imux; ++ struct hda_input_mux *imux = &spec->private_imux[0]; + imux->items[imux->num_items].label = "Int Mic"; + imux->items[imux->num_items].index = 0x05; + imux->num_items++; +@@ -12348,13 +12885,34 @@ static int alc269_auto_create_analog_input_ctls(struct alc_spec *spec, + #define alc269_pcm_digital_playback alc880_pcm_digital_playback + #define alc269_pcm_digital_capture alc880_pcm_digital_capture + ++static struct hda_pcm_stream alc269_44k_pcm_analog_playback = { ++ .substreams = 1, ++ .channels_min = 2, ++ .channels_max = 8, ++ .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ ++ /* NID is set in alc_build_pcms */ ++ .ops = { ++ .open = alc880_playback_pcm_open, ++ .prepare = alc880_playback_pcm_prepare, ++ .cleanup = alc880_playback_pcm_cleanup ++ }, ++}; ++ ++static struct hda_pcm_stream alc269_44k_pcm_analog_capture = { ++ .substreams = 1, ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ ++ /* NID is set in alc_build_pcms */ ++}; ++ + /* + * BIOS auto configuration + */ + static int alc269_parse_auto_config(struct hda_codec *codec) + { + struct alc_spec *spec = codec->spec; +- int i, err; ++ int err; + static hda_nid_t alc269_ignore[] = { 0x1d, 0 }; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, +@@ -12371,22 +12929,15 @@ static int alc269_parse_auto_config(struct hda_codec *codec) + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + +- if (spec->autocfg.dig_out_pin) ++ if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = ALC269_DIGOUT_NID; + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; +- +- /* create a beep mixer control if the pin 0x1d isn't assigned */ +- for (i = 0; i < ARRAY_SIZE(spec->autocfg.input_pins); i++) +- if (spec->autocfg.input_pins[i] == 0x1d) +- break; +- if (i >= ARRAY_SIZE(spec->autocfg.input_pins)) +- spec->mixers[spec->num_mixers++] = alc269_beep_mixer; ++ if (spec->kctls.list) ++ add_mixer(spec, spec->kctls.list); + +- spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs; ++ add_verb(spec, alc269_init_verbs); + spec->num_mux_defs = 1; +- spec->input_mux = &spec->private_imux; ++ spec->input_mux = &spec->private_imux[0]; + /* set default input source */ + snd_hda_codec_write_cache(codec, alc269_capsrc_nids[0], + 0, AC_VERB_SET_CONNECT_SEL, +@@ -12396,10 +12947,9 @@ static int alc269_parse_auto_config(struct hda_codec *codec) + if (err < 0) + return err; + +- spec->mixers[spec->num_mixers] = alc269_capture_mixer; +- spec->num_mixers++; ++ if (!spec->cap_mixer && !spec->no_analog) ++ set_capture_mixer(spec); + +- store_pin_configs(codec); + return 1; + } + +@@ -12427,24 +12977,33 @@ static const char *alc269_models[ALC269_MODEL_LAST] = { + [ALC269_QUANTA_FL1] = "quanta", + [ALC269_ASUS_EEEPC_P703] = "eeepc-p703", + [ALC269_ASUS_EEEPC_P901] = "eeepc-p901", +- [ALC269_FUJITSU] = "fujitsu" ++ [ALC269_FUJITSU] = "fujitsu", ++ [ALC269_LIFEBOOK] = "lifebook" + }; + + static struct snd_pci_quirk alc269_cfg_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1), + SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A", + ALC269_ASUS_EEEPC_P703), ++ SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_ASUS_EEEPC_P703), ++ SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_ASUS_EEEPC_P703), ++ SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_ASUS_EEEPC_P703), ++ SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_ASUS_EEEPC_P703), ++ SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_ASUS_EEEPC_P703), ++ SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_ASUS_EEEPC_P703), + SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901", + ALC269_ASUS_EEEPC_P901), + SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101", + ALC269_ASUS_EEEPC_P901), ++ SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_ASUS_EEEPC_P901), + SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU), ++ SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK), + {} + }; + + static struct alc_config_preset alc269_presets[] = { + [ALC269_BASIC] = { +- .mixers = { alc269_base_mixer, alc269_capture_mixer }, ++ .mixers = { alc269_base_mixer }, + .init_verbs = { alc269_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, +@@ -12466,7 +13025,8 @@ static struct alc_config_preset alc269_presets[] = { + .init_hook = alc269_quanta_fl1_init_hook, + }, + [ALC269_ASUS_EEEPC_P703] = { +- .mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer }, ++ .mixers = { alc269_eeepc_mixer }, ++ .cap_mixer = alc269_epc_capture_mixer, + .init_verbs = { alc269_init_verbs, + alc269_eeepc_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), +@@ -12479,7 +13039,8 @@ static struct alc_config_preset alc269_presets[] = { + .init_hook = alc269_eeepc_amic_inithook, + }, + [ALC269_ASUS_EEEPC_P901] = { +- .mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer}, ++ .mixers = { alc269_eeepc_mixer }, ++ .cap_mixer = alc269_epc_capture_mixer, + .init_verbs = { alc269_init_verbs, + alc269_eeepc_dmic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), +@@ -12492,8 +13053,8 @@ static struct alc_config_preset alc269_presets[] = { + .init_hook = alc269_eeepc_dmic_inithook, + }, + [ALC269_FUJITSU] = { +- .mixers = { alc269_fujitsu_mixer, alc269_beep_mixer, +- alc269_epc_capture_mixer }, ++ .mixers = { alc269_fujitsu_mixer }, ++ .cap_mixer = alc269_epc_capture_mixer, + .init_verbs = { alc269_init_verbs, + alc269_eeepc_dmic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), +@@ -12505,6 +13066,18 @@ static struct alc_config_preset alc269_presets[] = { + .unsol_event = alc269_eeepc_dmic_unsol_event, + .init_hook = alc269_eeepc_dmic_inithook, + }, ++ [ALC269_LIFEBOOK] = { ++ .mixers = { alc269_lifebook_mixer }, ++ .init_verbs = { alc269_init_verbs, alc269_lifebook_verbs }, ++ .num_dacs = ARRAY_SIZE(alc269_dac_nids), ++ .dac_nids = alc269_dac_nids, ++ .hp_nid = 0x03, ++ .num_channel_mode = ARRAY_SIZE(alc269_modes), ++ .channel_mode = alc269_modes, ++ .input_mux = &alc269_capture_source, ++ .unsol_event = alc269_lifebook_unsol_event, ++ .init_hook = alc269_lifebook_init_hook, ++ }, + }; + + static int patch_alc269(struct hda_codec *codec) +@@ -12545,13 +13118,26 @@ static int patch_alc269(struct hda_codec *codec) + } + } + ++ err = snd_hda_attach_beep_device(codec, 0x1); ++ if (err < 0) { ++ alc_free(codec); ++ return err; ++ } ++ + if (board_config != ALC269_AUTO) + setup_preset(spec, &alc269_presets[board_config]); + + spec->stream_name_analog = "ALC269 Analog"; +- spec->stream_analog_playback = &alc269_pcm_analog_playback; +- spec->stream_analog_capture = &alc269_pcm_analog_capture; +- ++ if (codec->subsystem_id == 0x17aa3bf8) { ++ /* Due to a hardware problem on Lenovo Ideadpad, we need to ++ * fix the sample rate of analog I/O to 44.1kHz ++ */ ++ spec->stream_analog_playback = &alc269_44k_pcm_analog_playback; ++ spec->stream_analog_capture = &alc269_44k_pcm_analog_capture; ++ } else { ++ spec->stream_analog_playback = &alc269_pcm_analog_playback; ++ spec->stream_analog_capture = &alc269_pcm_analog_capture; ++ } + spec->stream_name_digital = "ALC269 Digital"; + spec->stream_digital_playback = &alc269_pcm_digital_playback; + spec->stream_digital_capture = &alc269_pcm_digital_capture; +@@ -12559,6 +13145,9 @@ static int patch_alc269(struct hda_codec *codec) + spec->adc_nids = alc269_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); + spec->capsrc_nids = alc269_capsrc_nids; ++ if (!spec->cap_mixer) ++ set_capture_mixer(spec); ++ set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); + + codec->patch_ops = alc_patch_ops; + if (board_config == ALC269_AUTO) +@@ -12567,6 +13156,7 @@ static int patch_alc269(struct hda_codec *codec) + if (!spec->loopback.amplist) + spec->loopback.amplist = alc269_loopbacks; + #endif ++ codec->proc_widget_hook = print_realtek_coef; + + return 0; + } +@@ -12699,17 +13289,6 @@ static struct snd_kcontrol_new alc861_base_mixer[] = { + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), + +- /* Capture mixer control */ +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "Capture Source", +- .count = 1, +- .info = alc_mux_enum_info, +- .get = alc_mux_enum_get, +- .put = alc_mux_enum_put, +- }, + { } /* end */ + }; + +@@ -12733,17 +13312,6 @@ static struct snd_kcontrol_new alc861_3ST_mixer[] = { + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), + +- /* Capture mixer control */ +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "Capture Source", +- .count = 1, +- .info = alc_mux_enum_info, +- .get = alc_mux_enum_get, +- .put = alc_mux_enum_put, +- }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", +@@ -12761,18 +13329,6 @@ static struct snd_kcontrol_new alc861_toshiba_mixer[] = { + HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), + +- /*Capture mixer control */ +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "Capture Source", +- .count = 1, +- .info = alc_mux_enum_info, +- .get = alc_mux_enum_get, +- .put = alc_mux_enum_put, +- }, +- + { } /* end */ + }; + +@@ -12796,17 +13352,6 @@ static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = { + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), + +- /* Capture mixer control */ +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "Capture Source", +- .count = 1, +- .info = alc_mux_enum_info, +- .get = alc_mux_enum_get, +- .put = alc_mux_enum_put, +- }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", +@@ -12838,17 +13383,6 @@ static struct snd_kcontrol_new alc861_asus_mixer[] = { + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT), + +- /* Capture mixer control */ +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "Capture Source", +- .count = 1, +- .info = alc_mux_enum_info, +- .get = alc_mux_enum_get, +- .put = alc_mux_enum_put, +- }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", +@@ -12864,8 +13398,6 @@ static struct snd_kcontrol_new alc861_asus_mixer[] = { + static struct snd_kcontrol_new alc861_asus_laptop_mixer[] = { + HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x23, 0x0, HDA_OUTPUT), +- HDA_CODEC_MUTE("PC Beep Playback Switch", 0x23, 0x0, HDA_OUTPUT), + { } + }; + +@@ -13339,7 +13871,7 @@ static int alc861_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin) + static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec, + const struct auto_pin_cfg *cfg) + { +- struct hda_input_mux *imux = &spec->private_imux; ++ struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx, idx1; + + for (i = 0; i < AUTO_PIN_LAST; i++) { +@@ -13380,25 +13912,6 @@ static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec, + return 0; + } + +-static struct snd_kcontrol_new alc861_capture_mixer[] = { +- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +- +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* The multiple "Capture Source" controls confuse alsamixer +- * So call somewhat different.. +- */ +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 1, +- .info = alc_mux_enum_info, +- .get = alc_mux_enum_get, +- .put = alc_mux_enum_put, +- }, +- { } /* end */ +-}; +- + static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, + int pin_type, int dac_idx) +@@ -13445,12 +13958,8 @@ static void alc861_auto_init_analog_input(struct hda_codec *codec) + + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = spec->autocfg.input_pins[i]; +- if (nid >= 0x0c && nid <= 0x11) { +- snd_hda_codec_write(codec, nid, 0, +- AC_VERB_SET_PIN_WIDGET_CONTROL, +- i <= AUTO_PIN_FRONT_MIC ? +- PIN_VREF80 : PIN_IN); +- } ++ if (nid >= 0x0c && nid <= 0x11) ++ alc_set_input_pin(codec, nid, i); + } + } + +@@ -13486,23 +13995,21 @@ static int alc861_parse_auto_config(struct hda_codec *codec) + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + +- if (spec->autocfg.dig_out_pin) ++ if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = ALC861_DIGOUT_NID; + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; ++ if (spec->kctls.list) ++ add_mixer(spec, spec->kctls.list); + +- spec->init_verbs[spec->num_init_verbs++] = alc861_auto_init_verbs; ++ add_verb(spec, alc861_auto_init_verbs); + + spec->num_mux_defs = 1; +- spec->input_mux = &spec->private_imux; ++ spec->input_mux = &spec->private_imux[0]; + + spec->adc_nids = alc861_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids); +- spec->mixers[spec->num_mixers] = alc861_capture_mixer; +- spec->num_mixers++; ++ set_capture_mixer(spec); + +- store_pin_configs(codec); + return 1; + } + +@@ -13711,6 +14218,12 @@ static int patch_alc861(struct hda_codec *codec) + } + } + ++ err = snd_hda_attach_beep_device(codec, 0x23); ++ if (err < 0) { ++ alc_free(codec); ++ return err; ++ } ++ + if (board_config != ALC861_AUTO) + setup_preset(spec, &alc861_presets[board_config]); + +@@ -13722,6 +14235,8 @@ static int patch_alc861(struct hda_codec *codec) + spec->stream_digital_playback = &alc861_pcm_digital_playback; + spec->stream_digital_capture = &alc861_pcm_digital_capture; + ++ set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); ++ + spec->vmaster_nid = 0x03; + + codec->patch_ops = alc_patch_ops; +@@ -13731,6 +14246,7 @@ static int patch_alc861(struct hda_codec *codec) + if (!spec->loopback.amplist) + spec->loopback.amplist = alc861_loopbacks; + #endif ++ codec->proc_widget_hook = print_realtek_coef; + + return 0; + } +@@ -13796,11 +14312,6 @@ static struct hda_input_mux alc861vd_hp_capture_source = { + }, + }; + +-#define alc861vd_mux_enum_info alc_mux_enum_info +-#define alc861vd_mux_enum_get alc_mux_enum_get +-/* ALC861VD has the ALC882-type input selection (but has only one ADC) */ +-#define alc861vd_mux_enum_put alc882_mux_enum_put +- + /* + * 2ch mode + */ +@@ -13846,25 +14357,6 @@ static struct snd_kcontrol_new alc861vd_chmode_mixer[] = { + { } /* end */ + }; + +-static struct snd_kcontrol_new alc861vd_capture_mixer[] = { +- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), +- +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* The multiple "Capture Source" controls confuse alsamixer +- * So call somewhat different.. +- */ +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 1, +- .info = alc861vd_mux_enum_info, +- .get = alc861vd_mux_enum_get, +- .put = alc861vd_mux_enum_put, +- }, +- { } /* end */ +-}; +- + /* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 + * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b + */ +@@ -13901,9 +14393,6 @@ static struct snd_kcontrol_new alc861vd_6st_mixer[] = { + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), +- + { } /* end */ + }; + +@@ -13927,9 +14416,6 @@ static struct snd_kcontrol_new alc861vd_3st_mixer[] = { + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), +- + { } /* end */ + }; + +@@ -13968,8 +14454,6 @@ static struct snd_kcontrol_new alc861vd_dallas_mixer[] = { + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Beep Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Beep Switch", 0x0b, 0x05, HDA_INPUT), + { } /* end */ + }; + +@@ -14256,6 +14740,7 @@ static void alc861vd_dallas_unsol_event(struct hda_codec *codec, unsigned int re + static const char *alc861vd_models[ALC861VD_MODEL_LAST] = { + [ALC660VD_3ST] = "3stack-660", + [ALC660VD_3ST_DIG] = "3stack-660-digout", ++ [ALC660VD_ASUS_V1S] = "asus-v1s", + [ALC861VD_3ST] = "3stack", + [ALC861VD_3ST_DIG] = "3stack-digout", + [ALC861VD_6ST_DIG] = "6stack-digout", +@@ -14270,7 +14755,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP), + SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST), + SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST), +- SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC861VD_LENOVO), ++ SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S), + SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG), + SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST), + SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO), +@@ -14279,9 +14764,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = { + SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS), + SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG), +- SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO), +- SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO), +- SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 N200", ALC861VD_LENOVO), ++ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG), + {} + }; +@@ -14377,6 +14860,21 @@ static struct alc_config_preset alc861vd_presets[] = { + .unsol_event = alc861vd_dallas_unsol_event, + .init_hook = alc861vd_dallas_automute, + }, ++ [ALC660VD_ASUS_V1S] = { ++ .mixers = { alc861vd_lenovo_mixer }, ++ .init_verbs = { alc861vd_volume_init_verbs, ++ alc861vd_3stack_init_verbs, ++ alc861vd_eapd_verbs, ++ alc861vd_lenovo_unsol_verbs }, ++ .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), ++ .dac_nids = alc660vd_dac_nids, ++ .dig_out_nid = ALC861VD_DIGOUT_NID, ++ .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), ++ .channel_mode = alc861vd_3stack_2ch_modes, ++ .input_mux = &alc861vd_capture_source, ++ .unsol_event = alc861vd_lenovo_unsol_event, ++ .init_hook = alc861vd_lenovo_automute, ++ }, + }; + + /* +@@ -14428,11 +14926,9 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec) + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = spec->autocfg.input_pins[i]; + if (alc861vd_is_input_pin(nid)) { +- snd_hda_codec_write(codec, nid, 0, +- AC_VERB_SET_PIN_WIDGET_CONTROL, +- i <= AUTO_PIN_FRONT_MIC ? +- PIN_VREF80 : PIN_IN); +- if (nid != ALC861VD_PIN_CD_NID) ++ alc_set_input_pin(codec, nid, i); ++ if (nid != ALC861VD_PIN_CD_NID && ++ (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); +@@ -14598,23 +15094,21 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + +- if (spec->autocfg.dig_out_pin) ++ if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = ALC861VD_DIGOUT_NID; + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; ++ if (spec->kctls.list) ++ add_mixer(spec, spec->kctls.list); + +- spec->init_verbs[spec->num_init_verbs++] +- = alc861vd_volume_init_verbs; ++ add_verb(spec, alc861vd_volume_init_verbs); + + spec->num_mux_defs = 1; +- spec->input_mux = &spec->private_imux; ++ spec->input_mux = &spec->private_imux[0]; + + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; + +- store_pin_configs(codec); + return 1; + } + +@@ -14665,6 +15159,12 @@ static int patch_alc861vd(struct hda_codec *codec) + } + } + ++ err = snd_hda_attach_beep_device(codec, 0x23); ++ if (err < 0) { ++ alc_free(codec); ++ return err; ++ } ++ + if (board_config != ALC861VD_AUTO) + setup_preset(spec, &alc861vd_presets[board_config]); + +@@ -14672,7 +15172,7 @@ static int patch_alc861vd(struct hda_codec *codec) + spec->stream_name_analog = "ALC660-VD Analog"; + spec->stream_name_digital = "ALC660-VD Digital"; + /* always turn on EAPD */ +- spec->init_verbs[spec->num_init_verbs++] = alc660vd_eapd_verbs; ++ add_verb(spec, alc660vd_eapd_verbs); + } else { + spec->stream_name_analog = "ALC861VD Analog"; + spec->stream_name_digital = "ALC861VD Digital"; +@@ -14687,9 +15187,10 @@ static int patch_alc861vd(struct hda_codec *codec) + spec->adc_nids = alc861vd_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids); + spec->capsrc_nids = alc861vd_capsrc_nids; ++ spec->capture_style = CAPT_MIX; + +- spec->mixers[spec->num_mixers] = alc861vd_capture_mixer; +- spec->num_mixers++; ++ set_capture_mixer(spec); ++ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + + spec->vmaster_nid = 0x02; + +@@ -14701,6 +15202,7 @@ static int patch_alc861vd(struct hda_codec *codec) + if (!spec->loopback.amplist) + spec->loopback.amplist = alc861vd_loopbacks; + #endif ++ codec->proc_widget_hook = print_realtek_coef; + + return 0; + } +@@ -14724,12 +15226,23 @@ static hda_nid_t alc662_dac_nids[4] = { + 0x02, 0x03, 0x04 + }; + ++static hda_nid_t alc272_dac_nids[2] = { ++ 0x02, 0x03 ++}; ++ + static hda_nid_t alc662_adc_nids[1] = { + /* ADC1-2 */ + 0x09, + }; + ++static hda_nid_t alc272_adc_nids[1] = { ++ /* ADC1-2 */ ++ 0x08, ++}; ++ + static hda_nid_t alc662_capsrc_nids[1] = { 0x22 }; ++static hda_nid_t alc272_capsrc_nids[1] = { 0x23 }; ++ + + /* input MUX */ + /* FIXME: should be a matrix-type input source selection */ +@@ -14776,10 +15289,6 @@ static struct hda_input_mux alc663_m51va_capture_source = { + }, + }; + +-#define alc662_mux_enum_info alc_mux_enum_info +-#define alc662_mux_enum_get alc_mux_enum_get +-#define alc662_mux_enum_put alc882_mux_enum_put +- + /* + * 2ch mode + */ +@@ -14881,8 +15390,6 @@ static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + { } /* end */ + }; + +@@ -14904,8 +15411,6 @@ static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = { + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), +- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + { } /* end */ + }; + +@@ -15163,14 +15668,7 @@ static struct hda_verb alc662_init_verbs[] = { + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, +- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, +- + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, +- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* always trun on EAPD */ + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, +@@ -15365,23 +15863,34 @@ static struct hda_verb alc662_ecs_init_verbs[] = { + {} + }; + +-/* capture mixer elements */ +-static struct snd_kcontrol_new alc662_capture_mixer[] = { +- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- /* The multiple "Capture Source" controls confuse alsamixer +- * So call somewhat different.. +- */ +- /* .name = "Capture Source", */ +- .name = "Input Source", +- .count = 1, +- .info = alc662_mux_enum_info, +- .get = alc662_mux_enum_get, +- .put = alc662_mux_enum_put, +- }, +- { } /* end */ ++static struct hda_verb alc272_dell_zm1_init_verbs[] = { ++ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, ++ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, ++ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, ++ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, ++ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, ++ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ ++ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, ++ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, ++ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, ++ {} ++}; ++ ++static struct hda_verb alc272_dell_init_verbs[] = { ++ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, ++ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, ++ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, ++ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, ++ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, ++ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, ++ {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ ++ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, ++ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, ++ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, ++ {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, ++ {} + }; + + static struct snd_kcontrol_new alc662_auto_capture_mixer[] = { +@@ -15390,6 +15899,12 @@ static struct snd_kcontrol_new alc662_auto_capture_mixer[] = { + { } /* end */ + }; + ++static struct snd_kcontrol_new alc272_auto_capture_mixer[] = { ++ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), ++ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), ++ { } /* end */ ++}; ++ + static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec) + { + unsigned int present; +@@ -15900,62 +16415,74 @@ static const char *alc662_models[ALC662_MODEL_LAST] = { + }; + + static struct snd_pci_quirk alc662_cfg_tbl[] = { +- SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA), +- SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS G50V", ALC663_ASUS_G50V), +- SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG), +- SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701), +- SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20), +- SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1), +- SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M50Vr", ALC663_ASUS_MODE1), ++ SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_ECS), ++ SND_PCI_QUIRK(0x1028, 0x02d6, "DELL", ALC272_DELL), ++ SND_PCI_QUIRK(0x1028, 0x02f4, "DELL ZM1", ALC272_DELL_ZM1), + SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1), +- SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC663_ASUS_MODE1), +- SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC663_ASUS_MODE1), +- SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS NB", ALC663_ASUS_MODE1), ++ SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3), ++ SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1), ++ SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1), +- SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1), +- SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC663_ASUS_MODE1), +- SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2), +- SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2), +- SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_ASUS_MODE2), ++ SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2), ++ SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2), ++ SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6), ++ SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6), ++ SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2), ++ SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC663_ASUS_MODE3), ++ SND_PCI_QUIRK(0x1043, 0x17c3, "ASUS UX20", ALC663_ASUS_M51VA), ++ SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_ASUS_MODE2), +- SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2), +- SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_ASUS_MODE2), ++ SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC663_ASUS_MODE5), ++ SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC663_ASUS_MODE6), ++ SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_ASUS_MODE2), ++ SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_ASUS_MODE2), +- SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2), +- SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2), +- SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2), +- SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_ASUS_MODE2), ++ SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_ASUS_MODE2), ++ SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA), ++ /*SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M50Vr", ALC663_ASUS_MODE1),*/ + SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3), +- SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3), +- SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3), +- SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3), ++ SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC663_ASUS_MODE1), ++ SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC663_ASUS_MODE1), ++ SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC663_ASUS_MODE1), ++ SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1), ++ SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2), ++ SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_ASUS_MODE2), ++ SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC663_ASUS_MODE1), ++ SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC663_ASUS_MODE1), ++ SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC663_ASUS_MODE3), ++ SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC663_ASUS_MODE1), ++ SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC663_ASUS_MODE1), ++ SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS G50V", ALC663_ASUS_G50V), ++ /*SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS NB", ALC663_ASUS_MODE1),*/ ++ SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC663_ASUS_MODE1), ++ SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2), ++ SND_PCI_QUIRK(0x1043, 0x19d3, "ASUS NB", ALC663_ASUS_M51VA), ++ SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC663_ASUS_MODE4), +- SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC663_ASUS_MODE5), +- SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC663_ASUS_MODE6), +- SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6), +- SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6), ++ SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG), ++ SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701), ++ SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20), ++ SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS), + SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K", + ALC662_3ST_6ch_DIG), +- SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), +- SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_ECS), +- SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", + ALC662_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG), ++ SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA), ++ SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), + SND_PCI_QUIRK(0x1849, 0x3662, "ASROCK K10N78FullHD-hSLI R3.0", + ALC662_3ST_6ch_DIG), +- SND_PCI_QUIRK(0x1854, 0x2000, "ASUS H13-2000", ALC663_ASUS_H13), +- SND_PCI_QUIRK(0x1854, 0x2001, "ASUS H13-2001", ALC663_ASUS_H13), +- SND_PCI_QUIRK(0x1854, 0x2002, "ASUS H13-2002", ALC663_ASUS_H13), ++ SND_PCI_QUIRK_MASK(0x1854, 0xf000, 0x2000, "ASUS H13-200x", ++ ALC663_ASUS_H13), + {} + }; + + static struct alc_config_preset alc662_presets[] = { + [ALC662_3ST_2ch_DIG] = { +- .mixers = { alc662_3ST_2ch_mixer, alc662_capture_mixer }, ++ .mixers = { alc662_3ST_2ch_mixer }, + .init_verbs = { alc662_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, +@@ -15966,8 +16493,7 @@ static struct alc_config_preset alc662_presets[] = { + .input_mux = &alc662_capture_source, + }, + [ALC662_3ST_6ch_DIG] = { +- .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer, +- alc662_capture_mixer }, ++ .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer }, + .init_verbs = { alc662_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, +@@ -15979,8 +16505,7 @@ static struct alc_config_preset alc662_presets[] = { + .input_mux = &alc662_capture_source, + }, + [ALC662_3ST_6ch] = { +- .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer, +- alc662_capture_mixer }, ++ .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer }, + .init_verbs = { alc662_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, +@@ -15990,8 +16515,7 @@ static struct alc_config_preset alc662_presets[] = { + .input_mux = &alc662_capture_source, + }, + [ALC662_5ST_DIG] = { +- .mixers = { alc662_base_mixer, alc662_chmode_mixer, +- alc662_capture_mixer }, ++ .mixers = { alc662_base_mixer, alc662_chmode_mixer }, + .init_verbs = { alc662_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, +@@ -16002,7 +16526,7 @@ static struct alc_config_preset alc662_presets[] = { + .input_mux = &alc662_capture_source, + }, + [ALC662_LENOVO_101E] = { +- .mixers = { alc662_lenovo_101e_mixer, alc662_capture_mixer }, ++ .mixers = { alc662_lenovo_101e_mixer }, + .init_verbs = { alc662_init_verbs, alc662_sue_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, +@@ -16013,7 +16537,7 @@ static struct alc_config_preset alc662_presets[] = { + .init_hook = alc662_lenovo_101e_all_automute, + }, + [ALC662_ASUS_EEEPC_P701] = { +- .mixers = { alc662_eeepc_p701_mixer, alc662_capture_mixer }, ++ .mixers = { alc662_eeepc_p701_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eeepc_sue_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), +@@ -16025,7 +16549,7 @@ static struct alc_config_preset alc662_presets[] = { + .init_hook = alc662_eeepc_inithook, + }, + [ALC662_ASUS_EEEPC_EP20] = { +- .mixers = { alc662_eeepc_ep20_mixer, alc662_capture_mixer, ++ .mixers = { alc662_eeepc_ep20_mixer, + alc662_chmode_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eeepc_ep20_sue_init_verbs }, +@@ -16038,7 +16562,7 @@ static struct alc_config_preset alc662_presets[] = { + .init_hook = alc662_eeepc_ep20_inithook, + }, + [ALC662_ECS] = { +- .mixers = { alc662_ecs_mixer, alc662_capture_mixer }, ++ .mixers = { alc662_ecs_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_ecs_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), +@@ -16050,7 +16574,7 @@ static struct alc_config_preset alc662_presets[] = { + .init_hook = alc662_eeepc_inithook, + }, + [ALC663_ASUS_M51VA] = { +- .mixers = { alc663_m51va_mixer, alc662_capture_mixer}, ++ .mixers = { alc663_m51va_mixer }, + .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, +@@ -16062,7 +16586,7 @@ static struct alc_config_preset alc662_presets[] = { + .init_hook = alc663_m51va_inithook, + }, + [ALC663_ASUS_G71V] = { +- .mixers = { alc663_g71v_mixer, alc662_capture_mixer}, ++ .mixers = { alc663_g71v_mixer }, + .init_verbs = { alc662_init_verbs, alc663_g71v_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, +@@ -16074,7 +16598,7 @@ static struct alc_config_preset alc662_presets[] = { + .init_hook = alc663_g71v_inithook, + }, + [ALC663_ASUS_H13] = { +- .mixers = { alc663_m51va_mixer, alc662_capture_mixer}, ++ .mixers = { alc663_m51va_mixer }, + .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, +@@ -16085,7 +16609,7 @@ static struct alc_config_preset alc662_presets[] = { + .init_hook = alc663_m51va_inithook, + }, + [ALC663_ASUS_G50V] = { +- .mixers = { alc663_g50v_mixer, alc662_capture_mixer}, ++ .mixers = { alc663_g50v_mixer }, + .init_verbs = { alc662_init_verbs, alc663_g50v_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, +@@ -16097,7 +16621,8 @@ static struct alc_config_preset alc662_presets[] = { + .init_hook = alc663_g50v_inithook, + }, + [ALC663_ASUS_MODE1] = { +- .mixers = { alc663_m51va_mixer, alc662_auto_capture_mixer }, ++ .mixers = { alc663_m51va_mixer }, ++ .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc663_21jd_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), +@@ -16111,7 +16636,8 @@ static struct alc_config_preset alc662_presets[] = { + .init_hook = alc663_mode1_inithook, + }, + [ALC662_ASUS_MODE2] = { +- .mixers = { alc662_1bjd_mixer, alc662_auto_capture_mixer }, ++ .mixers = { alc662_1bjd_mixer }, ++ .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc662_1bjd_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), +@@ -16124,7 +16650,8 @@ static struct alc_config_preset alc662_presets[] = { + .init_hook = alc662_mode2_inithook, + }, + [ALC663_ASUS_MODE3] = { +- .mixers = { alc663_two_hp_m1_mixer, alc662_auto_capture_mixer }, ++ .mixers = { alc663_two_hp_m1_mixer }, ++ .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc663_two_hp_amic_m1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), +@@ -16138,8 +16665,8 @@ static struct alc_config_preset alc662_presets[] = { + .init_hook = alc663_mode3_inithook, + }, + [ALC663_ASUS_MODE4] = { +- .mixers = { alc663_asus_21jd_clfe_mixer, +- alc662_auto_capture_mixer}, ++ .mixers = { alc663_asus_21jd_clfe_mixer }, ++ .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc663_21jd_amic_init_verbs}, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), +@@ -16153,8 +16680,8 @@ static struct alc_config_preset alc662_presets[] = { + .init_hook = alc663_mode4_inithook, + }, + [ALC663_ASUS_MODE5] = { +- .mixers = { alc663_asus_15jd_clfe_mixer, +- alc662_auto_capture_mixer }, ++ .mixers = { alc663_asus_15jd_clfe_mixer }, ++ .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc663_15jd_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), +@@ -16168,7 +16695,8 @@ static struct alc_config_preset alc662_presets[] = { + .init_hook = alc663_mode5_inithook, + }, + [ALC663_ASUS_MODE6] = { +- .mixers = { alc663_two_hp_m2_mixer, alc662_auto_capture_mixer }, ++ .mixers = { alc663_two_hp_m2_mixer }, ++ .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc663_two_hp_amic_m2_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), +@@ -16181,6 +16709,36 @@ static struct alc_config_preset alc662_presets[] = { + .unsol_event = alc663_mode6_unsol_event, + .init_hook = alc663_mode6_inithook, + }, ++ [ALC272_DELL] = { ++ .mixers = { alc663_m51va_mixer }, ++ .cap_mixer = alc272_auto_capture_mixer, ++ .init_verbs = { alc662_init_verbs, alc272_dell_init_verbs }, ++ .num_dacs = ARRAY_SIZE(alc272_dac_nids), ++ .dac_nids = alc662_dac_nids, ++ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), ++ .adc_nids = alc272_adc_nids, ++ .num_adc_nids = ARRAY_SIZE(alc272_adc_nids), ++ .capsrc_nids = alc272_capsrc_nids, ++ .channel_mode = alc662_3ST_2ch_modes, ++ .input_mux = &alc663_m51va_capture_source, ++ .unsol_event = alc663_m51va_unsol_event, ++ .init_hook = alc663_m51va_inithook, ++ }, ++ [ALC272_DELL_ZM1] = { ++ .mixers = { alc663_m51va_mixer }, ++ .cap_mixer = alc662_auto_capture_mixer, ++ .init_verbs = { alc662_init_verbs, alc272_dell_zm1_init_verbs }, ++ .num_dacs = ARRAY_SIZE(alc272_dac_nids), ++ .dac_nids = alc662_dac_nids, ++ .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), ++ .adc_nids = alc662_adc_nids, ++ .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), ++ .capsrc_nids = alc662_capsrc_nids, ++ .channel_mode = alc662_3ST_2ch_modes, ++ .input_mux = &alc663_m51va_capture_source, ++ .unsol_event = alc663_m51va_unsol_event, ++ .init_hook = alc663_m51va_inithook, ++ }, + }; + + +@@ -16268,7 +16826,7 @@ static int alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, + + if (alc880_is_fixed_pin(pin)) { + nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); +- /* printk("DAC nid=%x\n",nid); */ ++ /* printk(KERN_DEBUG "DAC nid=%x\n",nid); */ + /* specify the DAC as the extra output */ + if (!spec->multiout.hp_nid) + spec->multiout.hp_nid = nid; +@@ -16298,26 +16856,58 @@ static int alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, + return 0; + } + ++/* return the index of the src widget from the connection list of the nid. ++ * return -1 if not found ++ */ ++static int alc662_input_pin_idx(struct hda_codec *codec, hda_nid_t nid, ++ hda_nid_t src) ++{ ++ hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; ++ int i, conns; ++ ++ conns = snd_hda_get_connections(codec, nid, conn_list, ++ ARRAY_SIZE(conn_list)); ++ if (conns < 0) ++ return -1; ++ for (i = 0; i < conns; i++) ++ if (conn_list[i] == src) ++ return i; ++ return -1; ++} ++ ++static int alc662_is_input_pin(struct hda_codec *codec, hda_nid_t nid) ++{ ++ unsigned int pincap = snd_hda_query_pin_caps(codec, nid); ++ return (pincap & AC_PINCAP_IN) != 0; ++} ++ + /* create playback/capture controls for input pins */ +-static int alc662_auto_create_analog_input_ctls(struct alc_spec *spec, ++static int alc662_auto_create_analog_input_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) + { +- struct hda_input_mux *imux = &spec->private_imux; ++ struct alc_spec *spec = codec->spec; ++ struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx; + + for (i = 0; i < AUTO_PIN_LAST; i++) { +- if (alc880_is_input_pin(cfg->input_pins[i])) { +- idx = alc880_input_pin_idx(cfg->input_pins[i]); +- err = new_analog_input(spec, cfg->input_pins[i], +- auto_pin_cfg_labels[i], +- idx, 0x0b); +- if (err < 0) +- return err; +- imux->items[imux->num_items].label = +- auto_pin_cfg_labels[i]; +- imux->items[imux->num_items].index = +- alc880_input_pin_idx(cfg->input_pins[i]); +- imux->num_items++; ++ if (alc662_is_input_pin(codec, cfg->input_pins[i])) { ++ idx = alc662_input_pin_idx(codec, 0x0b, ++ cfg->input_pins[i]); ++ if (idx >= 0) { ++ err = new_analog_input(spec, cfg->input_pins[i], ++ auto_pin_cfg_labels[i], ++ idx, 0x0b); ++ if (err < 0) ++ return err; ++ } ++ idx = alc662_input_pin_idx(codec, 0x22, ++ cfg->input_pins[i]); ++ if (idx >= 0) { ++ imux->items[imux->num_items].label = ++ auto_pin_cfg_labels[i]; ++ imux->items[imux->num_items].index = idx; ++ imux->num_items++; ++ } + } + } + return 0; +@@ -16367,7 +16957,6 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec) + alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); + } + +-#define alc662_is_input_pin(nid) alc880_is_input_pin(nid) + #define ALC662_PIN_CD_NID ALC880_PIN_CD_NID + + static void alc662_auto_init_analog_input(struct hda_codec *codec) +@@ -16377,12 +16966,10 @@ static void alc662_auto_init_analog_input(struct hda_codec *codec) + + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = spec->autocfg.input_pins[i]; +- if (alc662_is_input_pin(nid)) { +- snd_hda_codec_write(codec, nid, 0, +- AC_VERB_SET_PIN_WIDGET_CONTROL, +- (i <= AUTO_PIN_FRONT_MIC ? +- PIN_VREF80 : PIN_IN)); +- if (nid != ALC662_PIN_CD_NID) ++ if (alc662_is_input_pin(codec, nid)) { ++ alc_set_input_pin(codec, nid, i); ++ if (nid != ALC662_PIN_CD_NID && ++ (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); +@@ -16420,34 +17007,29 @@ static int alc662_parse_auto_config(struct hda_codec *codec) + "Headphone"); + if (err < 0) + return err; +- err = alc662_auto_create_analog_input_ctls(spec, &spec->autocfg); ++ err = alc662_auto_create_analog_input_ctls(codec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + +- if (spec->autocfg.dig_out_pin) ++ if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = ALC880_DIGOUT_NID; + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; ++ if (spec->kctls.list) ++ add_mixer(spec, spec->kctls.list); + + spec->num_mux_defs = 1; +- spec->input_mux = &spec->private_imux; ++ spec->input_mux = &spec->private_imux[0]; + +- spec->init_verbs[spec->num_init_verbs++] = alc662_auto_init_verbs; ++ add_verb(spec, alc662_auto_init_verbs); + if (codec->vendor_id == 0x10ec0663) +- spec->init_verbs[spec->num_init_verbs++] = +- alc663_auto_init_verbs; ++ add_verb(spec, alc663_auto_init_verbs); + + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; + +- spec->mixers[spec->num_mixers] = alc662_capture_mixer; +- spec->num_mixers++; +- +- store_pin_configs(codec); + return 1; + } + +@@ -16499,6 +17081,12 @@ static int patch_alc662(struct hda_codec *codec) + } + } + ++ err = snd_hda_attach_beep_device(codec, 0x1); ++ if (err < 0) { ++ alc_free(codec); ++ return err; ++ } ++ + if (board_config != ALC662_AUTO) + setup_preset(spec, &alc662_presets[board_config]); + +@@ -16522,6 +17110,14 @@ static int patch_alc662(struct hda_codec *codec) + spec->adc_nids = alc662_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids); + spec->capsrc_nids = alc662_capsrc_nids; ++ spec->capture_style = CAPT_MIX; ++ ++ if (!spec->cap_mixer) ++ set_capture_mixer(spec); ++ if (codec->vendor_id == 0x10ec0662) ++ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); ++ else ++ set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); + + spec->vmaster_nid = 0x02; + +@@ -16532,6 +17128,7 @@ static int patch_alc662(struct hda_codec *codec) + if (!spec->loopback.amplist) + spec->loopback.amplist = alc662_loopbacks; + #endif ++ codec->proc_widget_hook = print_realtek_coef; + + return 0; + } +@@ -16539,7 +17136,7 @@ static int patch_alc662(struct hda_codec *codec) + /* + * patch entries + */ +-struct hda_codec_preset snd_hda_preset_realtek[] = { ++static struct hda_codec_preset snd_hda_preset_realtek[] = { + { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, + { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, + { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 }, +@@ -16565,9 +17162,32 @@ struct hda_codec_preset snd_hda_preset_realtek[] = { + .patch = patch_alc882 }, /* should be patch_alc883() in future */ + { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, + { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc883 }, +- { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, + { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200", + .patch = patch_alc883 }, ++ { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, + { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 }, + {} /* terminator */ + }; ++ ++MODULE_ALIAS("snd-hda-codec-id:10ec*"); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Realtek HD-audio codec"); ++ ++static struct hda_codec_preset_list realtek_list = { ++ .preset = snd_hda_preset_realtek, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init patch_realtek_init(void) ++{ ++ return snd_hda_add_codec_preset(&realtek_list); ++} ++ ++static void __exit patch_realtek_exit(void) ++{ ++ snd_hda_delete_codec_preset(&realtek_list); ++} ++ ++module_init(patch_realtek_init) ++module_exit(patch_realtek_exit) +diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c +index 9332b63..43b436c 100644 +--- a/sound/pci/hda/patch_si3054.c ++++ b/sound/pci/hda/patch_si3054.c +@@ -28,7 +28,6 @@ + #include + #include "hda_codec.h" + #include "hda_local.h" +-#include "hda_patch.h" + + /* si3054 verbs */ + #define SI3054_VERB_READ_NODE 0x900 +@@ -283,7 +282,7 @@ static int patch_si3054(struct hda_codec *codec) + /* + * patch entries + */ +-struct hda_codec_preset snd_hda_preset_si3054[] = { ++static struct hda_codec_preset snd_hda_preset_si3054[] = { + { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 }, + { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 }, + { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 }, +@@ -301,3 +300,35 @@ struct hda_codec_preset snd_hda_preset_si3054[] = { + {} + }; + ++MODULE_ALIAS("snd-hda-codec-id:163c3055"); ++MODULE_ALIAS("snd-hda-codec-id:163c3155"); ++MODULE_ALIAS("snd-hda-codec-id:11c13026"); ++MODULE_ALIAS("snd-hda-codec-id:11c13055"); ++MODULE_ALIAS("snd-hda-codec-id:11c13155"); ++MODULE_ALIAS("snd-hda-codec-id:10573055"); ++MODULE_ALIAS("snd-hda-codec-id:10573057"); ++MODULE_ALIAS("snd-hda-codec-id:10573155"); ++MODULE_ALIAS("snd-hda-codec-id:11063288"); ++MODULE_ALIAS("snd-hda-codec-id:15433155"); ++MODULE_ALIAS("snd-hda-codec-id:18540018"); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Si3054 HD-audio modem codec"); ++ ++static struct hda_codec_preset_list si3054_list = { ++ .preset = snd_hda_preset_si3054, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init patch_si3054_init(void) ++{ ++ return snd_hda_add_codec_preset(&si3054_list); ++} ++ ++static void __exit patch_si3054_exit(void) ++{ ++ snd_hda_delete_codec_preset(&si3054_list); ++} ++ ++module_init(patch_si3054_init) ++module_exit(patch_si3054_exit) +diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c +index 5e89424..d2fd8ef 100644 +--- a/sound/pci/hda/patch_sigmatel.c ++++ b/sound/pci/hda/patch_sigmatel.c +@@ -30,19 +30,20 @@ + #include + #include + #include ++#include + #include "hda_codec.h" + #include "hda_local.h" +-#include "hda_patch.h" + #include "hda_beep.h" + +-#define NUM_CONTROL_ALLOC 32 +- +-#define STAC_VREF_EVENT 0x00 +-#define STAC_INSERT_EVENT 0x10 +-#define STAC_PWR_EVENT 0x20 +-#define STAC_HP_EVENT 0x30 ++enum { ++ STAC_VREF_EVENT = 1, ++ STAC_INSERT_EVENT, ++ STAC_PWR_EVENT, ++ STAC_HP_EVENT, ++}; + + enum { ++ STAC_AUTO, + STAC_REF, + STAC_9200_OQO, + STAC_9200_DELL_D21, +@@ -62,6 +63,7 @@ enum { + }; + + enum { ++ STAC_9205_AUTO, + STAC_9205_REF, + STAC_9205_DELL_M42, + STAC_9205_DELL_M43, +@@ -71,6 +73,7 @@ enum { + }; + + enum { ++ STAC_92HD73XX_AUTO, + STAC_92HD73XX_NO_JD, /* no jack-detection */ + STAC_92HD73XX_REF, + STAC_DELL_M6_AMIC, +@@ -81,21 +84,27 @@ enum { + }; + + enum { ++ STAC_92HD83XXX_AUTO, + STAC_92HD83XXX_REF, ++ STAC_92HD83XXX_PWR_REF, ++ STAC_DELL_S14, + STAC_92HD83XXX_MODELS + }; + + enum { ++ STAC_92HD71BXX_AUTO, + STAC_92HD71BXX_REF, + STAC_DELL_M4_1, + STAC_DELL_M4_2, + STAC_DELL_M4_3, + STAC_HP_M4, + STAC_HP_DV5, ++ STAC_HP_HDX, + STAC_92HD71BXX_MODELS + }; + + enum { ++ STAC_925x_AUTO, + STAC_925x_REF, + STAC_M1, + STAC_M1_2, +@@ -108,6 +117,7 @@ enum { + }; + + enum { ++ STAC_922X_AUTO, + STAC_D945_REF, + STAC_D945GTP3, + STAC_D945GTP5, +@@ -135,15 +145,36 @@ enum { + }; + + enum { ++ STAC_927X_AUTO, + STAC_D965_REF_NO_JD, /* no jack-detection */ + STAC_D965_REF, + STAC_D965_3ST, + STAC_D965_5ST, ++ STAC_D965_5ST_NO_FP, + STAC_DELL_3ST, + STAC_DELL_BIOS, + STAC_927X_MODELS + }; + ++enum { ++ STAC_9872_AUTO, ++ STAC_9872_VAIO, ++ STAC_9872_MODELS ++}; ++ ++struct sigmatel_event { ++ hda_nid_t nid; ++ unsigned char type; ++ unsigned char tag; ++ int data; ++}; ++ ++struct sigmatel_jack { ++ hda_nid_t nid; ++ int type; ++ struct snd_jack *jack; ++}; ++ + struct sigmatel_spec { + struct snd_kcontrol_new *mixers[4]; + unsigned int num_mixers; +@@ -151,8 +182,6 @@ struct sigmatel_spec { + int board_config; + unsigned int eapd_switch: 1; + unsigned int surr_switch: 1; +- unsigned int line_switch: 1; +- unsigned int mic_switch: 1; + unsigned int alt_switch: 1; + unsigned int hp_detect: 1; + unsigned int spdif_mute: 1; +@@ -169,6 +198,7 @@ struct sigmatel_spec { + unsigned int stream_delay; + + /* analog loopback */ ++ struct snd_kcontrol_new *aloopback_ctl; + unsigned char aloopback_mask; + unsigned char aloopback_shift; + +@@ -178,12 +208,20 @@ struct sigmatel_spec { + hda_nid_t *pwr_nids; + hda_nid_t *dac_list; + ++ /* jack detection */ ++ struct snd_array jacks; ++ ++ /* events */ ++ struct snd_array events; ++ + /* playback */ + struct hda_input_mux *mono_mux; + struct hda_input_mux *amp_mux; + unsigned int cur_mmux; + struct hda_multi_out multiout; + hda_nid_t dac_nids[5]; ++ hda_nid_t hp_dacs[5]; ++ hda_nid_t speaker_dacs[5]; + + int volume_offset; + +@@ -208,8 +246,6 @@ struct sigmatel_spec { + /* pin widgets */ + hda_nid_t *pin_nids; + unsigned int num_pins; +- unsigned int *pin_configs; +- unsigned int *bios_pin_configs; + + /* codec specific stuff */ + struct hda_verb *init; +@@ -230,15 +266,16 @@ struct sigmatel_spec { + /* i/o switches */ + unsigned int io_switch[2]; + unsigned int clfe_swap; +- unsigned int hp_switch; /* NID of HP as line-out */ ++ hda_nid_t line_switch; /* shared line-in for input and output */ ++ hda_nid_t mic_switch; /* shared mic-in for input and output */ ++ hda_nid_t hp_switch; /* NID of HP as line-out */ + unsigned int aloopback; + + struct hda_pcm pcm_rec[2]; /* PCM information */ + + /* dynamic controls and input_mux */ + struct auto_pin_cfg autocfg; +- unsigned int num_kctl_alloc, num_kctl_used; +- struct snd_kcontrol_new *kctl_alloc; ++ struct snd_array kctls; + struct hda_input_mux private_dimux; + struct hda_input_mux private_imux; + struct hda_input_mux private_smux; +@@ -282,9 +319,6 @@ static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = { + }; + + #define STAC92HD73_DAC_COUNT 5 +-static hda_nid_t stac92hd73xx_dac_nids[STAC92HD73_DAC_COUNT] = { +- 0x15, 0x16, 0x17, 0x18, 0x19, +-}; + + static hda_nid_t stac92hd73xx_mux_nids[4] = { + 0x28, 0x29, 0x2a, 0x2b, +@@ -303,11 +337,7 @@ static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = { + 0x11, 0x12, 0 + }; + +-#define STAC92HD81_DAC_COUNT 2 + #define STAC92HD83_DAC_COUNT 3 +-static hda_nid_t stac92hd83xxx_dac_nids[STAC92HD73_DAC_COUNT] = { +- 0x13, 0x14, 0x22, +-}; + + static hda_nid_t stac92hd83xxx_dmux_nids[2] = { + 0x17, 0x18, +@@ -326,7 +356,11 @@ static hda_nid_t stac92hd83xxx_slave_dig_outs[2] = { + }; + + static unsigned int stac92hd83xxx_pwr_mapping[4] = { +- 0x03, 0x0c, 0x10, 0x40, ++ 0x03, 0x0c, 0x20, 0x40, ++}; ++ ++static hda_nid_t stac92hd83xxx_amp_nids[1] = { ++ 0xc, + }; + + static hda_nid_t stac92hd71bxx_pwr_nids[3] = { +@@ -349,10 +383,6 @@ static hda_nid_t stac92hd71bxx_smux_nids[2] = { + 0x24, 0x25, + }; + +-static hda_nid_t stac92hd71bxx_dac_nids[1] = { +- 0x10, /*0x11, */ +-}; +- + #define STAC92HD71BXX_NUM_DMICS 2 + static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = { + 0x18, 0x19, 0 +@@ -391,6 +421,10 @@ static hda_nid_t stac922x_mux_nids[2] = { + 0x12, 0x13, + }; + ++static hda_nid_t stac927x_slave_dig_outs[2] = { ++ 0x1f, 0, ++}; ++ + static hda_nid_t stac927x_adc_nids[3] = { + 0x07, 0x08, 0x09 + }; +@@ -463,15 +497,21 @@ static hda_nid_t stac92hd73xx_pin_nids[13] = { + 0x14, 0x22, 0x23 + }; + +-static hda_nid_t stac92hd83xxx_pin_nids[14] = { ++static hda_nid_t stac92hd83xxx_pin_nids[10] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, +- 0x0f, 0x10, 0x11, 0x12, 0x13, +- 0x1d, 0x1e, 0x1f, 0x20 ++ 0x0f, 0x10, 0x11, 0x1f, 0x20, ++}; ++ ++#define STAC92HD71BXX_NUM_PINS 13 ++static hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = { ++ 0x0a, 0x0b, 0x0c, 0x0d, 0x00, ++ 0x00, 0x14, 0x18, 0x19, 0x1e, ++ 0x1f, 0x20, 0x27 + }; +-static hda_nid_t stac92hd71bxx_pin_nids[11] = { ++static hda_nid_t stac92hd71bxx_pin_nids_6port[STAC92HD71BXX_NUM_PINS] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x14, 0x18, 0x19, 0x1e, +- 0x1f, ++ 0x1f, 0x20, 0x27 + }; + + static hda_nid_t stac927x_pin_nids[14] = { +@@ -584,12 +624,12 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol, + else + nid = codec->slave_dig_outs[smux_idx - 1]; + if (spec->cur_smux[smux_idx] == smux->num_items - 1) +- val = AMP_OUT_MUTE; ++ val = HDA_AMP_MUTE; + else +- val = AMP_OUT_UNMUTE; ++ val = 0; + /* un/mute SPDIF out */ +- snd_hda_codec_write_cache(codec, nid, 0, +- AC_VERB_SET_AMP_GAIN_MUTE, val); ++ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, ++ HDA_AMP_MUTE, val); + } + return 0; + } +@@ -754,10 +794,6 @@ static struct hda_verb stac9200_eapd_init[] = { + static struct hda_verb stac92hd73xx_6ch_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, +- /* setup audio connections */ +- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00}, +- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01}, +- { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, +@@ -776,10 +812,6 @@ static struct hda_verb dell_eq_core_init[] = { + /* set master volume to max value without distortion + * and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec}, +- /* setup audio connections */ +- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, +- { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x02}, +- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, +@@ -793,10 +825,6 @@ static struct hda_verb dell_eq_core_init[] = { + + static struct hda_verb dell_m6_core_init[] = { + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, +- /* setup audio connections */ +- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, +- { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, +- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, +@@ -811,13 +839,6 @@ static struct hda_verb dell_m6_core_init[] = { + static struct hda_verb stac92hd73xx_8ch_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, +- /* setup audio connections */ +- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00}, +- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01}, +- { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02}, +- /* connect hp ports to dac3 */ +- { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03}, +- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, +@@ -835,15 +856,8 @@ static struct hda_verb stac92hd73xx_8ch_core_init[] = { + static struct hda_verb stac92hd73xx_10ch_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, +- /* setup audio connections */ +- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, +- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 }, +- { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 }, + /* dac3 is connected to import3 mux */ + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f}, +- /* connect hp ports to dac4 */ +- { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04}, +- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, +@@ -859,13 +873,9 @@ static struct hda_verb stac92hd73xx_10ch_core_init[] = { + }; + + static struct hda_verb stac92hd83xxx_core_init[] = { +- /* start of config #1 */ +- { 0xe, AC_VERB_SET_CONNECT_SEL, 0x3}, +- +- /* start of config #2 */ +- { 0xa, AC_VERB_SET_CONNECT_SEL, 0x0}, +- { 0xb, AC_VERB_SET_CONNECT_SEL, 0x0}, +- { 0xd, AC_VERB_SET_CONNECT_SEL, 0x1}, ++ { 0xa, AC_VERB_SET_CONNECT_SEL, 0x1}, ++ { 0xb, AC_VERB_SET_CONNECT_SEL, 0x1}, ++ { 0xd, AC_VERB_SET_CONNECT_SEL, 0x0}, + + /* power state controls amps */ + { 0x01, AC_VERB_SET_EAPD, 1 << 2}, +@@ -875,30 +885,25 @@ static struct hda_verb stac92hd83xxx_core_init[] = { + static struct hda_verb stac92hd71bxx_core_init[] = { + /* set master volume and direct control */ + { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, +- /* connect headphone jack to dac1 */ +- { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, +- /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */ +- { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +- { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {} + }; + +-#define HD_DISABLE_PORTF 2 ++#define HD_DISABLE_PORTF 1 + static struct hda_verb stac92hd71bxx_analog_core_init[] = { + /* start of config #1 */ + + /* connect port 0f to audio mixer */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2}, +- /* unmute right and left channels for node 0x0f */ +- { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* start of config #2 */ + + /* set master volume and direct control */ + { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, +- /* connect headphone jack to dac1 */ +- { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, +- /* unmute right and left channels for nodes 0x0a, 0xd */ ++ {} ++}; ++ ++static struct hda_verb stac92hd71bxx_unmute_core_init[] = { ++ /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */ ++ { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {} +@@ -979,16 +984,6 @@ static struct hda_verb stac9205_core_init[] = { + .private_value = HDA_COMPOSE_AMP_VAL(nid, chs, idx, dir) \ + } + +-#define STAC_INPUT_SOURCE(cnt) \ +- { \ +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ +- .name = "Input Source", \ +- .count = cnt, \ +- .info = stac92xx_mux_enum_info, \ +- .get = stac92xx_mux_enum_get, \ +- .put = stac92xx_mux_enum_put, \ +- } +- + #define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ +@@ -1003,7 +998,6 @@ static struct hda_verb stac9205_core_init[] = { + static struct snd_kcontrol_new stac9200_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT), +- STAC_INPUT_SOURCE(1), + HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT), + { } /* end */ +@@ -1028,8 +1022,6 @@ static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = { + HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), + +- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3), +- + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), + +@@ -1039,9 +1031,22 @@ static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = { + { } /* end */ + }; + +-static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = { ++static struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = { ++ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3), ++ {} ++}; ++ ++static struct snd_kcontrol_new stac92hd73xx_8ch_loopback[] = { + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4), ++ {} ++}; + ++static struct snd_kcontrol_new stac92hd73xx_10ch_loopback[] = { ++ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5), ++ {} ++}; ++ ++static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = { + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), + +@@ -1066,8 +1071,6 @@ static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = { + }; + + static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = { +- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5), +- + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), + +@@ -1099,29 +1102,26 @@ static struct snd_kcontrol_new stac92hd83xxx_mixer[] = { + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_OUTPUT), + +- HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0, HDA_INPUT), +- HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0, HDA_INPUT), ++ HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0x3, HDA_INPUT), ++ HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0x3, HDA_INPUT), + +- HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x1, HDA_INPUT), +- HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x1, HDA_INPUT), ++ HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x4, HDA_INPUT), ++ HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x4, HDA_INPUT), + +- HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x2, HDA_INPUT), +- HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x2, HDA_INPUT), ++ HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x0, HDA_INPUT), ++ HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x0, HDA_INPUT), + +- HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x3, HDA_INPUT), +- HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x3, HDA_INPUT), ++ HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x2, HDA_INPUT), ++ HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x2, HDA_INPUT), + + /* +- HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x4, HDA_INPUT), +- HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x4, HDA_INPUT), ++ HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x1, HDA_INPUT), ++ HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x1, HDA_INPUT), + */ + { } /* end */ + }; + + static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = { +- STAC_INPUT_SOURCE(2), +- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2), +- + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), + +@@ -1147,10 +1147,11 @@ static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = { + { } /* end */ + }; + +-static struct snd_kcontrol_new stac92hd71bxx_mixer[] = { +- STAC_INPUT_SOURCE(2), +- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2), ++static struct snd_kcontrol_new stac92hd71bxx_loopback[] = { ++ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2) ++}; + ++static struct snd_kcontrol_new stac92hd71bxx_mixer[] = { + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), + +@@ -1162,16 +1163,12 @@ static struct snd_kcontrol_new stac92hd71bxx_mixer[] = { + static struct snd_kcontrol_new stac925x_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT), +- STAC_INPUT_SOURCE(1), + HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x14, 0, HDA_OUTPUT), + { } /* end */ + }; + + static struct snd_kcontrol_new stac9205_mixer[] = { +- STAC_INPUT_SOURCE(2), +- STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1), +- + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT), + +@@ -1180,9 +1177,13 @@ static struct snd_kcontrol_new stac9205_mixer[] = { + { } /* end */ + }; + ++static struct snd_kcontrol_new stac9205_loopback[] = { ++ STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1), ++ {} ++}; ++ + /* This needs to be generated dynamically based on sequence */ + static struct snd_kcontrol_new stac922x_mixer[] = { +- STAC_INPUT_SOURCE(2), + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_INPUT), + +@@ -1193,9 +1194,6 @@ static struct snd_kcontrol_new stac922x_mixer[] = { + + + static struct snd_kcontrol_new stac927x_mixer[] = { +- STAC_INPUT_SOURCE(3), +- STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1), +- + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT), + +@@ -1207,6 +1205,11 @@ static struct snd_kcontrol_new stac927x_mixer[] = { + { } /* end */ + }; + ++static struct snd_kcontrol_new stac927x_loopback[] = { ++ STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1), ++ {} ++}; ++ + static struct snd_kcontrol_new stac_dmux_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Input Source", +@@ -1232,10 +1235,7 @@ static const char *slave_vols[] = { + "LFE Playback Volume", + "Side Playback Volume", + "Headphone Playback Volume", +- "Headphone Playback Volume", + "Speaker Playback Volume", +- "External Speaker Playback Volume", +- "Speaker2 Playback Volume", + NULL + }; + +@@ -1246,17 +1246,19 @@ static const char *slave_sws[] = { + "LFE Playback Switch", + "Side Playback Switch", + "Headphone Playback Switch", +- "Headphone Playback Switch", + "Speaker Playback Switch", +- "External Speaker Playback Switch", +- "Speaker2 Playback Switch", + "IEC958 Playback Switch", + NULL + }; + ++static void stac92xx_free_kctls(struct hda_codec *codec); ++static int stac92xx_add_jack(struct hda_codec *codec, hda_nid_t nid, int type); ++ + static int stac92xx_build_controls(struct hda_codec *codec) + { + struct sigmatel_spec *spec = codec->spec; ++ struct auto_pin_cfg *cfg = &spec->autocfg; ++ hda_nid_t nid; + int err; + int i; + +@@ -1271,7 +1273,7 @@ static int stac92xx_build_controls(struct hda_codec *codec) + } + if (spec->num_dmuxes > 0) { + stac_dmux_mixer.count = spec->num_dmuxes; +- err = snd_ctl_add(codec->bus->card, ++ err = snd_hda_ctl_add(codec, + snd_ctl_new1(&stac_dmux_mixer, codec)); + if (err < 0) + return err; +@@ -1287,7 +1289,7 @@ static int stac92xx_build_controls(struct hda_codec *codec) + spec->spdif_mute = 1; + } + stac_smux_mixer.count = spec->num_smuxes; +- err = snd_ctl_add(codec->bus->card, ++ err = snd_hda_ctl_add(codec, + snd_ctl_new1(&stac_smux_mixer, codec)); + if (err < 0) + return err; +@@ -1328,6 +1330,44 @@ static int stac92xx_build_controls(struct hda_codec *codec) + return err; + } + ++ if (spec->aloopback_ctl && ++ snd_hda_get_bool_hint(codec, "loopback") == 1) { ++ err = snd_hda_add_new_ctls(codec, spec->aloopback_ctl); ++ if (err < 0) ++ return err; ++ } ++ ++ stac92xx_free_kctls(codec); /* no longer needed */ ++ ++ /* create jack input elements */ ++ if (spec->hp_detect) { ++ for (i = 0; i < cfg->hp_outs; i++) { ++ int type = SND_JACK_HEADPHONE; ++ nid = cfg->hp_pins[i]; ++ /* jack detection */ ++ if (cfg->hp_outs == i) ++ type |= SND_JACK_LINEOUT; ++ err = stac92xx_add_jack(codec, nid, type); ++ if (err < 0) ++ return err; ++ } ++ } ++ for (i = 0; i < cfg->line_outs; i++) { ++ err = stac92xx_add_jack(codec, cfg->line_out_pins[i], ++ SND_JACK_LINEOUT); ++ if (err < 0) ++ return err; ++ } ++ for (i = 0; i < AUTO_PIN_LAST; i++) { ++ nid = cfg->input_pins[i]; ++ if (nid) { ++ err = stac92xx_add_jack(codec, nid, ++ SND_JACK_MICROPHONE); ++ if (err < 0) ++ return err; ++ } ++ } ++ + return 0; + } + +@@ -1481,6 +1521,7 @@ static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = { + }; + + static const char *stac9200_models[STAC_9200_MODELS] = { ++ [STAC_AUTO] = "auto", + [STAC_REF] = "ref", + [STAC_9200_OQO] = "oqo", + [STAC_9200_DELL_D21] = "dell-d21", +@@ -1502,6 +1543,8 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_REF), ++ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, ++ "DFI LanParty", STAC_REF), + /* Dell laptops have BIOS problem */ + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a8, + "unknown Dell", STAC_9200_DELL_D21), +@@ -1624,6 +1667,7 @@ static unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = { + }; + + static const char *stac925x_models[STAC_925x_MODELS] = { ++ [STAC_925x_AUTO] = "auto", + [STAC_REF] = "ref", + [STAC_M1] = "m1", + [STAC_M1_2] = "m1-2", +@@ -1651,6 +1695,7 @@ static struct snd_pci_quirk stac925x_codec_id_cfg_tbl[] = { + static struct snd_pci_quirk stac925x_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), ++ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF), + SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF), + + /* Default table for unknown ID */ +@@ -1682,6 +1727,7 @@ static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { + }; + + static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = { ++ [STAC_92HD73XX_AUTO] = "auto", + [STAC_92HD73XX_NO_JD] = "no-jd", + [STAC_92HD73XX_REF] = "ref", + [STAC_DELL_M6_AMIC] = "dell-m6-amic", +@@ -1694,6 +1740,8 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_92HD73XX_REF), ++ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, ++ "DFI LanParty", STAC_92HD73XX_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254, + "Dell Studio 1535", STAC_DELL_M6_DMIC), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255, +@@ -1717,50 +1765,68 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { + {} /* terminator */ + }; + +-static unsigned int ref92hd83xxx_pin_configs[14] = { ++static unsigned int ref92hd83xxx_pin_configs[10] = { + 0x02214030, 0x02211010, 0x02a19020, 0x02170130, + 0x01014050, 0x01819040, 0x01014020, 0x90a3014e, +- 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x40f000f0, + 0x01451160, 0x98560170, + }; + ++static unsigned int dell_s14_pin_configs[10] = { ++ 0x02214030, 0x02211010, 0x02a19020, 0x01014050, ++ 0x40f000f0, 0x01819040, 0x40f000f0, 0x90a60160, ++ 0x40f000f0, 0x40f000f0, ++}; ++ + static unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = { + [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs, ++ [STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs, ++ [STAC_DELL_S14] = dell_s14_pin_configs, + }; + + static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { ++ [STAC_92HD83XXX_AUTO] = "auto", + [STAC_92HD83XXX_REF] = "ref", ++ [STAC_92HD83XXX_PWR_REF] = "mic-ref", ++ [STAC_DELL_S14] = "dell-s14", + }; + + static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_92HD83XXX_REF), ++ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, ++ "DFI LanParty", STAC_92HD83XXX_REF), ++ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba, ++ "unknown Dell", STAC_DELL_S14), + {} /* terminator */ + }; + +-static unsigned int ref92hd71bxx_pin_configs[11] = { ++static unsigned int ref92hd71bxx_pin_configs[STAC92HD71BXX_NUM_PINS] = { + 0x02214030, 0x02a19040, 0x01a19020, 0x01014010, + 0x0181302e, 0x01014010, 0x01019020, 0x90a000f0, +- 0x90a000f0, 0x01452050, 0x01452050, ++ 0x90a000f0, 0x01452050, 0x01452050, 0x00000000, ++ 0x00000000 + }; + +-static unsigned int dell_m4_1_pin_configs[11] = { ++static unsigned int dell_m4_1_pin_configs[STAC92HD71BXX_NUM_PINS] = { + 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110, + 0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0, +- 0x40f000f0, 0x4f0000f0, 0x4f0000f0, ++ 0x40f000f0, 0x4f0000f0, 0x4f0000f0, 0x00000000, ++ 0x00000000 + }; + +-static unsigned int dell_m4_2_pin_configs[11] = { ++static unsigned int dell_m4_2_pin_configs[STAC92HD71BXX_NUM_PINS] = { + 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110, + 0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0, +- 0x40f000f0, 0x044413b0, 0x044413b0, ++ 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000, ++ 0x00000000 + }; + +-static unsigned int dell_m4_3_pin_configs[11] = { ++static unsigned int dell_m4_3_pin_configs[STAC92HD71BXX_NUM_PINS] = { + 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110, + 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a000f0, +- 0x40f000f0, 0x044413b0, 0x044413b0, ++ 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000, ++ 0x00000000 + }; + + static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { +@@ -1770,33 +1836,38 @@ static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { + [STAC_DELL_M4_3] = dell_m4_3_pin_configs, + [STAC_HP_M4] = NULL, + [STAC_HP_DV5] = NULL, ++ [STAC_HP_HDX] = NULL, + }; + + static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { ++ [STAC_92HD71BXX_AUTO] = "auto", + [STAC_92HD71BXX_REF] = "ref", + [STAC_DELL_M4_1] = "dell-m4-1", + [STAC_DELL_M4_2] = "dell-m4-2", + [STAC_DELL_M4_3] = "dell-m4-3", + [STAC_HP_M4] = "hp-m4", + [STAC_HP_DV5] = "hp-dv5", ++ [STAC_HP_HDX] = "hp-hdx", + }; + + static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_92HD71BXX_REF), +- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f2, +- "HP dv5", STAC_HP_M4), +- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f4, +- "HP dv7", STAC_HP_M4), +- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f7, +- "HP dv4", STAC_HP_DV5), +- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fc, +- "HP dv7", STAC_HP_M4), +- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3603, +- "HP dv5", STAC_HP_DV5), ++ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, ++ "DFI LanParty", STAC_92HD71BXX_REF), ++ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080, ++ "HP", STAC_HP_DV5), ++ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0, ++ "HP dv4-7", STAC_HP_DV5), ++ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3600, ++ "HP dv4-7", STAC_HP_DV5), ++ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3610, ++ "HP HDX", STAC_HP_HDX), /* HDX18 */ + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a, +- "unknown HP", STAC_HP_M4), ++ "HP mini 1000", STAC_HP_M4), ++ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b, ++ "HP HDX", STAC_HP_HDX), /* HDX16 */ + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233, + "unknown Dell", STAC_DELL_M4_1), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234, +@@ -1948,6 +2019,7 @@ static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { + }; + + static const char *stac922x_models[STAC_922X_MODELS] = { ++ [STAC_922X_AUTO] = "auto", + [STAC_D945_REF] = "ref", + [STAC_D945GTP5] = "5stack", + [STAC_D945GTP3] = "3stack", +@@ -1975,6 +2047,8 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_D945_REF), ++ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, ++ "DFI LanParty", STAC_D945_REF), + /* Intel 945G based systems */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0101, + "Intel D945G", STAC_D945GTP3), +@@ -2055,31 +2129,7 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = { + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d7, + "Dell XPS M1210", STAC_922X_DELL_M82), + /* ECS/PC Chips boards */ +- SND_PCI_QUIRK(0x1019, 0x2144, +- "ECS/PC chips", STAC_ECS_202), +- SND_PCI_QUIRK(0x1019, 0x2608, +- "ECS/PC chips", STAC_ECS_202), +- SND_PCI_QUIRK(0x1019, 0x2633, +- "ECS/PC chips P17G/1333", STAC_ECS_202), +- SND_PCI_QUIRK(0x1019, 0x2811, +- "ECS/PC chips", STAC_ECS_202), +- SND_PCI_QUIRK(0x1019, 0x2812, +- "ECS/PC chips", STAC_ECS_202), +- SND_PCI_QUIRK(0x1019, 0x2813, +- "ECS/PC chips", STAC_ECS_202), +- SND_PCI_QUIRK(0x1019, 0x2814, +- "ECS/PC chips", STAC_ECS_202), +- SND_PCI_QUIRK(0x1019, 0x2815, +- "ECS/PC chips", STAC_ECS_202), +- SND_PCI_QUIRK(0x1019, 0x2816, +- "ECS/PC chips", STAC_ECS_202), +- SND_PCI_QUIRK(0x1019, 0x2817, +- "ECS/PC chips", STAC_ECS_202), +- SND_PCI_QUIRK(0x1019, 0x2818, +- "ECS/PC chips", STAC_ECS_202), +- SND_PCI_QUIRK(0x1019, 0x2819, +- "ECS/PC chips", STAC_ECS_202), +- SND_PCI_QUIRK(0x1019, 0x2820, ++ SND_PCI_QUIRK_MASK(0x1019, 0xf000, 0x2000, + "ECS/PC chips", STAC_ECS_202), + {} /* terminator */ + }; +@@ -2105,6 +2155,13 @@ static unsigned int d965_5st_pin_configs[14] = { + 0x40000100, 0x40000100 + }; + ++static unsigned int d965_5st_no_fp_pin_configs[14] = { ++ 0x40000100, 0x40000100, 0x0181304e, 0x01014010, ++ 0x01a19040, 0x01011012, 0x01016011, 0x40000100, ++ 0x40000100, 0x40000100, 0x40000100, 0x01442070, ++ 0x40000100, 0x40000100 ++}; ++ + static unsigned int dell_3st_pin_configs[14] = { + 0x02211230, 0x02a11220, 0x01a19040, 0x01114210, + 0x01111212, 0x01116211, 0x01813050, 0x01112214, +@@ -2117,15 +2174,18 @@ static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { + [STAC_D965_REF] = ref927x_pin_configs, + [STAC_D965_3ST] = d965_3st_pin_configs, + [STAC_D965_5ST] = d965_5st_pin_configs, ++ [STAC_D965_5ST_NO_FP] = d965_5st_no_fp_pin_configs, + [STAC_DELL_3ST] = dell_3st_pin_configs, + [STAC_DELL_BIOS] = NULL, + }; + + static const char *stac927x_models[STAC_927X_MODELS] = { ++ [STAC_927X_AUTO] = "auto", + [STAC_D965_REF_NO_JD] = "ref-no-jd", + [STAC_D965_REF] = "ref", + [STAC_D965_3ST] = "3stack", + [STAC_D965_5ST] = "5stack", ++ [STAC_D965_5ST_NO_FP] = "5stack-no-fp", + [STAC_DELL_3ST] = "dell-3stack", + [STAC_DELL_BIOS] = "dell-bios", + }; +@@ -2134,26 +2194,16 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_D965_REF), ++ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, ++ "DFI LanParty", STAC_D965_REF), + /* Intel 946 based systems */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x3d01, "Intel D946", STAC_D965_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xa301, "Intel D946", STAC_D965_3ST), + /* 965 based 3 stack systems */ +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2116, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2115, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2114, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2113, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2112, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2111, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2110, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2009, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2008, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2007, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2006, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2005, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2004, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST), ++ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2100, ++ "Intel D965", STAC_D965_3ST), ++ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2000, ++ "Intel D965", STAC_D965_3ST), + /* Dell 3 stack systems */ + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell Dimension E520", STAC_DELL_3ST), +@@ -2169,15 +2219,10 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = { + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS), + /* 965 based 5 stack systems */ +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2304, "Intel D965", STAC_D965_5ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2305, "Intel D965", STAC_D965_5ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2501, "Intel D965", STAC_D965_5ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2502, "Intel D965", STAC_D965_5ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2503, "Intel D965", STAC_D965_5ST), +- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2504, "Intel D965", STAC_D965_5ST), ++ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2300, ++ "Intel D965", STAC_D965_5ST), ++ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2500, ++ "Intel D965", STAC_D965_5ST), + {} /* terminator */ + }; + +@@ -2234,6 +2279,7 @@ static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = { + }; + + static const char *stac9205_models[STAC_9205_MODELS] = { ++ [STAC_9205_AUTO] = "auto", + [STAC_9205_REF] = "ref", + [STAC_9205_DELL_M42] = "dell-m42", + [STAC_9205_DELL_M43] = "dell-m43", +@@ -2245,6 +2291,8 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_9205_REF), ++ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, ++ "DFI LanParty", STAC_9205_REF), + /* Dell */ + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1, + "unknown Dell", STAC_9205_DELL_M42), +@@ -2281,66 +2329,19 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = { + {} /* terminator */ + }; + +-static int stac92xx_save_bios_config_regs(struct hda_codec *codec) ++static void stac92xx_set_config_regs(struct hda_codec *codec, ++ unsigned int *pincfgs) + { + int i; + struct sigmatel_spec *spec = codec->spec; +- +- if (! spec->bios_pin_configs) { +- spec->bios_pin_configs = kcalloc(spec->num_pins, +- sizeof(*spec->bios_pin_configs), GFP_KERNEL); +- if (! spec->bios_pin_configs) +- return -ENOMEM; +- } +- +- for (i = 0; i < spec->num_pins; i++) { +- hda_nid_t nid = spec->pin_nids[i]; +- unsigned int pin_cfg; +- +- pin_cfg = snd_hda_codec_read(codec, nid, 0, +- AC_VERB_GET_CONFIG_DEFAULT, 0x00); +- snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n", +- nid, pin_cfg); +- spec->bios_pin_configs[i] = pin_cfg; +- } +- +- return 0; +-} + +-static void stac92xx_set_config_reg(struct hda_codec *codec, +- hda_nid_t pin_nid, unsigned int pin_config) +-{ +- int i; +- snd_hda_codec_write(codec, pin_nid, 0, +- AC_VERB_SET_CONFIG_DEFAULT_BYTES_0, +- pin_config & 0x000000ff); +- snd_hda_codec_write(codec, pin_nid, 0, +- AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, +- (pin_config & 0x0000ff00) >> 8); +- snd_hda_codec_write(codec, pin_nid, 0, +- AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, +- (pin_config & 0x00ff0000) >> 16); +- snd_hda_codec_write(codec, pin_nid, 0, +- AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, +- pin_config >> 24); +- i = snd_hda_codec_read(codec, pin_nid, 0, +- AC_VERB_GET_CONFIG_DEFAULT, +- 0x00); +- snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n", +- pin_nid, i); +-} +- +-static void stac92xx_set_config_regs(struct hda_codec *codec) +-{ +- int i; +- struct sigmatel_spec *spec = codec->spec; +- +- if (!spec->pin_configs) +- return; ++ if (!pincfgs) ++ return; + + for (i = 0; i < spec->num_pins; i++) +- stac92xx_set_config_reg(codec, spec->pin_nids[i], +- spec->pin_configs[i]); ++ if (spec->pin_nids[i] && pincfgs[i]) ++ snd_hda_codec_set_pincfg(codec, spec->pin_nids[i], ++ pincfgs[i]); + } + + /* +@@ -2405,6 +2406,15 @@ static int stac92xx_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + stream_tag, format, substream); + } + ++static int stac92xx_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, ++ struct hda_codec *codec, ++ struct snd_pcm_substream *substream) ++{ ++ struct sigmatel_spec *spec = codec->spec; ++ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); ++} ++ ++ + /* + * Analog capture callbacks + */ +@@ -2419,7 +2429,7 @@ static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + + if (spec->powerdown_adcs) { + msleep(40); +- snd_hda_codec_write_cache(codec, nid, 0, ++ snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + } + snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); +@@ -2435,7 +2445,7 @@ static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + + snd_hda_codec_cleanup_stream(codec, nid); + if (spec->powerdown_adcs) +- snd_hda_codec_write_cache(codec, nid, 0, ++ snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + return 0; + } +@@ -2448,7 +2458,8 @@ static struct hda_pcm_stream stac92xx_pcm_digital_playback = { + .ops = { + .open = stac92xx_dig_playback_pcm_open, + .close = stac92xx_dig_playback_pcm_close, +- .prepare = stac92xx_dig_playback_pcm_prepare ++ .prepare = stac92xx_dig_playback_pcm_prepare, ++ .cleanup = stac92xx_dig_playback_pcm_cleanup + }, + }; + +@@ -2520,7 +2531,7 @@ static int stac92xx_build_pcms(struct hda_codec *codec) + codec->num_pcms++; + info++; + info->name = "STAC92xx Digital"; +- info->pcm_type = HDA_PCM_TYPE_SPDIF; ++ info->pcm_type = spec->autocfg.dig_out_type[0]; + if (spec->multiout.dig_out_nid) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; +@@ -2536,8 +2547,7 @@ static int stac92xx_build_pcms(struct hda_codec *codec) + + static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid) + { +- unsigned int pincap = snd_hda_param_read(codec, nid, +- AC_PAR_PIN_CAP); ++ unsigned int pincap = snd_hda_query_pin_caps(codec, nid); + pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; + if (pincap & AC_PINCAP_VREF_100) + return AC_PINCTL_VREF_100; +@@ -2569,19 +2579,22 @@ static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol, + return 0; + } + ++static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid, ++ unsigned char type); ++ + static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + int nid = kcontrol->private_value; +- ++ + spec->hp_switch = ucontrol->value.integer.value[0] ? nid : 0; + + /* check to be sure that the ports are upto date with + * switch changes + */ +- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); ++ stac_issue_unsol_event(codec, nid, STAC_HP_EVENT); + + return 1; + } +@@ -2621,7 +2634,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ + * appropriately according to the pin direction + */ + if (spec->hp_detect) +- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); ++ stac_issue_unsol_event(codec, nid, STAC_HP_EVENT); + + return 1; + } +@@ -2709,35 +2722,38 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = { + }; + + /* add dynamic controls */ +-static int stac92xx_add_control_temp(struct sigmatel_spec *spec, +- struct snd_kcontrol_new *ktemp, +- int idx, const char *name, +- unsigned long val) ++static struct snd_kcontrol_new * ++stac_control_new(struct sigmatel_spec *spec, ++ struct snd_kcontrol_new *ktemp, ++ const char *name) + { + struct snd_kcontrol_new *knew; + +- if (spec->num_kctl_used >= spec->num_kctl_alloc) { +- int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; +- +- knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */ +- if (! knew) +- return -ENOMEM; +- if (spec->kctl_alloc) { +- memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc); +- kfree(spec->kctl_alloc); +- } +- spec->kctl_alloc = knew; +- spec->num_kctl_alloc = num; +- } +- +- knew = &spec->kctl_alloc[spec->num_kctl_used]; ++ snd_array_init(&spec->kctls, sizeof(*knew), 32); ++ knew = snd_array_new(&spec->kctls); ++ if (!knew) ++ return NULL; + *knew = *ktemp; +- knew->index = idx; + knew->name = kstrdup(name, GFP_KERNEL); +- if (!knew->name) ++ if (!knew->name) { ++ /* roolback */ ++ memset(knew, 0, sizeof(*knew)); ++ spec->kctls.alloced--; ++ return NULL; ++ } ++ return knew; ++} ++ ++static int stac92xx_add_control_temp(struct sigmatel_spec *spec, ++ struct snd_kcontrol_new *ktemp, ++ int idx, const char *name, ++ unsigned long val) ++{ ++ struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name); ++ if (!knew) + return -ENOMEM; ++ knew->index = idx; + knew->private_value = val; +- spec->num_kctl_used++; + return 0; + } + +@@ -2758,70 +2774,75 @@ static inline int stac92xx_add_control(struct sigmatel_spec *spec, int type, + return stac92xx_add_control_idx(spec, type, 0, name, val); + } + +-/* flag inputs as additional dynamic lineouts */ +-static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg) ++static struct snd_kcontrol_new stac_input_src_temp = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "Input Source", ++ .info = stac92xx_mux_enum_info, ++ .get = stac92xx_mux_enum_get, ++ .put = stac92xx_mux_enum_put, ++}; ++ ++static int stac92xx_add_input_source(struct sigmatel_spec *spec) ++{ ++ struct snd_kcontrol_new *knew; ++ struct hda_input_mux *imux = &spec->private_imux; ++ ++ if (!spec->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); ++ if (!knew) ++ return -ENOMEM; ++ knew->count = spec->num_adcs; ++ return 0; ++} ++ ++/* check whether the line-input can be used as line-out */ ++static hda_nid_t check_line_out_switch(struct hda_codec *codec) + { + struct sigmatel_spec *spec = codec->spec; +- unsigned int wcaps, wtype; +- int i, num_dacs = 0; +- +- /* use the wcaps cache to count all DACs available for line-outs */ +- for (i = 0; i < codec->num_nodes; i++) { +- wcaps = codec->wcaps[i]; +- wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; ++ struct auto_pin_cfg *cfg = &spec->autocfg; ++ hda_nid_t nid; ++ unsigned int pincap; + +- if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) +- num_dacs++; +- } ++ if (cfg->line_out_type != AUTO_PIN_LINE_OUT) ++ return 0; ++ nid = cfg->input_pins[AUTO_PIN_LINE]; ++ pincap = snd_hda_query_pin_caps(codec, nid); ++ if (pincap & AC_PINCAP_OUT) ++ return nid; ++ return 0; ++} + +- snd_printdd("%s: total dac count=%d\n", __func__, num_dacs); +- +- switch (cfg->line_outs) { +- case 3: +- /* add line-in as side */ +- if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 3) { +- cfg->line_out_pins[cfg->line_outs] = +- cfg->input_pins[AUTO_PIN_LINE]; +- spec->line_switch = 1; +- cfg->line_outs++; +- } +- break; +- case 2: +- /* add line-in as clfe and mic as side */ +- if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 2) { +- cfg->line_out_pins[cfg->line_outs] = +- cfg->input_pins[AUTO_PIN_LINE]; +- spec->line_switch = 1; +- cfg->line_outs++; +- } +- if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 3) { +- cfg->line_out_pins[cfg->line_outs] = +- cfg->input_pins[AUTO_PIN_MIC]; +- spec->mic_switch = 1; +- cfg->line_outs++; +- } +- break; +- case 1: +- /* add line-in as surr and mic as clfe */ +- if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 1) { +- cfg->line_out_pins[cfg->line_outs] = +- cfg->input_pins[AUTO_PIN_LINE]; +- spec->line_switch = 1; +- cfg->line_outs++; +- } +- if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 2) { +- cfg->line_out_pins[cfg->line_outs] = +- cfg->input_pins[AUTO_PIN_MIC]; +- spec->mic_switch = 1; +- cfg->line_outs++; ++/* check whether the mic-input can be used as line-out */ ++static hda_nid_t check_mic_out_switch(struct hda_codec *codec) ++{ ++ struct sigmatel_spec *spec = codec->spec; ++ struct auto_pin_cfg *cfg = &spec->autocfg; ++ unsigned int def_conf, pincap; ++ unsigned int mic_pin; ++ ++ if (cfg->line_out_type != AUTO_PIN_LINE_OUT) ++ return 0; ++ mic_pin = AUTO_PIN_MIC; ++ for (;;) { ++ hda_nid_t nid = cfg->input_pins[mic_pin]; ++ def_conf = snd_hda_codec_get_pincfg(codec, nid); ++ /* some laptops have an internal analog microphone ++ * which can't be used as a output */ ++ if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) { ++ pincap = snd_hda_query_pin_caps(codec, nid); ++ if (pincap & AC_PINCAP_OUT) ++ return nid; + } +- break; ++ if (mic_pin == AUTO_PIN_MIC) ++ mic_pin = AUTO_PIN_FRONT_MIC; ++ else ++ break; + } +- + return 0; + } + +- + static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) + { + int i; +@@ -2834,6 +2855,61 @@ static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) + return 0; + } + ++static int check_all_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) ++{ ++ int i; ++ if (is_in_dac_nids(spec, nid)) ++ return 1; ++ for (i = 0; i < spec->autocfg.hp_outs; i++) ++ if (spec->hp_dacs[i] == nid) ++ return 1; ++ for (i = 0; i < spec->autocfg.speaker_outs; i++) ++ if (spec->speaker_dacs[i] == nid) ++ return 1; ++ return 0; ++} ++ ++static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid) ++{ ++ struct sigmatel_spec *spec = codec->spec; ++ int j, conn_len; ++ hda_nid_t conn[HDA_MAX_CONNECTIONS]; ++ unsigned int wcaps, wtype; ++ ++ conn_len = snd_hda_get_connections(codec, nid, conn, ++ HDA_MAX_CONNECTIONS); ++ for (j = 0; j < conn_len; j++) { ++ wcaps = get_wcaps(codec, conn[j]); ++ wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; ++ /* we check only analog outputs */ ++ if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL)) ++ continue; ++ /* if this route has a free DAC, assign it */ ++ if (!check_all_dac_nids(spec, conn[j])) { ++ if (conn_len > 1) { ++ /* select this DAC in the pin's input mux */ ++ snd_hda_codec_write_cache(codec, nid, 0, ++ AC_VERB_SET_CONNECT_SEL, j); ++ } ++ return conn[j]; ++ } ++ } ++ /* if all DACs are already assigned, connect to the primary DAC */ ++ if (conn_len > 1) { ++ for (j = 0; j < conn_len; j++) { ++ if (conn[j] == spec->multiout.dac_nids[0]) { ++ snd_hda_codec_write_cache(codec, nid, 0, ++ AC_VERB_SET_CONNECT_SEL, j); ++ break; ++ } ++ } ++ } ++ return 0; ++} ++ ++static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid); ++static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid); ++ + /* + * Fill in the dac_nids table from the parsed pin configuration + * This function only works when every pin in line_out_pins[] +@@ -2841,31 +2917,17 @@ static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) + * codecs are not connected directly to a DAC, such as the 9200 + * and 9202/925x. For those, dac_nids[] must be hard-coded. + */ +-static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, +- struct auto_pin_cfg *cfg) ++static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec) + { + struct sigmatel_spec *spec = codec->spec; +- int i, j, conn_len = 0; +- hda_nid_t nid, conn[HDA_MAX_CONNECTIONS]; +- unsigned int wcaps, wtype; ++ struct auto_pin_cfg *cfg = &spec->autocfg; ++ int i; ++ hda_nid_t nid, dac; + + for (i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; +- conn_len = snd_hda_get_connections(codec, nid, conn, +- HDA_MAX_CONNECTIONS); +- for (j = 0; j < conn_len; j++) { +- wcaps = snd_hda_param_read(codec, conn[j], +- AC_PAR_AUDIO_WIDGET_CAP); +- wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; +- if (wtype != AC_WID_AUD_OUT || +- (wcaps & AC_WCAP_DIGITAL)) +- continue; +- /* conn[j] is a DAC routed to this line-out */ +- if (!is_in_dac_nids(spec, conn[j])) +- break; +- } +- +- if (j == conn_len) { ++ dac = get_unassigned_dac(codec, nid); ++ if (!dac) { + if (spec->multiout.num_dacs > 0) { + /* we have already working output pins, + * so let's drop the broken ones again +@@ -2879,30 +2941,70 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, + __func__, nid); + return -ENODEV; + } ++ add_spec_dacs(spec, dac); ++ } + +- spec->multiout.dac_nids[i] = conn[j]; +- spec->multiout.num_dacs++; +- if (conn_len > 1) { +- /* select this DAC in the pin's input mux */ +- snd_hda_codec_write_cache(codec, nid, 0, +- AC_VERB_SET_CONNECT_SEL, j); ++ for (i = 0; i < cfg->hp_outs; i++) { ++ nid = cfg->hp_pins[i]; ++ dac = get_unassigned_dac(codec, nid); ++ if (dac) { ++ if (!spec->multiout.hp_nid) ++ spec->multiout.hp_nid = dac; ++ else ++ add_spec_extra_dacs(spec, dac); ++ } ++ spec->hp_dacs[i] = dac; ++ } ++ ++ for (i = 0; i < cfg->speaker_outs; i++) { ++ nid = cfg->speaker_pins[i]; ++ dac = get_unassigned_dac(codec, nid); ++ if (dac) ++ add_spec_extra_dacs(spec, dac); ++ spec->speaker_dacs[i] = dac; ++ } + ++ /* add line-in as output */ ++ nid = check_line_out_switch(codec); ++ if (nid) { ++ dac = get_unassigned_dac(codec, nid); ++ if (dac) { ++ snd_printdd("STAC: Add line-in 0x%x as output %d\n", ++ nid, cfg->line_outs); ++ cfg->line_out_pins[cfg->line_outs] = nid; ++ cfg->line_outs++; ++ spec->line_switch = nid; ++ add_spec_dacs(spec, dac); ++ } ++ } ++ /* add mic as output */ ++ nid = check_mic_out_switch(codec); ++ if (nid) { ++ dac = get_unassigned_dac(codec, nid); ++ if (dac) { ++ snd_printdd("STAC: Add mic-in 0x%x as output %d\n", ++ nid, cfg->line_outs); ++ cfg->line_out_pins[cfg->line_outs] = nid; ++ cfg->line_outs++; ++ spec->mic_switch = nid; ++ add_spec_dacs(spec, dac); + } + } + +- snd_printd("dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", ++ snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", + spec->multiout.num_dacs, + spec->multiout.dac_nids[0], + spec->multiout.dac_nids[1], + spec->multiout.dac_nids[2], + spec->multiout.dac_nids[3], + spec->multiout.dac_nids[4]); ++ + return 0; + } + + /* create volume control/switch for the given prefx type */ +-static int create_controls(struct hda_codec *codec, const char *pfx, +- hda_nid_t nid, int chs) ++static int create_controls_idx(struct hda_codec *codec, const char *pfx, ++ int idx, hda_nid_t nid, int chs) + { + struct sigmatel_spec *spec = codec->spec; + char name[32]; +@@ -2926,24 +3028,25 @@ static int create_controls(struct hda_codec *codec, const char *pfx, + } + + sprintf(name, "%s Playback Volume", pfx); +- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name, ++ err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, idx, name, + HDA_COMPOSE_AMP_VAL_OFS(nid, chs, 0, HDA_OUTPUT, + spec->volume_offset)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", pfx); +- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name, ++ err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_MUTE, idx, name, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); + if (err < 0) + return err; + return 0; + } + ++#define create_controls(codec, pfx, nid, chs) \ ++ create_controls_idx(codec, pfx, 0, nid, chs) ++ + static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid) + { +- if (!spec->multiout.hp_nid) +- spec->multiout.hp_nid = nid; +- else if (spec->multiout.num_dacs > 4) { ++ if (spec->multiout.num_dacs > 4) { + printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid); + return 1; + } else { +@@ -2953,36 +3056,45 @@ static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid) + return 0; + } + +-static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) ++static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid) + { +- if (is_in_dac_nids(spec, nid)) +- return 1; +- if (spec->multiout.hp_nid == nid) +- return 1; +- return 0; ++ int i; ++ for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) { ++ if (!spec->multiout.extra_out_nid[i]) { ++ spec->multiout.extra_out_nid[i] = nid; ++ return 0; ++ } ++ } ++ printk(KERN_WARNING "stac92xx: No space for extra DAC 0x%x\n", nid); ++ return 1; + } + +-/* add playback controls from the parsed DAC table */ +-static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, +- const struct auto_pin_cfg *cfg) ++/* Create output controls ++ * The mixer elements are named depending on the given type (AUTO_PIN_XXX_OUT) ++ */ ++static int create_multi_out_ctls(struct hda_codec *codec, int num_outs, ++ const hda_nid_t *pins, ++ const hda_nid_t *dac_nids, ++ int type) + { ++ struct sigmatel_spec *spec = codec->spec; + static const char *chname[4] = { + "Front", "Surround", NULL /*CLFE*/, "Side" + }; +- hda_nid_t nid = 0; ++ hda_nid_t nid; + int i, err; ++ unsigned int wid_caps; + +- struct sigmatel_spec *spec = codec->spec; +- unsigned int wid_caps, pincap; +- +- +- for (i = 0; i < cfg->line_outs && i < spec->multiout.num_dacs; i++) { +- if (!spec->multiout.dac_nids[i]) ++ for (i = 0; i < num_outs && i < ARRAY_SIZE(chname); i++) { ++ if (type == AUTO_PIN_HP_OUT && !spec->hp_detect) { ++ wid_caps = get_wcaps(codec, pins[i]); ++ if (wid_caps & AC_WCAP_UNSOL_CAP) ++ spec->hp_detect = 1; ++ } ++ nid = dac_nids[i]; ++ if (!nid) + continue; +- +- nid = spec->multiout.dac_nids[i]; +- +- if (i == 2) { ++ if (type != AUTO_PIN_HP_OUT && i == 2) { + /* Center/LFE */ + err = create_controls(codec, "Center", nid, 1); + if (err < 0) +@@ -3003,15 +3115,42 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, + } + + } else { +- err = create_controls(codec, chname[i], nid, 3); ++ const char *name; ++ int idx; ++ switch (type) { ++ case AUTO_PIN_HP_OUT: ++ name = "Headphone"; ++ idx = i; ++ break; ++ case AUTO_PIN_SPEAKER_OUT: ++ name = "Speaker"; ++ idx = i; ++ break; ++ default: ++ name = chname[i]; ++ idx = 0; ++ break; ++ } ++ err = create_controls_idx(codec, name, idx, nid, 3); + if (err < 0) + return err; + } + } ++ return 0; ++} ++ ++/* add playback controls from the parsed DAC table */ ++static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, ++ const struct auto_pin_cfg *cfg) ++{ ++ struct sigmatel_spec *spec = codec->spec; ++ int err; + +- if ((spec->multiout.num_dacs - cfg->line_outs) > 0 && +- cfg->hp_outs == 1 && !spec->multiout.hp_nid) +- spec->multiout.hp_nid = nid; ++ err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins, ++ spec->multiout.dac_nids, ++ cfg->line_out_type); ++ if (err < 0) ++ return err; + + if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) { + err = stac92xx_add_control(spec, +@@ -3023,45 +3162,19 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, + } + + if (spec->line_switch) { +- nid = cfg->input_pins[AUTO_PIN_LINE]; +- pincap = snd_hda_param_read(codec, nid, +- AC_PAR_PIN_CAP); +- if (pincap & AC_PINCAP_OUT) { +- err = stac92xx_add_control(spec, +- STAC_CTL_WIDGET_IO_SWITCH, +- "Line In as Output Switch", nid << 8); +- if (err < 0) +- return err; +- } ++ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, ++ "Line In as Output Switch", ++ spec->line_switch << 8); ++ if (err < 0) ++ return err; + } + + if (spec->mic_switch) { +- unsigned int def_conf; +- unsigned int mic_pin = AUTO_PIN_MIC; +-again: +- nid = cfg->input_pins[mic_pin]; +- def_conf = snd_hda_codec_read(codec, nid, 0, +- AC_VERB_GET_CONFIG_DEFAULT, 0); +- /* some laptops have an internal analog microphone +- * which can't be used as a output */ +- if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) { +- pincap = snd_hda_param_read(codec, nid, +- AC_PAR_PIN_CAP); +- if (pincap & AC_PINCAP_OUT) { +- err = stac92xx_add_control(spec, +- STAC_CTL_WIDGET_IO_SWITCH, +- "Mic as Output Switch", (nid << 8) | 1); +- nid = snd_hda_codec_read(codec, nid, 0, +- AC_VERB_GET_CONNECT_LIST, 0) & 0xff; +- if (!check_in_dac_nids(spec, nid)) +- add_spec_dacs(spec, nid); +- if (err < 0) +- return err; +- } +- } else if (mic_pin == AUTO_PIN_MIC) { +- mic_pin = AUTO_PIN_FRONT_MIC; +- goto again; +- } ++ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, ++ "Mic as Output Switch", ++ (spec->mic_switch << 8) | 1); ++ if (err < 0) ++ return err; + } + + return 0; +@@ -3072,55 +3185,17 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, + struct auto_pin_cfg *cfg) + { + struct sigmatel_spec *spec = codec->spec; +- hda_nid_t nid; +- int i, old_num_dacs, err; ++ int err; + +- old_num_dacs = spec->multiout.num_dacs; +- for (i = 0; i < cfg->hp_outs; i++) { +- unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]); +- if (wid_caps & AC_WCAP_UNSOL_CAP) +- spec->hp_detect = 1; +- nid = snd_hda_codec_read(codec, cfg->hp_pins[i], 0, +- AC_VERB_GET_CONNECT_LIST, 0) & 0xff; +- if (check_in_dac_nids(spec, nid)) +- nid = 0; +- if (! nid) +- continue; +- add_spec_dacs(spec, nid); +- } +- for (i = 0; i < cfg->speaker_outs; i++) { +- nid = snd_hda_codec_read(codec, cfg->speaker_pins[i], 0, +- AC_VERB_GET_CONNECT_LIST, 0) & 0xff; +- if (check_in_dac_nids(spec, nid)) +- nid = 0; +- if (! nid) +- continue; +- add_spec_dacs(spec, nid); +- } +- for (i = 0; i < cfg->line_outs; i++) { +- nid = snd_hda_codec_read(codec, cfg->line_out_pins[i], 0, +- AC_VERB_GET_CONNECT_LIST, 0) & 0xff; +- if (check_in_dac_nids(spec, nid)) +- nid = 0; +- if (! nid) +- continue; +- add_spec_dacs(spec, nid); +- } +- for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) { +- static const char *pfxs[] = { +- "Speaker", "External Speaker", "Speaker2", +- }; +- err = create_controls(codec, pfxs[i - old_num_dacs], +- spec->multiout.dac_nids[i], 3); +- if (err < 0) +- return err; +- } +- if (spec->multiout.hp_nid) { +- err = create_controls(codec, "Headphone", +- spec->multiout.hp_nid, 3); +- if (err < 0) +- return err; +- } ++ err = create_multi_out_ctls(codec, cfg->hp_outs, cfg->hp_pins, ++ spec->hp_dacs, AUTO_PIN_HP_OUT); ++ if (err < 0) ++ return err; ++ ++ err = create_multi_out_ctls(codec, cfg->speaker_outs, cfg->speaker_pins, ++ spec->speaker_dacs, AUTO_PIN_SPEAKER_OUT); ++ if (err < 0) ++ return err; + + return 0; + } +@@ -3330,11 +3405,7 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, + unsigned int wcaps; + unsigned int def_conf; + +- def_conf = snd_hda_codec_read(codec, +- spec->dmic_nids[i], +- 0, +- AC_VERB_GET_CONFIG_DEFAULT, +- 0); ++ def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]); + if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) + continue; + +@@ -3458,8 +3529,8 @@ static void stac92xx_auto_init_hp_out(struct hda_codec *codec) + static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in) + { + struct sigmatel_spec *spec = codec->spec; ++ int hp_swap = 0; + int err; +- int hp_speaker_swap = 0; + + if ((err = snd_hda_parse_pin_def_config(codec, + &spec->autocfg, +@@ -3477,13 +3548,16 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out + * speaker_outs so that the following routines can handle + * HP pins as primary outputs. + */ ++ snd_printdd("stac92xx: Enabling multi-HPs workaround\n"); + memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins, + sizeof(spec->autocfg.line_out_pins)); + spec->autocfg.speaker_outs = spec->autocfg.line_outs; + memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins, + sizeof(spec->autocfg.hp_pins)); + spec->autocfg.line_outs = spec->autocfg.hp_outs; +- hp_speaker_swap = 1; ++ spec->autocfg.line_out_type = AUTO_PIN_HP_OUT; ++ spec->autocfg.hp_outs = 0; ++ hp_swap = 1; + } + if (spec->autocfg.mono_out_pin) { + int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) & +@@ -3535,10 +3609,9 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out + AC_PINCTL_OUT_EN); + } + +- if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0) +- return err; +- if (spec->multiout.num_dacs == 0) { +- if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0) ++ if (!spec->multiout.num_dacs) { ++ err = stac92xx_auto_fill_dac_nids(codec); ++ if (err < 0) + return err; + err = stac92xx_auto_create_multi_out_ctls(codec, + &spec->autocfg); +@@ -3577,26 +3650,20 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out + } + #endif + +- if (hp_speaker_swap == 1) { +- /* Restore the hp_outs and line_outs */ +- memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins, +- sizeof(spec->autocfg.line_out_pins)); +- spec->autocfg.hp_outs = spec->autocfg.line_outs; +- memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins, +- sizeof(spec->autocfg.speaker_pins)); +- spec->autocfg.line_outs = spec->autocfg.speaker_outs; +- memset(spec->autocfg.speaker_pins, 0, +- sizeof(spec->autocfg.speaker_pins)); +- spec->autocfg.speaker_outs = 0; +- } +- + err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg); +- + if (err < 0) + return err; + +- err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg); ++ /* All output parsing done, now restore the swapped hp pins */ ++ if (hp_swap) { ++ memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins, ++ sizeof(spec->autocfg.hp_pins)); ++ spec->autocfg.hp_outs = spec->autocfg.line_outs; ++ spec->autocfg.line_out_type = AUTO_PIN_HP_OUT; ++ spec->autocfg.line_outs = 0; ++ } + ++ err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg); + if (err < 0) + return err; + +@@ -3625,20 +3692,25 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out + return err; + } + ++ err = stac92xx_add_input_source(spec); ++ if (err < 0) ++ return err; ++ + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + if (spec->multiout.max_channels > 2) + spec->surr_switch = 1; + +- if (spec->autocfg.dig_out_pin) ++ if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = dig_out; + if (dig_in && spec->autocfg.dig_in_pin) + spec->dig_in_nid = dig_in; + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; ++ if (spec->kctls.list) ++ spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux; +- spec->dinput_mux = &spec->private_dimux; ++ if (!spec->dinput_mux) ++ spec->dinput_mux = &spec->private_dimux; + spec->sinput_mux = &spec->private_smux; + spec->mono_mux = &spec->private_mono_mux; + spec->amp_mux = &spec->private_amp_mux; +@@ -3691,9 +3763,7 @@ static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec, + for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) { + hda_nid_t pin = spec->autocfg.line_out_pins[i]; + unsigned int defcfg; +- defcfg = snd_hda_codec_read(codec, pin, 0, +- AC_VERB_GET_CONFIG_DEFAULT, +- 0x00); ++ defcfg = snd_hda_codec_get_pincfg(codec, pin); + if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) { + unsigned int wcaps = get_wcaps(codec, pin); + wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP); +@@ -3737,13 +3807,17 @@ static int stac9200_parse_auto_config(struct hda_codec *codec) + return err; + } + +- if (spec->autocfg.dig_out_pin) ++ err = stac92xx_add_input_source(spec); ++ if (err < 0) ++ return err; ++ ++ if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = 0x05; + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = 0x04; + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; ++ if (spec->kctls.list) ++ spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux; + spec->dinput_mux = &spec->private_dimux; +@@ -3787,13 +3861,115 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, + AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ + } + ++#ifdef CONFIG_SND_JACK ++static void stac92xx_free_jack_priv(struct snd_jack *jack) ++{ ++ struct sigmatel_jack *jacks = jack->private_data; ++ jacks->nid = 0; ++ jacks->jack = NULL; ++} ++#endif ++ ++static int stac92xx_add_jack(struct hda_codec *codec, ++ hda_nid_t nid, int type) ++{ ++#ifdef CONFIG_SND_JACK ++ struct sigmatel_spec *spec = codec->spec; ++ struct sigmatel_jack *jack; ++ int def_conf = snd_hda_codec_get_pincfg(codec, nid); ++ int connectivity = get_defcfg_connect(def_conf); ++ char name[32]; ++ int err; ++ ++ if (connectivity && connectivity != AC_JACK_PORT_FIXED) ++ return 0; ++ ++ snd_array_init(&spec->jacks, sizeof(*jack), 32); ++ jack = snd_array_new(&spec->jacks); ++ if (!jack) ++ return -ENOMEM; ++ jack->nid = nid; ++ jack->type = type; ++ ++ sprintf(name, "%s at %s %s Jack", ++ snd_hda_get_jack_type(def_conf), ++ snd_hda_get_jack_connectivity(def_conf), ++ snd_hda_get_jack_location(def_conf)); ++ ++ err = snd_jack_new(codec->bus->card, name, type, &jack->jack); ++ if (err < 0) { ++ jack->nid = 0; ++ return err; ++ } ++ jack->jack->private_data = jack; ++ jack->jack->private_free = stac92xx_free_jack_priv; ++#endif ++ return 0; ++} ++ ++static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid, ++ unsigned char type, int data) ++{ ++ struct sigmatel_event *event; ++ ++ snd_array_init(&spec->events, sizeof(*event), 32); ++ event = snd_array_new(&spec->events); ++ if (!event) ++ return -ENOMEM; ++ event->nid = nid; ++ event->type = type; ++ event->tag = spec->events.used; ++ event->data = data; ++ ++ return event->tag; ++} ++ ++static struct sigmatel_event *stac_get_event(struct hda_codec *codec, ++ hda_nid_t nid, unsigned char type) ++{ ++ struct sigmatel_spec *spec = codec->spec; ++ struct sigmatel_event *event = spec->events.list; ++ int i; ++ ++ for (i = 0; i < spec->events.used; i++, event++) { ++ if (event->nid == nid && event->type == type) ++ return event; ++ } ++ return NULL; ++} ++ ++static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec, ++ unsigned char tag) ++{ ++ struct sigmatel_spec *spec = codec->spec; ++ struct sigmatel_event *event = spec->events.list; ++ int i; ++ ++ for (i = 0; i < spec->events.used; i++, event++) { ++ if (event->tag == tag) ++ return event; ++ } ++ return NULL; ++} ++ + static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, +- unsigned int event) ++ unsigned int type) + { +- if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) +- snd_hda_codec_write_cache(codec, nid, 0, +- AC_VERB_SET_UNSOLICITED_ENABLE, +- (AC_USRSP_EN | event)); ++ struct sigmatel_event *event; ++ int tag; ++ ++ if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)) ++ return; ++ event = stac_get_event(codec, nid, type); ++ if (event) ++ tag = event->tag; ++ else ++ tag = stac_add_event(codec->spec, nid, type, 0); ++ if (tag < 0) ++ return; ++ snd_hda_codec_write_cache(codec, nid, 0, ++ AC_VERB_SET_UNSOLICITED_ENABLE, ++ AC_USRSP_EN | tag); + } + + static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) +@@ -3813,15 +3989,44 @@ static void stac92xx_power_down(struct hda_codec *codec) + /* power down inactive DACs */ + hda_nid_t *dac; + for (dac = spec->dac_list; *dac; dac++) +- if (!is_in_dac_nids(spec, *dac) && +- spec->multiout.hp_nid != *dac) +- snd_hda_codec_write_cache(codec, *dac, 0, ++ if (!check_all_dac_nids(spec, *dac)) ++ snd_hda_codec_write(codec, *dac, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + } + + static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, + int enable); + ++/* override some hints from the hwdep entry */ ++static void stac_store_hints(struct hda_codec *codec) ++{ ++ struct sigmatel_spec *spec = codec->spec; ++ const char *p; ++ int val; ++ ++ val = snd_hda_get_bool_hint(codec, "hp_detect"); ++ if (val >= 0) ++ spec->hp_detect = val; ++ p = snd_hda_get_hint(codec, "gpio_mask"); ++ if (p) { ++ spec->gpio_mask = simple_strtoul(p, NULL, 0); ++ spec->eapd_mask = spec->gpio_dir = spec->gpio_data = ++ spec->gpio_mask; ++ } ++ p = snd_hda_get_hint(codec, "gpio_dir"); ++ if (p) ++ spec->gpio_dir = simple_strtoul(p, NULL, 0) & spec->gpio_mask; ++ p = snd_hda_get_hint(codec, "gpio_data"); ++ if (p) ++ spec->gpio_data = simple_strtoul(p, NULL, 0) & spec->gpio_mask; ++ p = snd_hda_get_hint(codec, "eapd_mask"); ++ if (p) ++ spec->eapd_mask = simple_strtoul(p, NULL, 0) & spec->gpio_mask; ++ val = snd_hda_get_bool_hint(codec, "eapd_switch"); ++ if (val >= 0) ++ spec->eapd_switch = val; ++} ++ + static int stac92xx_init(struct hda_codec *codec) + { + struct sigmatel_spec *spec = codec->spec; +@@ -3834,10 +4039,13 @@ static int stac92xx_init(struct hda_codec *codec) + /* power down adcs initially */ + if (spec->powerdown_adcs) + for (i = 0; i < spec->num_adcs; i++) +- snd_hda_codec_write_cache(codec, ++ snd_hda_codec_write(codec, + spec->adc_nids[i], 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + ++ /* override some hints */ ++ stac_store_hints(codec); ++ + /* set up GPIO */ + gpio = spec->gpio_data; + /* turn on EAPD statically when spec->eapd_switch isn't set. +@@ -3850,44 +4058,62 @@ static int stac92xx_init(struct hda_codec *codec) + /* set up pins */ + if (spec->hp_detect) { + /* Enable unsolicited responses on the HP widget */ +- for (i = 0; i < cfg->hp_outs; i++) +- enable_pin_detect(codec, cfg->hp_pins[i], +- STAC_HP_EVENT); ++ for (i = 0; i < cfg->hp_outs; i++) { ++ hda_nid_t nid = cfg->hp_pins[i]; ++ enable_pin_detect(codec, nid, STAC_HP_EVENT); ++ } + /* force to enable the first line-out; the others are set up + * in unsol_event + */ + stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0], +- AC_PINCTL_OUT_EN); +- stac92xx_auto_init_hp_out(codec); ++ AC_PINCTL_OUT_EN); + /* fake event to set up pins */ +- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); ++ stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0], ++ STAC_HP_EVENT); + } else { + stac92xx_auto_init_multi_out(codec); + stac92xx_auto_init_hp_out(codec); ++ for (i = 0; i < cfg->hp_outs; i++) ++ stac_toggle_power_map(codec, cfg->hp_pins[i], 1); + } + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = cfg->input_pins[i]; + if (nid) { +- unsigned int pinctl; ++ unsigned int pinctl, conf; + if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) { + /* for mic pins, force to initialize */ + pinctl = stac92xx_get_vref(codec, nid); ++ pinctl |= AC_PINCTL_IN_EN; ++ stac92xx_auto_set_pinctl(codec, nid, pinctl); + } else { + pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + /* if PINCTL already set then skip */ +- if (pinctl & AC_PINCTL_IN_EN) +- continue; ++ /* Also, if both INPUT and OUTPUT are set, ++ * it must be a BIOS bug; need to override, too ++ */ ++ if (!(pinctl & AC_PINCTL_IN_EN) || ++ (pinctl & AC_PINCTL_OUT_EN)) { ++ pinctl &= ~AC_PINCTL_OUT_EN; ++ pinctl |= AC_PINCTL_IN_EN; ++ stac92xx_auto_set_pinctl(codec, nid, ++ pinctl); ++ } ++ } ++ conf = snd_hda_codec_get_pincfg(codec, nid); ++ if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) { ++ enable_pin_detect(codec, nid, ++ STAC_INSERT_EVENT); ++ stac_issue_unsol_event(codec, nid, ++ STAC_INSERT_EVENT); + } +- pinctl |= AC_PINCTL_IN_EN; +- stac92xx_auto_set_pinctl(codec, nid, pinctl); + } + } + for (i = 0; i < spec->num_dmics; i++) + stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i], + AC_PINCTL_IN_EN); +- if (cfg->dig_out_pin) +- stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin, ++ if (cfg->dig_out_pins[0]) ++ stac92xx_auto_set_pinctl(codec, cfg->dig_out_pins[0], + AC_PINCTL_OUT_EN); + if (cfg->dig_in_pin) + stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin, +@@ -3895,9 +4121,14 @@ static int stac92xx_init(struct hda_codec *codec) + for (i = 0; i < spec->num_pwrs; i++) { + hda_nid_t nid = spec->pwr_nids[i]; + int pinctl, def_conf; +- int event = STAC_PWR_EVENT; + +- if (is_nid_hp_pin(cfg, nid) && spec->hp_detect) ++ /* power on when no jack detection is available */ ++ if (!spec->hp_detect) { ++ stac_toggle_power_map(codec, nid, 1); ++ continue; ++ } ++ ++ if (is_nid_hp_pin(cfg, nid)) + continue; /* already has an unsol event */ + + pinctl = snd_hda_codec_read(codec, nid, 0, +@@ -3906,10 +4137,11 @@ static int stac92xx_init(struct hda_codec *codec) + * any attempts on powering down a input port cause the + * referenced VREF to act quirky. + */ +- if (pinctl & AC_PINCTL_IN_EN) ++ if (pinctl & AC_PINCTL_IN_EN) { ++ stac_toggle_power_map(codec, nid, 1); + continue; +- def_conf = snd_hda_codec_read(codec, nid, 0, +- AC_VERB_GET_CONFIG_DEFAULT, 0); ++ } ++ def_conf = snd_hda_codec_get_pincfg(codec, nid); + def_conf = get_defcfg_connect(def_conf); + /* skip any ports that don't have jacks since presence + * detection is useless */ +@@ -3918,30 +4150,55 @@ static int stac92xx_init(struct hda_codec *codec) + stac_toggle_power_map(codec, nid, 1); + continue; + } +- enable_pin_detect(codec, spec->pwr_nids[i], event | i); +- codec->patch_ops.unsol_event(codec, (event | i) << 26); ++ if (!stac_get_event(codec, nid, STAC_INSERT_EVENT)) { ++ enable_pin_detect(codec, nid, STAC_PWR_EVENT); ++ stac_issue_unsol_event(codec, nid, STAC_PWR_EVENT); ++ } + } + if (spec->dac_list) + stac92xx_power_down(codec); + return 0; + } + ++static void stac92xx_free_jacks(struct hda_codec *codec) ++{ ++#ifdef CONFIG_SND_JACK ++ /* free jack instances manually when clearing/reconfiguring */ ++ struct sigmatel_spec *spec = codec->spec; ++ if (!codec->bus->shutdown && spec->jacks.list) { ++ struct sigmatel_jack *jacks = spec->jacks.list; ++ int i; ++ for (i = 0; i < spec->jacks.used; i++, jacks++) { ++ if (jacks->jack) ++ snd_device_free(codec->bus->card, jacks->jack); ++ } ++ } ++ snd_array_free(&spec->jacks); ++#endif ++} ++ ++static void stac92xx_free_kctls(struct hda_codec *codec) ++{ ++ struct sigmatel_spec *spec = codec->spec; ++ ++ if (spec->kctls.list) { ++ struct snd_kcontrol_new *kctl = spec->kctls.list; ++ int i; ++ for (i = 0; i < spec->kctls.used; i++) ++ kfree(kctl[i].name); ++ } ++ snd_array_free(&spec->kctls); ++} ++ + static void stac92xx_free(struct hda_codec *codec) + { + struct sigmatel_spec *spec = codec->spec; +- int i; + + if (! spec) + return; + +- if (spec->kctl_alloc) { +- for (i = 0; i < spec->num_kctl_used; i++) +- kfree(spec->kctl_alloc[i].name); +- kfree(spec->kctl_alloc); +- } +- +- if (spec->bios_pin_configs) +- kfree(spec->bios_pin_configs); ++ stac92xx_free_jacks(codec); ++ snd_array_free(&spec->events); + + kfree(spec); + snd_hda_detach_beep_device(codec); +@@ -3950,7 +4207,9 @@ static void stac92xx_free(struct hda_codec *codec) + static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, + unsigned int flag) + { +- unsigned int pin_ctl = snd_hda_codec_read(codec, nid, ++ unsigned int old_ctl, pin_ctl; ++ ++ pin_ctl = snd_hda_codec_read(codec, nid, + 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); + + if (pin_ctl & AC_PINCTL_IN_EN) { +@@ -3960,22 +4219,21 @@ static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, + * "xxx as Output" mixer switch + */ + struct sigmatel_spec *spec = codec->spec; +- struct auto_pin_cfg *cfg = &spec->autocfg; +- if ((nid == cfg->input_pins[AUTO_PIN_LINE] && +- spec->line_switch) || +- (nid == cfg->input_pins[AUTO_PIN_MIC] && +- spec->mic_switch)) ++ if (nid == spec->line_switch || nid == spec->mic_switch) + return; + } + ++ old_ctl = pin_ctl; + /* if setting pin direction bits, clear the current + direction bits first */ + if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN)) + pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); + +- snd_hda_codec_write_cache(codec, nid, 0, +- AC_VERB_SET_PIN_WIDGET_CONTROL, +- pin_ctl | flag); ++ pin_ctl |= flag; ++ if (old_ctl != pin_ctl) ++ snd_hda_codec_write_cache(codec, nid, 0, ++ AC_VERB_SET_PIN_WIDGET_CONTROL, ++ pin_ctl); + } + + static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, +@@ -3983,25 +4241,19 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, + { + unsigned int pin_ctl = snd_hda_codec_read(codec, nid, + 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); +- snd_hda_codec_write_cache(codec, nid, 0, +- AC_VERB_SET_PIN_WIDGET_CONTROL, +- pin_ctl & ~flag); ++ if (pin_ctl & flag) ++ snd_hda_codec_write_cache(codec, nid, 0, ++ AC_VERB_SET_PIN_WIDGET_CONTROL, ++ pin_ctl & ~flag); + } + +-static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid) ++static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) + { + if (!nid) + return 0; + if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00) +- & (1 << 31)) { +- unsigned int pinctl; +- pinctl = snd_hda_codec_read(codec, nid, 0, +- AC_VERB_GET_PIN_WIDGET_CONTROL, 0); +- if (pinctl & AC_PINCTL_IN_EN) +- return 0; /* mic- or line-input */ +- else +- return 1; /* HP-output */ +- } ++ & (1 << 31)) ++ return 1; + return 0; + } + +@@ -4013,11 +4265,9 @@ static int no_hp_sensing(struct sigmatel_spec *spec, int i) + struct auto_pin_cfg *cfg = &spec->autocfg; + + /* ignore sensing of shared line and mic jacks */ +- if (spec->line_switch && +- cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_LINE]) ++ if (cfg->hp_pins[i] == spec->line_switch) + return 1; +- if (spec->mic_switch && +- cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_MIC]) ++ if (cfg->hp_pins[i] == spec->mic_switch) + return 1; + /* ignore if the pin is set as line-out */ + if (cfg->hp_pins[i] == spec->hp_switch) +@@ -4025,7 +4275,7 @@ static int no_hp_sensing(struct sigmatel_spec *spec, int i) + return 0; + } + +-static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) ++static void stac92xx_hp_detect(struct hda_codec *codec) + { + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; +@@ -4041,7 +4291,14 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) + break; + if (no_hp_sensing(spec, i)) + continue; +- presence = get_hp_pin_presence(codec, cfg->hp_pins[i]); ++ presence = get_pin_presence(codec, cfg->hp_pins[i]); ++ if (presence) { ++ unsigned int pinctl; ++ pinctl = snd_hda_codec_read(codec, cfg->hp_pins[i], 0, ++ AC_VERB_GET_PIN_WIDGET_CONTROL, 0); ++ if (pinctl & AC_PINCTL_IN_EN) ++ presence = 0; /* mic- or line-input */ ++ } + } + + if (presence) { +@@ -4082,8 +4339,19 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) + continue; + if (presence) + stac92xx_set_pinctl(codec, cfg->hp_pins[i], val); ++#if 0 /* FIXME */ ++/* Resetting the pinctl like below may lead to (a sort of) regressions ++ * on some devices since they use the HP pin actually for line/speaker ++ * outs although the default pin config shows a different pin (that is ++ * wrong and useless). ++ * ++ * So, it's basically a problem of default pin configs, likely a BIOS issue. ++ * But, disabling the code below just works around it, and I'm too tired of ++ * bug reports with such devices... ++ */ + else + stac92xx_reset_pinctl(codec, cfg->hp_pins[i], val); ++#endif /* FIXME */ + } + } + +@@ -4118,50 +4386,193 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, + + static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid) + { +- stac_toggle_power_map(codec, nid, get_hp_pin_presence(codec, nid)); ++ stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid)); ++} ++ ++static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid) ++{ ++ struct sigmatel_spec *spec = codec->spec; ++ struct sigmatel_jack *jacks = spec->jacks.list; ++ ++ if (jacks) { ++ int i; ++ for (i = 0; i < spec->jacks.used; i++) { ++ if (jacks->nid == nid) { ++ unsigned int pin_ctl = ++ snd_hda_codec_read(codec, nid, ++ 0, AC_VERB_GET_PIN_WIDGET_CONTROL, ++ 0x00); ++ int type = jacks->type; ++ if (type == (SND_JACK_LINEOUT ++ | SND_JACK_HEADPHONE)) ++ type = (pin_ctl & AC_PINCTL_HP_EN) ++ ? SND_JACK_HEADPHONE : SND_JACK_LINEOUT; ++ snd_jack_report(jacks->jack, ++ get_pin_presence(codec, nid) ++ ? type : 0); ++ } ++ jacks++; ++ } ++ } ++} ++ ++static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid, ++ unsigned char type) ++{ ++ struct sigmatel_event *event = stac_get_event(codec, nid, type); ++ if (!event) ++ return; ++ codec->patch_ops.unsol_event(codec, (unsigned)event->tag << 26); + } + + static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) + { + struct sigmatel_spec *spec = codec->spec; +- int idx = res >> 26 & 0x0f; ++ struct sigmatel_event *event; ++ int tag, data; + +- switch ((res >> 26) & 0x70) { ++ tag = (res >> 26) & 0x7f; ++ event = stac_get_event_from_tag(codec, tag); ++ if (!event) ++ return; ++ ++ switch (event->type) { + case STAC_HP_EVENT: +- stac92xx_hp_detect(codec, res); ++ stac92xx_hp_detect(codec); + /* fallthru */ ++ case STAC_INSERT_EVENT: + case STAC_PWR_EVENT: + if (spec->num_pwrs > 0) +- stac92xx_pin_sense(codec, idx); ++ stac92xx_pin_sense(codec, event->nid); ++ stac92xx_report_jack(codec, event->nid); ++ ++ switch (codec->subsystem_id) { ++ case 0x103c308f: ++ if (event->nid == 0xb) { ++ int pin = AC_PINCTL_IN_EN; ++ ++ if (get_pin_presence(codec, 0xa) ++ && get_pin_presence(codec, 0xb)) ++ pin |= AC_PINCTL_VREF_80; ++ if (!get_pin_presence(codec, 0xb)) ++ pin |= AC_PINCTL_VREF_80; ++ ++ /* toggle VREF state based on mic + hp pin ++ * status ++ */ ++ stac92xx_auto_set_pinctl(codec, 0x0a, pin); ++ } ++ } + break; +- case STAC_VREF_EVENT: { +- int data = snd_hda_codec_read(codec, codec->afg, 0, +- AC_VERB_GET_GPIO_DATA, 0); ++ case STAC_VREF_EVENT: ++ data = snd_hda_codec_read(codec, codec->afg, 0, ++ AC_VERB_GET_GPIO_DATA, 0); + /* toggle VREF state based on GPIOx status */ + snd_hda_codec_write(codec, codec->afg, 0, 0x7e0, +- !!(data & (1 << idx))); ++ !!(data & (1 << event->data))); + break; +- } + } + } + ++#ifdef CONFIG_PROC_FS ++static void stac92hd_proc_hook(struct snd_info_buffer *buffer, ++ struct hda_codec *codec, hda_nid_t nid) ++{ ++ if (nid == codec->afg) ++ snd_iprintf(buffer, "Power-Map: 0x%02x\n", ++ snd_hda_codec_read(codec, nid, 0, 0x0fec, 0x0)); ++} ++ ++static void analog_loop_proc_hook(struct snd_info_buffer *buffer, ++ struct hda_codec *codec, ++ unsigned int verb) ++{ ++ snd_iprintf(buffer, "Analog Loopback: 0x%02x\n", ++ snd_hda_codec_read(codec, codec->afg, 0, verb, 0)); ++} ++ ++/* stac92hd71bxx, stac92hd73xx */ ++static void stac92hd7x_proc_hook(struct snd_info_buffer *buffer, ++ struct hda_codec *codec, hda_nid_t nid) ++{ ++ stac92hd_proc_hook(buffer, codec, nid); ++ if (nid == codec->afg) ++ analog_loop_proc_hook(buffer, codec, 0xfa0); ++} ++ ++static void stac9205_proc_hook(struct snd_info_buffer *buffer, ++ struct hda_codec *codec, hda_nid_t nid) ++{ ++ if (nid == codec->afg) ++ analog_loop_proc_hook(buffer, codec, 0xfe0); ++} ++ ++static void stac927x_proc_hook(struct snd_info_buffer *buffer, ++ struct hda_codec *codec, hda_nid_t nid) ++{ ++ if (nid == codec->afg) ++ analog_loop_proc_hook(buffer, codec, 0xfeb); ++} ++#else ++#define stac92hd_proc_hook NULL ++#define stac92hd7x_proc_hook NULL ++#define stac9205_proc_hook NULL ++#define stac927x_proc_hook NULL ++#endif ++ + #ifdef SND_HDA_NEEDS_RESUME + static int stac92xx_resume(struct hda_codec *codec) + { + struct sigmatel_spec *spec = codec->spec; + +- stac92xx_set_config_regs(codec); +- snd_hda_sequence_write(codec, spec->init); +- stac_gpio_set(codec, spec->gpio_mask, +- spec->gpio_dir, spec->gpio_data); ++ stac92xx_init(codec); + snd_hda_codec_resume_amp(codec); + snd_hda_codec_resume_cache(codec); +- /* power down inactive DACs */ +- if (spec->dac_list) +- stac92xx_power_down(codec); +- /* invoke unsolicited event to reset the HP state */ ++ /* fake event to set up pins again to override cached values */ + if (spec->hp_detect) +- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); ++ stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0], ++ STAC_HP_EVENT); ++ return 0; ++} ++ ++ ++/* ++ * using power check for controlling mute led of HP HDX notebooks ++ * check for mute state only on Speakers (nid = 0x10) ++ * ++ * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise ++ * the LED is NOT working properly ! ++ */ ++ ++#ifdef CONFIG_SND_HDA_POWER_SAVE ++static int stac92xx_hp_hdx_check_power_status(struct hda_codec *codec, ++ hda_nid_t nid) ++{ ++ struct sigmatel_spec *spec = codec->spec; ++ ++ if (nid == 0x10) { ++ if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & ++ HDA_AMP_MUTE) ++ spec->gpio_data &= ~0x08; /* orange */ ++ else ++ spec->gpio_data |= 0x08; /* white */ ++ ++ stac_gpio_set(codec, spec->gpio_mask, ++ spec->gpio_dir, ++ spec->gpio_data); ++ } ++ ++ return 0; ++} ++#endif ++ ++static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) ++{ ++ struct sigmatel_spec *spec = codec->spec; ++ if (spec->eapd_mask) ++ stac_gpio_set(codec, spec->gpio_mask, ++ spec->gpio_dir, spec->gpio_data & ++ ~spec->eapd_mask); + return 0; + } + #endif +@@ -4173,6 +4584,7 @@ static struct hda_codec_ops stac92xx_patch_ops = { + .free = stac92xx_free, + .unsol_event = stac92xx_unsol_event, + #ifdef SND_HDA_NEEDS_RESUME ++ .suspend = stac92xx_suspend, + .resume = stac92xx_resume, + #endif + }; +@@ -4192,18 +4604,11 @@ static int patch_stac9200(struct hda_codec *codec) + spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS, + stac9200_models, + stac9200_cfg_tbl); +- if (spec->board_config < 0) { ++ if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n"); +- err = stac92xx_save_bios_config_regs(codec); +- if (err < 0) { +- stac92xx_free(codec); +- return err; +- } +- spec->pin_configs = spec->bios_pin_configs; +- } else { +- spec->pin_configs = stac9200_brd_tbl[spec->board_config]; +- stac92xx_set_config_regs(codec); +- } ++ else ++ stac92xx_set_config_regs(codec, ++ stac9200_brd_tbl[spec->board_config]); + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = 1; +@@ -4234,6 +4639,12 @@ static int patch_stac9200(struct hda_codec *codec) + return err; + } + ++ /* CF-74 has no headphone detection, and the driver should *NOT* ++ * do detection and HP/speaker toggle because the hardware does it. ++ */ ++ if (spec->board_config == STAC_9200_PANASONIC) ++ spec->hp_detect = 0; ++ + codec->patch_ops = stac92xx_patch_ops; + + return 0; +@@ -4265,19 +4676,12 @@ static int patch_stac925x(struct hda_codec *codec) + stac925x_models, + stac925x_cfg_tbl); + again: +- if (spec->board_config < 0) { ++ if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x," + "using BIOS defaults\n"); +- err = stac92xx_save_bios_config_regs(codec); +- if (err < 0) { +- stac92xx_free(codec); +- return err; +- } +- spec->pin_configs = spec->bios_pin_configs; +- } else if (stac925x_brd_tbl[spec->board_config] != NULL){ +- spec->pin_configs = stac925x_brd_tbl[spec->board_config]; +- stac92xx_set_config_regs(codec); +- } ++ else ++ stac92xx_set_config_regs(codec, ++ stac925x_brd_tbl[spec->board_config]); + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = 1; +@@ -4340,6 +4744,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec) + struct sigmatel_spec *spec; + hda_nid_t conn[STAC92HD73_DAC_COUNT + 2]; + int err = 0; ++ int num_dacs; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) +@@ -4354,47 +4759,40 @@ static int patch_stac92hd73xx(struct hda_codec *codec) + stac92hd73xx_models, + stac92hd73xx_cfg_tbl); + again: +- if (spec->board_config < 0) { ++ if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: Unknown model for" + " STAC92HD73XX, using BIOS defaults\n"); +- err = stac92xx_save_bios_config_regs(codec); +- if (err < 0) { +- stac92xx_free(codec); +- return err; +- } +- spec->pin_configs = spec->bios_pin_configs; +- } else { +- spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config]; +- stac92xx_set_config_regs(codec); +- } ++ else ++ stac92xx_set_config_regs(codec, ++ stac92hd73xx_brd_tbl[spec->board_config]); + +- spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a, ++ num_dacs = snd_hda_get_connections(codec, 0x0a, + conn, STAC92HD73_DAC_COUNT + 2) - 1; + +- if (spec->multiout.num_dacs < 0) { ++ if (num_dacs < 3 || num_dacs > 5) { + printk(KERN_WARNING "hda_codec: Could not determine " + "number of channels defaulting to DAC count\n"); +- spec->multiout.num_dacs = STAC92HD73_DAC_COUNT; ++ num_dacs = STAC92HD73_DAC_COUNT; + } +- +- switch (spec->multiout.num_dacs) { ++ switch (num_dacs) { + case 0x3: /* 6 Channel */ +- spec->multiout.hp_nid = 0x17; + spec->mixer = stac92hd73xx_6ch_mixer; + spec->init = stac92hd73xx_6ch_core_init; ++ spec->aloopback_ctl = stac92hd73xx_6ch_loopback; + break; + case 0x4: /* 8 Channel */ +- spec->multiout.hp_nid = 0x18; + spec->mixer = stac92hd73xx_8ch_mixer; + spec->init = stac92hd73xx_8ch_core_init; ++ spec->aloopback_ctl = stac92hd73xx_8ch_loopback; + break; + case 0x5: /* 10 Channel */ +- spec->multiout.hp_nid = 0x19; + spec->mixer = stac92hd73xx_10ch_mixer; + spec->init = stac92hd73xx_10ch_core_init; +- }; ++ spec->aloopback_ctl = stac92hd73xx_10ch_loopback; ++ break; ++ } ++ spec->multiout.dac_nids = spec->dac_nids; + +- spec->multiout.dac_nids = stac92hd73xx_dac_nids; + spec->aloopback_mask = 0x01; + spec->aloopback_shift = 8; + +@@ -4425,24 +4823,23 @@ again: + spec->amp_nids = &stac92hd73xx_amp_nids[DELL_M6_AMP]; + spec->eapd_switch = 0; + spec->num_amps = 1; +- spec->multiout.hp_nid = 0; /* dual HPs */ + +- if (!spec->init) ++ if (spec->board_config != STAC_DELL_EQ) + spec->init = dell_m6_core_init; + switch (spec->board_config) { + case STAC_DELL_M6_AMIC: /* Analog Mics */ +- stac92xx_set_config_reg(codec, 0x0b, 0x90A70170); ++ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); + spec->num_dmics = 0; + spec->private_dimux.num_items = 1; + break; + case STAC_DELL_M6_DMIC: /* Digital Mics */ +- stac92xx_set_config_reg(codec, 0x13, 0x90A60160); ++ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); + spec->num_dmics = 1; + spec->private_dimux.num_items = 2; + break; + case STAC_DELL_M6_BOTH: /* Both */ +- stac92xx_set_config_reg(codec, 0x0b, 0x90A70170); +- stac92xx_set_config_reg(codec, 0x13, 0x90A60160); ++ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); ++ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); + spec->num_dmics = 1; + spec->private_dimux.num_items = 2; + break; +@@ -4485,6 +4882,8 @@ again: + + codec->patch_ops = stac92xx_patch_ops; + ++ codec->proc_widget_hook = stac92hd7x_proc_hook; ++ + return 0; + } + +@@ -4500,7 +4899,10 @@ static struct hda_input_mux stac92hd83xxx_dmux = { + static int patch_stac92hd83xxx(struct hda_codec *codec) + { + struct sigmatel_spec *spec; ++ hda_nid_t conn[STAC92HD83_DAC_COUNT + 1]; + int err; ++ int num_dacs; ++ hda_nid_t nid; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) +@@ -4514,25 +4916,17 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) + spec->dmux_nids = stac92hd83xxx_dmux_nids; + spec->adc_nids = stac92hd83xxx_adc_nids; + spec->pwr_nids = stac92hd83xxx_pwr_nids; ++ spec->amp_nids = stac92hd83xxx_amp_nids; + spec->pwr_mapping = stac92hd83xxx_pwr_mapping; + spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids); +- spec->multiout.dac_nids = stac92hd83xxx_dac_nids; ++ spec->multiout.dac_nids = spec->dac_nids; + + spec->init = stac92hd83xxx_core_init; +- switch (codec->vendor_id) { +- case 0x111d7605: +- spec->multiout.num_dacs = STAC92HD81_DAC_COUNT; +- break; +- default: +- spec->num_pwrs--; +- spec->init++; /* switch to config #2 */ +- spec->multiout.num_dacs = STAC92HD83_DAC_COUNT; +- } +- + spec->mixer = stac92hd83xxx_mixer; + spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids); + spec->num_dmuxes = ARRAY_SIZE(stac92hd83xxx_dmux_nids); + spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids); ++ spec->num_amps = ARRAY_SIZE(stac92hd83xxx_amp_nids); + spec->num_dmics = STAC92HD83XXX_NUM_DMICS; + spec->dinput_mux = &stac92hd83xxx_dmux; + spec->pin_nids = stac92hd83xxx_pin_nids; +@@ -4541,18 +4935,21 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) + stac92hd83xxx_models, + stac92hd83xxx_cfg_tbl); + again: +- if (spec->board_config < 0) { ++ if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: Unknown model for" + " STAC92HD83XXX, using BIOS defaults\n"); +- err = stac92xx_save_bios_config_regs(codec); +- if (err < 0) { +- stac92xx_free(codec); +- return err; +- } +- spec->pin_configs = spec->bios_pin_configs; +- } else { +- spec->pin_configs = stac92hd83xxx_brd_tbl[spec->board_config]; +- stac92xx_set_config_regs(codec); ++ else ++ stac92xx_set_config_regs(codec, ++ stac92hd83xxx_brd_tbl[spec->board_config]); ++ ++ switch (codec->vendor_id) { ++ case 0x111d7604: ++ case 0x111d7605: ++ case 0x111d76d5: ++ if (spec->board_config == STAC_92HD83XXX_PWR_REF) ++ break; ++ spec->num_pwrs = 0; ++ break; + } + + err = stac92xx_parse_auto_config(codec, 0x1d, 0); +@@ -4571,73 +4968,110 @@ again: + return err; + } + ++ switch (spec->board_config) { ++ case STAC_DELL_S14: ++ nid = 0xf; ++ break; ++ default: ++ nid = 0xe; ++ break; ++ } ++ ++ num_dacs = snd_hda_get_connections(codec, nid, ++ conn, STAC92HD83_DAC_COUNT + 1) - 1; ++ ++ /* set port X to select the last DAC ++ */ ++ snd_hda_codec_write_cache(codec, nid, 0, ++ AC_VERB_SET_CONNECT_SEL, num_dacs); ++ + codec->patch_ops = stac92xx_patch_ops; + ++ codec->proc_widget_hook = stac92hd_proc_hook; ++ + return 0; + } + +-#ifdef SND_HDA_NEEDS_RESUME +-static void stac92hd71xx_set_power_state(struct hda_codec *codec, int pwr) +-{ +- struct sigmatel_spec *spec = codec->spec; +- int i; +- snd_hda_codec_write_cache(codec, codec->afg, 0, +- AC_VERB_SET_POWER_STATE, pwr); ++static struct hda_input_mux stac92hd71bxx_dmux_nomixer = { ++ .num_items = 3, ++ .items = { ++ { "Analog Inputs", 0x00 }, ++ { "Digital Mic 1", 0x02 }, ++ { "Digital Mic 2", 0x03 }, ++ } ++}; + +- msleep(1); +- for (i = 0; i < spec->num_adcs; i++) { +- snd_hda_codec_write_cache(codec, +- spec->adc_nids[i], 0, +- AC_VERB_SET_POWER_STATE, pwr); ++static struct hda_input_mux stac92hd71bxx_dmux_amixer = { ++ .num_items = 4, ++ .items = { ++ { "Analog Inputs", 0x00 }, ++ { "Mixer", 0x01 }, ++ { "Digital Mic 1", 0x02 }, ++ { "Digital Mic 2", 0x03 }, + } + }; + +-static int stac92hd71xx_resume(struct hda_codec *codec) ++/* get the pin connection (fixed, none, etc) */ ++static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx) + { +- stac92hd71xx_set_power_state(codec, AC_PWRST_D0); +- return stac92xx_resume(codec); ++ struct sigmatel_spec *spec = codec->spec; ++ unsigned int cfg; ++ ++ cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]); ++ return get_defcfg_connect(cfg); + } + +-static int stac92hd71xx_suspend(struct hda_codec *codec, pm_message_t state) ++static int stac92hd71bxx_connected_ports(struct hda_codec *codec, ++ hda_nid_t *nids, int num_nids) + { + struct sigmatel_spec *spec = codec->spec; ++ int idx, num; ++ unsigned int def_conf; + +- stac92hd71xx_set_power_state(codec, AC_PWRST_D3); +- if (spec->eapd_mask) +- stac_gpio_set(codec, spec->gpio_mask, +- spec->gpio_dir, spec->gpio_data & +- ~spec->eapd_mask); +- return 0; +-}; ++ for (num = 0; num < num_nids; num++) { ++ for (idx = 0; idx < spec->num_pins; idx++) ++ if (spec->pin_nids[idx] == nids[num]) ++ break; ++ if (idx >= spec->num_pins) ++ break; ++ def_conf = stac_get_defcfg_connect(codec, idx); ++ if (def_conf == AC_JACK_PORT_NONE) ++ break; ++ } ++ return num; ++} + +-#endif ++static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec, ++ hda_nid_t dig0pin) ++{ ++ struct sigmatel_spec *spec = codec->spec; ++ int idx; + +-static struct hda_codec_ops stac92hd71bxx_patch_ops = { +- .build_controls = stac92xx_build_controls, +- .build_pcms = stac92xx_build_pcms, +- .init = stac92xx_init, +- .free = stac92xx_free, +- .unsol_event = stac92xx_unsol_event, +-#ifdef SND_HDA_NEEDS_RESUME +- .resume = stac92hd71xx_resume, +- .suspend = stac92hd71xx_suspend, +-#endif +-}; ++ for (idx = 0; idx < spec->num_pins; idx++) ++ if (spec->pin_nids[idx] == dig0pin) ++ break; ++ if ((idx + 2) >= spec->num_pins) ++ return 0; + +-static struct hda_input_mux stac92hd71bxx_dmux = { +- .num_items = 4, +- .items = { +- { "Analog Inputs", 0x00 }, +- { "Mixer", 0x01 }, +- { "Digital Mic 1", 0x02 }, +- { "Digital Mic 2", 0x03 }, +- } +-}; ++ /* dig1pin case */ ++ if (stac_get_defcfg_connect(codec, idx + 1) != AC_JACK_PORT_NONE) ++ return 2; ++ ++ /* dig0pin + dig2pin case */ ++ if (stac_get_defcfg_connect(codec, idx + 2) != AC_JACK_PORT_NONE) ++ return 2; ++ if (stac_get_defcfg_connect(codec, idx) != AC_JACK_PORT_NONE) ++ return 1; ++ else ++ return 0; ++} + + static int patch_stac92hd71bxx(struct hda_codec *codec) + { + struct sigmatel_spec *spec; ++ struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init; + int err = 0; ++ unsigned int ndmic_nids = 0; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) +@@ -4645,29 +5079,32 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) + + codec->spec = spec; + codec->patch_ops = stac92xx_patch_ops; +- spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids); ++ spec->num_pins = STAC92HD71BXX_NUM_PINS; ++ switch (codec->vendor_id) { ++ case 0x111d76b6: ++ case 0x111d76b7: ++ spec->pin_nids = stac92hd71bxx_pin_nids_4port; ++ break; ++ case 0x111d7603: ++ case 0x111d7608: ++ /* On 92HD75Bx 0x27 isn't a pin nid */ ++ spec->num_pins--; ++ /* fallthrough */ ++ default: ++ spec->pin_nids = stac92hd71bxx_pin_nids_6port; ++ } + spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); +- spec->pin_nids = stac92hd71bxx_pin_nids; +- memcpy(&spec->private_dimux, &stac92hd71bxx_dmux, +- sizeof(stac92hd71bxx_dmux)); + spec->board_config = snd_hda_check_board_config(codec, + STAC_92HD71BXX_MODELS, + stac92hd71bxx_models, + stac92hd71bxx_cfg_tbl); + again: +- if (spec->board_config < 0) { ++ if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: Unknown model for" + " STAC92HD71BXX, using BIOS defaults\n"); +- err = stac92xx_save_bios_config_regs(codec); +- if (err < 0) { +- stac92xx_free(codec); +- return err; +- } +- spec->pin_configs = spec->bios_pin_configs; +- } else { +- spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config]; +- stac92xx_set_config_regs(codec); +- } ++ else ++ stac92xx_set_config_regs(codec, ++ stac92hd71bxx_brd_tbl[spec->board_config]); + + if (spec->board_config > STAC_92HD71BXX_REF) { + /* GPIO0 = EAPD */ +@@ -4676,34 +5113,52 @@ again: + spec->gpio_data = 0x01; + } + ++ spec->dmic_nids = stac92hd71bxx_dmic_nids; ++ spec->dmux_nids = stac92hd71bxx_dmux_nids; ++ + switch (codec->vendor_id) { + case 0x111d76b6: /* 4 Port without Analog Mixer */ + case 0x111d76b7: ++ unmute_init++; ++ /* fallthru */ + case 0x111d76b4: /* 6 Port without Analog Mixer */ + case 0x111d76b5: ++ memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_nomixer, ++ sizeof(stac92hd71bxx_dmux_nomixer)); + spec->mixer = stac92hd71bxx_mixer; + spec->init = stac92hd71bxx_core_init; + codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; ++ spec->num_dmics = stac92hd71bxx_connected_ports(codec, ++ stac92hd71bxx_dmic_nids, ++ STAC92HD71BXX_NUM_DMICS); ++ if (spec->num_dmics) { ++ spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); ++ spec->dinput_mux = &spec->private_dimux; ++ ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1; ++ } + break; + case 0x111d7608: /* 5 Port with Analog Mixer */ ++ memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_amixer, ++ sizeof(stac92hd71bxx_dmux_amixer)); ++ spec->private_dimux.num_items--; + switch (spec->board_config) { + case STAC_HP_M4: + /* Enable VREF power saving on GPIO1 detect */ ++ err = stac_add_event(spec, codec->afg, ++ STAC_VREF_EVENT, 0x02); ++ if (err < 0) ++ return err; + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); + snd_hda_codec_write_cache(codec, codec->afg, 0, +- AC_VERB_SET_UNSOLICITED_ENABLE, +- (AC_USRSP_EN | STAC_VREF_EVENT | 0x01)); ++ AC_VERB_SET_UNSOLICITED_ENABLE, ++ AC_USRSP_EN | err); + spec->gpio_mask |= 0x02; + break; + } + if ((codec->revision_id & 0xf) == 0 || +- (codec->revision_id & 0xf) == 1) { +-#ifdef SND_HDA_NEEDS_RESUME +- codec->patch_ops = stac92hd71bxx_patch_ops; +-#endif ++ (codec->revision_id & 0xf) == 1) + spec->stream_delay = 40; /* 40 milliseconds */ +- } + + /* no output amps */ + spec->num_pwrs = 0; +@@ -4712,26 +5167,41 @@ again: + + /* disable VSW */ + spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF]; +- stac92xx_set_config_reg(codec, 0xf, 0x40f000f0); ++ unmute_init++; ++ snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0); ++ snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3); ++ stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0; ++ spec->num_dmics = stac92hd71bxx_connected_ports(codec, ++ stac92hd71bxx_dmic_nids, ++ STAC92HD71BXX_NUM_DMICS - 1); ++ spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); ++ ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 2; + break; + case 0x111d7603: /* 6 Port with Analog Mixer */ +- if ((codec->revision_id & 0xf) == 1) { +-#ifdef SND_HDA_NEEDS_RESUME +- codec->patch_ops = stac92hd71bxx_patch_ops; +-#endif ++ if ((codec->revision_id & 0xf) == 1) + spec->stream_delay = 40; /* 40 milliseconds */ +- } + + /* no output amps */ + spec->num_pwrs = 0; + /* fallthru */ + default: ++ memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_amixer, ++ sizeof(stac92hd71bxx_dmux_amixer)); + spec->dinput_mux = &spec->private_dimux; + spec->mixer = stac92hd71bxx_analog_mixer; + spec->init = stac92hd71bxx_analog_core_init; + codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; ++ spec->num_dmics = stac92hd71bxx_connected_ports(codec, ++ stac92hd71bxx_dmic_nids, ++ STAC92HD71BXX_NUM_DMICS); ++ spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); ++ ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1; + } + ++ if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) ++ snd_hda_sequence_write_cache(codec, unmute_init); ++ ++ spec->aloopback_ctl = stac92hd71bxx_loopback; + spec->aloopback_mask = 0x50; + spec->aloopback_shift = 0; + +@@ -4739,18 +5209,17 @@ again: + spec->digbeep_nid = 0x26; + spec->mux_nids = stac92hd71bxx_mux_nids; + spec->adc_nids = stac92hd71bxx_adc_nids; +- spec->dmic_nids = stac92hd71bxx_dmic_nids; +- spec->dmux_nids = stac92hd71bxx_dmux_nids; + spec->smux_nids = stac92hd71bxx_smux_nids; + spec->pwr_nids = stac92hd71bxx_pwr_nids; + + spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids); + spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids); ++ spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e); + + switch (spec->board_config) { + case STAC_HP_M4: + /* enable internal microphone */ +- stac92xx_set_config_reg(codec, 0x0e, 0x01813040); ++ snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040); + stac92xx_auto_set_pinctl(codec, 0x0e, + AC_PINCTL_IN_EN | AC_PINCTL_VREF_80); + /* fallthru */ +@@ -4763,23 +5232,38 @@ again: + case STAC_DELL_M4_3: + spec->num_dmics = 1; + spec->num_smuxes = 0; +- spec->num_dmuxes = 0; ++ spec->num_dmuxes = 1; ++ break; ++ case STAC_HP_DV5: ++ snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); ++ stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN); ++ break; ++ case STAC_HP_HDX: ++ spec->num_dmics = 1; ++ spec->num_dmuxes = 1; ++ spec->num_smuxes = 1; ++ /* ++ * For controlling MUTE LED on HP HDX16/HDX18 notebooks, ++ * the CONFIG_SND_HDA_POWER_SAVE is needed to be set. ++ */ ++#ifdef CONFIG_SND_HDA_POWER_SAVE ++ /* orange/white mute led on GPIO3, orange=0, white=1 */ ++ spec->gpio_mask |= 0x08; ++ spec->gpio_dir |= 0x08; ++ spec->gpio_data |= 0x08; /* set to white */ ++ ++ /* register check_power_status callback. */ ++ codec->patch_ops.check_power_status = ++ stac92xx_hp_hdx_check_power_status; ++#endif + break; +- default: +- spec->num_dmics = STAC92HD71BXX_NUM_DMICS; +- spec->num_smuxes = ARRAY_SIZE(stac92hd71bxx_smux_nids); +- spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); + }; + +- spec->multiout.num_dacs = 1; +- spec->multiout.hp_nid = 0x11; +- spec->multiout.dac_nids = stac92hd71bxx_dac_nids; ++ spec->multiout.dac_nids = spec->dac_nids; + if (spec->dinput_mux) +- spec->private_dimux.num_items += +- spec->num_dmics - +- (ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1); ++ spec->private_dimux.num_items += spec->num_dmics - ndmic_nids; + +- err = stac92xx_parse_auto_config(codec, 0x21, 0x23); ++ err = stac92xx_parse_auto_config(codec, 0x21, 0); + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " +@@ -4795,6 +5279,8 @@ again: + return err; + } + ++ codec->proc_widget_hook = stac92hd7x_proc_hook; ++ + return 0; + }; + +@@ -4852,19 +5338,12 @@ static int patch_stac922x(struct hda_codec *codec) + } + + again: +- if (spec->board_config < 0) { ++ if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, " + "using BIOS defaults\n"); +- err = stac92xx_save_bios_config_regs(codec); +- if (err < 0) { +- stac92xx_free(codec); +- return err; +- } +- spec->pin_configs = spec->bios_pin_configs; +- } else if (stac922x_brd_tbl[spec->board_config] != NULL) { +- spec->pin_configs = stac922x_brd_tbl[spec->board_config]; +- stac92xx_set_config_regs(codec); +- } ++ else ++ stac92xx_set_config_regs(codec, ++ stac922x_brd_tbl[spec->board_config]); + + spec->adc_nids = stac922x_adc_nids; + spec->mux_nids = stac922x_mux_nids; +@@ -4915,26 +5394,19 @@ static int patch_stac927x(struct hda_codec *codec) + return -ENOMEM; + + codec->spec = spec; ++ codec->slave_dig_outs = stac927x_slave_dig_outs; + spec->num_pins = ARRAY_SIZE(stac927x_pin_nids); + spec->pin_nids = stac927x_pin_nids; + spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS, + stac927x_models, + stac927x_cfg_tbl); + again: +- if (spec->board_config < 0 || !stac927x_brd_tbl[spec->board_config]) { +- if (spec->board_config < 0) +- snd_printdd(KERN_INFO "hda_codec: Unknown model for" +- "STAC927x, using BIOS defaults\n"); +- err = stac92xx_save_bios_config_regs(codec); +- if (err < 0) { +- stac92xx_free(codec); +- return err; +- } +- spec->pin_configs = spec->bios_pin_configs; +- } else { +- spec->pin_configs = stac927x_brd_tbl[spec->board_config]; +- stac92xx_set_config_regs(codec); +- } ++ if (spec->board_config < 0) ++ snd_printdd(KERN_INFO "hda_codec: Unknown model for" ++ "STAC927x, using BIOS defaults\n"); ++ else ++ stac92xx_set_config_regs(codec, ++ stac927x_brd_tbl[spec->board_config]); + + spec->digbeep_nid = 0x23; + spec->adc_nids = stac927x_adc_nids; +@@ -4963,15 +5435,15 @@ static int patch_stac927x(struct hda_codec *codec) + case 0x10280209: + case 0x1028022e: + /* correct the device field to SPDIF out */ +- stac92xx_set_config_reg(codec, 0x21, 0x01442070); ++ snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070); + break; + }; + /* configure the analog microphone on some laptops */ +- stac92xx_set_config_reg(codec, 0x0c, 0x90a79130); ++ snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130); + /* correct the front output jack as a hp out */ +- stac92xx_set_config_reg(codec, 0x0f, 0x0227011f); ++ snd_hda_codec_set_pincfg(codec, 0x0f, 0x0227011f); + /* correct the front input jack as a mic */ +- stac92xx_set_config_reg(codec, 0x0e, 0x02a79130); ++ snd_hda_codec_set_pincfg(codec, 0x0e, 0x02a79130); + /* fallthru */ + case STAC_DELL_3ST: + /* GPIO2 High = Enable EAPD */ +@@ -4998,6 +5470,7 @@ static int patch_stac927x(struct hda_codec *codec) + } + + spec->num_pwrs = 0; ++ spec->aloopback_ctl = stac927x_loopback; + spec->aloopback_mask = 0x40; + spec->aloopback_shift = 0; + spec->eapd_switch = 1; +@@ -5019,6 +5492,8 @@ static int patch_stac927x(struct hda_codec *codec) + + codec->patch_ops = stac92xx_patch_ops; + ++ codec->proc_widget_hook = stac927x_proc_hook; ++ + /* + * !!FIXME!! + * The STAC927x seem to require fairly long delays for certain +@@ -5054,18 +5529,11 @@ static int patch_stac9205(struct hda_codec *codec) + stac9205_models, + stac9205_cfg_tbl); + again: +- if (spec->board_config < 0) { ++ if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n"); +- err = stac92xx_save_bios_config_regs(codec); +- if (err < 0) { +- stac92xx_free(codec); +- return err; +- } +- spec->pin_configs = spec->bios_pin_configs; +- } else { +- spec->pin_configs = stac9205_brd_tbl[spec->board_config]; +- stac92xx_set_config_regs(codec); +- } ++ else ++ stac92xx_set_config_regs(codec, ++ stac9205_brd_tbl[spec->board_config]); + + spec->digbeep_nid = 0x23; + spec->adc_nids = stac9205_adc_nids; +@@ -5082,6 +5550,7 @@ static int patch_stac9205(struct hda_codec *codec) + + spec->init = stac9205_core_init; + spec->mixer = stac9205_mixer; ++ spec->aloopback_ctl = stac9205_loopback; + + spec->aloopback_mask = 0x40; + spec->aloopback_shift = 0; +@@ -5093,15 +5562,18 @@ static int patch_stac9205(struct hda_codec *codec) + switch (spec->board_config){ + case STAC_9205_DELL_M43: + /* Enable SPDIF in/out */ +- stac92xx_set_config_reg(codec, 0x1f, 0x01441030); +- stac92xx_set_config_reg(codec, 0x20, 0x1c410030); ++ snd_hda_codec_set_pincfg(codec, 0x1f, 0x01441030); ++ snd_hda_codec_set_pincfg(codec, 0x20, 0x1c410030); + + /* Enable unsol response for GPIO4/Dock HP connection */ ++ err = stac_add_event(spec, codec->afg, STAC_VREF_EVENT, 0x01); ++ if (err < 0) ++ return err; + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, +- (AC_USRSP_EN | STAC_HP_EVENT)); ++ AC_USRSP_EN | err); + + spec->gpio_dir = 0x0b; + spec->eapd_mask = 0x01; +@@ -5139,6 +5611,8 @@ static int patch_stac9205(struct hda_codec *codec) + + codec->patch_ops = stac92xx_patch_ops; + ++ codec->proc_widget_hook = stac9205_proc_hook; ++ + return 0; + } + +@@ -5146,239 +5620,87 @@ static int patch_stac9205(struct hda_codec *codec) + * STAC9872 hack + */ + +-/* static config for Sony VAIO FE550G and Sony VAIO AR */ +-static hda_nid_t vaio_dacs[] = { 0x2 }; +-#define VAIO_HP_DAC 0x5 +-static hda_nid_t vaio_adcs[] = { 0x8 /*,0x6*/ }; +-static hda_nid_t vaio_mux_nids[] = { 0x15 }; +- +-static struct hda_input_mux vaio_mux = { +- .num_items = 3, +- .items = { +- /* { "HP", 0x0 }, */ +- { "Mic Jack", 0x1 }, +- { "Internal Mic", 0x2 }, +- { "PCM", 0x3 }, +- } +-}; +- +-static struct hda_verb vaio_init[] = { +- {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */ +- {0x0a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | STAC_HP_EVENT}, +- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */ +- {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */ +- {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */ +- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */ +- {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */ +- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */ +- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */ +- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */ +- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */ +- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */ +- {} +-}; +- +-static struct hda_verb vaio_ar_init[] = { +- {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */ +- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */ +- {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */ +- {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */ +-/* {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */ +- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */ ++static struct hda_verb stac9872_core_init[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */ +- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */ +- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */ +-/* {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */ +- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */ +- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */ + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */ + {} + }; + +-/* bind volumes of both NID 0x02 and 0x05 */ +-static struct hda_bind_ctls vaio_bind_master_vol = { +- .ops = &snd_hda_bind_vol, +- .values = { +- HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), +- HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT), +- 0 +- }, +-}; +- +-/* bind volumes of both NID 0x02 and 0x05 */ +-static struct hda_bind_ctls vaio_bind_master_sw = { +- .ops = &snd_hda_bind_sw, +- .values = { +- HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), +- HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT), +- 0, +- }, +-}; +- +-static struct snd_kcontrol_new vaio_mixer[] = { +- HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol), +- HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw), +- /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */ ++static struct snd_kcontrol_new stac9872_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT), +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "Capture Source", +- .count = 1, +- .info = stac92xx_mux_enum_info, +- .get = stac92xx_mux_enum_get, +- .put = stac92xx_mux_enum_put, +- }, +- {} ++ { } /* end */ + }; + +-static struct snd_kcontrol_new vaio_ar_mixer[] = { +- HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol), +- HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw), +- /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */ +- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT), +- HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT), +- /*HDA_CODEC_MUTE("Optical Out Switch", 0x10, 0, HDA_OUTPUT), +- HDA_CODEC_VOLUME("Optical Out Volume", 0x10, 0, HDA_OUTPUT),*/ +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "Capture Source", +- .count = 1, +- .info = stac92xx_mux_enum_info, +- .get = stac92xx_mux_enum_get, +- .put = stac92xx_mux_enum_put, +- }, +- {} ++static hda_nid_t stac9872_pin_nids[] = { ++ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, ++ 0x11, 0x13, 0x14, + }; + +-static struct hda_codec_ops stac9872_patch_ops = { +- .build_controls = stac92xx_build_controls, +- .build_pcms = stac92xx_build_pcms, +- .init = stac92xx_init, +- .free = stac92xx_free, +-#ifdef SND_HDA_NEEDS_RESUME +- .resume = stac92xx_resume, +-#endif ++static hda_nid_t stac9872_adc_nids[] = { ++ 0x8 /*,0x6*/ + }; + +-static int stac9872_vaio_init(struct hda_codec *codec) +-{ +- int err; +- +- err = stac92xx_init(codec); +- if (err < 0) +- return err; +- if (codec->patch_ops.unsol_event) +- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); +- return 0; +-} +- +-static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res) +-{ +- if (get_hp_pin_presence(codec, 0x0a)) { +- stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN); +- stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN); +- } else { +- stac92xx_reset_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN); +- stac92xx_set_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN); +- } +-} +- +-static void stac9872_vaio_unsol_event(struct hda_codec *codec, unsigned int res) +-{ +- switch (res >> 26) { +- case STAC_HP_EVENT: +- stac9872_vaio_hp_detect(codec, res); +- break; +- } +-} +- +-static struct hda_codec_ops stac9872_vaio_patch_ops = { +- .build_controls = stac92xx_build_controls, +- .build_pcms = stac92xx_build_pcms, +- .init = stac9872_vaio_init, +- .free = stac92xx_free, +- .unsol_event = stac9872_vaio_unsol_event, +-#ifdef CONFIG_PM +- .resume = stac92xx_resume, +-#endif ++static hda_nid_t stac9872_mux_nids[] = { ++ 0x15 + }; + +-enum { /* FE and SZ series. id=0x83847661 and subsys=0x104D0700 or 104D1000. */ +- CXD9872RD_VAIO, +- /* Unknown. id=0x83847662 and subsys=0x104D1200 or 104D1000. */ +- STAC9872AK_VAIO, +- /* Unknown. id=0x83847661 and subsys=0x104D1200. */ +- STAC9872K_VAIO, +- /* AR Series. id=0x83847664 and subsys=104D1300 */ +- CXD9872AKD_VAIO, +- STAC_9872_MODELS, ++static unsigned int stac9872_vaio_pin_configs[9] = { ++ 0x03211020, 0x411111f0, 0x411111f0, 0x03a15030, ++ 0x411111f0, 0x90170110, 0x411111f0, 0x411111f0, ++ 0x90a7013e + }; + + static const char *stac9872_models[STAC_9872_MODELS] = { +- [CXD9872RD_VAIO] = "vaio", +- [CXD9872AKD_VAIO] = "vaio-ar", ++ [STAC_9872_AUTO] = "auto", ++ [STAC_9872_VAIO] = "vaio", ++}; ++ ++static unsigned int *stac9872_brd_tbl[STAC_9872_MODELS] = { ++ [STAC_9872_VAIO] = stac9872_vaio_pin_configs, + }; + + static struct snd_pci_quirk stac9872_cfg_tbl[] = { +- SND_PCI_QUIRK(0x104d, 0x81e6, "Sony VAIO F/S", CXD9872RD_VAIO), +- SND_PCI_QUIRK(0x104d, 0x81ef, "Sony VAIO F/S", CXD9872RD_VAIO), +- SND_PCI_QUIRK(0x104d, 0x81fd, "Sony VAIO AR", CXD9872AKD_VAIO), +- SND_PCI_QUIRK(0x104d, 0x8205, "Sony VAIO AR", CXD9872AKD_VAIO), +- {} ++ {} /* terminator */ + }; + + static int patch_stac9872(struct hda_codec *codec) + { + struct sigmatel_spec *spec; +- int board_config; ++ int err; + +- board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS, +- stac9872_models, +- stac9872_cfg_tbl); +- if (board_config < 0) +- /* unknown config, let generic-parser do its job... */ +- return snd_hda_parse_generic_codec(codec); +- + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; +- + codec->spec = spec; +- switch (board_config) { +- case CXD9872RD_VAIO: +- case STAC9872AK_VAIO: +- case STAC9872K_VAIO: +- spec->mixer = vaio_mixer; +- spec->init = vaio_init; +- spec->multiout.max_channels = 2; +- spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs); +- spec->multiout.dac_nids = vaio_dacs; +- spec->multiout.hp_nid = VAIO_HP_DAC; +- spec->num_adcs = ARRAY_SIZE(vaio_adcs); +- spec->adc_nids = vaio_adcs; +- spec->num_pwrs = 0; +- spec->input_mux = &vaio_mux; +- spec->mux_nids = vaio_mux_nids; +- codec->patch_ops = stac9872_vaio_patch_ops; +- break; +- +- case CXD9872AKD_VAIO: +- spec->mixer = vaio_ar_mixer; +- spec->init = vaio_ar_init; +- spec->multiout.max_channels = 2; +- spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs); +- spec->multiout.dac_nids = vaio_dacs; +- spec->multiout.hp_nid = VAIO_HP_DAC; +- spec->num_adcs = ARRAY_SIZE(vaio_adcs); +- spec->num_pwrs = 0; +- spec->adc_nids = vaio_adcs; +- spec->input_mux = &vaio_mux; +- spec->mux_nids = vaio_mux_nids; +- codec->patch_ops = stac9872_patch_ops; +- break; +- } + ++ spec->board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS, ++ stac9872_models, ++ stac9872_cfg_tbl); ++ if (spec->board_config < 0) ++ snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9872, " ++ "using BIOS defaults\n"); ++ else ++ stac92xx_set_config_regs(codec, ++ stac9872_brd_tbl[spec->board_config]); ++ ++ spec->num_pins = ARRAY_SIZE(stac9872_pin_nids); ++ spec->pin_nids = stac9872_pin_nids; ++ spec->multiout.dac_nids = spec->dac_nids; ++ spec->num_adcs = ARRAY_SIZE(stac9872_adc_nids); ++ spec->adc_nids = stac9872_adc_nids; ++ spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids); ++ spec->mux_nids = stac9872_mux_nids; ++ spec->mixer = stac9872_mixer; ++ spec->init = stac9872_core_init; ++ ++ err = stac92xx_parse_auto_config(codec, 0x10, 0x12); ++ if (err < 0) { ++ stac92xx_free(codec); ++ return -EINVAL; ++ } ++ spec->input_mux = &spec->private_imux; ++ codec->patch_ops = stac92xx_patch_ops; + return 0; + } + +@@ -5386,7 +5708,7 @@ static int patch_stac9872(struct hda_codec *codec) + /* + * patch entries + */ +-struct hda_codec_preset snd_hda_preset_sigmatel[] = { ++static struct hda_codec_preset snd_hda_preset_sigmatel[] = { + { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 }, + { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x }, + { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x }, +@@ -5436,6 +5758,7 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = { + { .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx}, + { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx}, + { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx}, ++ { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx}, + { .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx}, + { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx }, + { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx }, +@@ -5450,3 +5773,27 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = { + { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx }, + {} /* terminator */ + }; ++ ++MODULE_ALIAS("snd-hda-codec-id:8384*"); ++MODULE_ALIAS("snd-hda-codec-id:111d*"); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec"); ++ ++static struct hda_codec_preset_list sigmatel_list = { ++ .preset = snd_hda_preset_sigmatel, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init patch_sigmatel_init(void) ++{ ++ return snd_hda_add_codec_preset(&sigmatel_list); ++} ++ ++static void __exit patch_sigmatel_exit(void) ++{ ++ snd_hda_delete_codec_preset(&sigmatel_list); ++} ++ ++module_init(patch_sigmatel_init) ++module_exit(patch_sigmatel_exit) +diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c +index 63e4871..b25a5cc 100644 +--- a/sound/pci/hda/patch_via.c ++++ b/sound/pci/hda/patch_via.c +@@ -47,15 +47,11 @@ + #include + #include "hda_codec.h" + #include "hda_local.h" +-#include "hda_patch.h" + + /* amp values */ + #define AMP_VAL_IDX_SHIFT 19 + #define AMP_VAL_IDX_MASK (0x0f<<19) + +-#define NUM_CONTROL_ALLOC 32 +-#define NUM_VERB_ALLOC 32 +- + /* Pin Widget NID */ + #define VT1708_HP_NID 0x13 + #define VT1708_DIGOUT_NID 0x14 +@@ -145,8 +141,6 @@ enum { + AUTO_SEQ_SIDE + }; + +-#define get_amp_nid(kc) ((kc)->private_value & 0xffff) +- + /* 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, +@@ -227,8 +221,7 @@ struct via_spec { + + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; +- unsigned int num_kctl_alloc, num_kctl_used; +- struct snd_kcontrol_new *kctl_alloc; ++ struct snd_array kctls; + struct hda_input_mux private_imux[2]; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + +@@ -272,33 +265,31 @@ static int via_add_control(struct via_spec *spec, int type, const char *name, + { + struct snd_kcontrol_new *knew; + +- if (spec->num_kctl_used >= spec->num_kctl_alloc) { +- int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; +- +- /* array + terminator */ +- knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); +- if (!knew) +- return -ENOMEM; +- if (spec->kctl_alloc) { +- memcpy(knew, spec->kctl_alloc, +- sizeof(*knew) * spec->num_kctl_alloc); +- kfree(spec->kctl_alloc); +- } +- spec->kctl_alloc = knew; +- spec->num_kctl_alloc = num; +- } +- +- knew = &spec->kctl_alloc[spec->num_kctl_used]; ++ snd_array_init(&spec->kctls, sizeof(*knew), 32); ++ knew = snd_array_new(&spec->kctls); ++ if (!knew) ++ return -ENOMEM; + *knew = vt1708_control_templates[type]; + knew->name = kstrdup(name, GFP_KERNEL); +- + if (!knew->name) + return -ENOMEM; + knew->private_value = val; +- spec->num_kctl_used++; + return 0; + } + ++static void via_free_kctls(struct hda_codec *codec) ++{ ++ struct via_spec *spec = codec->spec; ++ ++ if (spec->kctls.list) { ++ struct snd_kcontrol_new *kctl = spec->kctls.list; ++ int i; ++ for (i = 0; i < spec->kctls.used; i++) ++ kfree(kctl[i].name); ++ } ++ snd_array_free(&spec->kctls); ++} ++ + /* 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) +@@ -896,6 +887,7 @@ static int via_build_controls(struct hda_codec *codec) + if (err < 0) + return err; + } ++ via_free_kctls(codec); /* no longer needed */ + return 0; + } + +@@ -941,17 +933,11 @@ static int via_build_pcms(struct hda_codec *codec) + static void via_free(struct hda_codec *codec) + { + struct via_spec *spec = codec->spec; +- unsigned int i; + + if (!spec) + return; + +- if (spec->kctl_alloc) { +- for (i = 0; i < spec->num_kctl_used; i++) +- kfree(spec->kctl_alloc[i].name); +- kfree(spec->kctl_alloc); +- } +- ++ via_free_kctls(codec); + kfree(codec->spec); + } + +@@ -1322,16 +1308,13 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) + unsigned int def_conf; + unsigned char seqassoc; + +- def_conf = snd_hda_codec_read(codec, nid, 0, +- AC_VERB_GET_CONFIG_DEFAULT, 0); ++ 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_write(codec, nid, 0, +- AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, +- def_conf >> 24); ++ snd_hda_codec_set_pincfg(codec, nid, def_conf); + } + } + +@@ -1368,13 +1351,13 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + +- if (spec->autocfg.dig_out_pin) ++ if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = VT1708_DIGIN_NID; + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; ++ if (spec->kctls.list) ++ spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; + +@@ -1841,13 +1824,13 @@ static int vt1709_parse_auto_config(struct hda_codec *codec) + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + +- if (spec->autocfg.dig_out_pin) ++ if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = VT1709_DIGIN_NID; + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; ++ if (spec->kctls.list) ++ spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + +@@ -2385,13 +2368,13 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec) + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + +- if (spec->autocfg.dig_out_pin) ++ if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = VT1708B_DIGIN_NID; + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; ++ if (spec->kctls.list) ++ spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + +@@ -2850,13 +2833,13 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec) + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + +- if (spec->autocfg.dig_out_pin) ++ if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID; + + spec->extra_dig_out_nid = 0x15; + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; ++ if (spec->kctls.list) ++ spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + +@@ -3169,13 +3152,13 @@ static int vt1702_parse_auto_config(struct hda_codec *codec) + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + +- if (spec->autocfg.dig_out_pin) ++ if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = VT1702_DIGOUT_NID; + + spec->extra_dig_out_nid = 0x1B; + +- if (spec->kctl_alloc) +- spec->mixers[spec->num_mixers++] = spec->kctl_alloc; ++ if (spec->kctls.list) ++ spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + +@@ -3262,74 +3245,97 @@ static int patch_vt1702(struct hda_codec *codec) + /* + * patch entries + */ +-struct hda_codec_preset snd_hda_preset_via[] = { +- { .id = 0x11061708, .name = "VIA VT1708", .patch = patch_vt1708}, +- { .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708}, +- { .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708}, +- { .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708}, +- { .id = 0x1106E710, .name = "VIA VT1709 10-Ch", ++static struct hda_codec_preset snd_hda_preset_via[] = { ++ { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, ++ { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, ++ { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, ++ { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, ++ { .id = 0x1106e710, .name = "VT1709 10-Ch", + .patch = patch_vt1709_10ch}, +- { .id = 0x1106E711, .name = "VIA VT1709 10-Ch", ++ { .id = 0x1106e711, .name = "VT1709 10-Ch", + .patch = patch_vt1709_10ch}, +- { .id = 0x1106E712, .name = "VIA VT1709 10-Ch", ++ { .id = 0x1106e712, .name = "VT1709 10-Ch", + .patch = patch_vt1709_10ch}, +- { .id = 0x1106E713, .name = "VIA VT1709 10-Ch", ++ { .id = 0x1106e713, .name = "VT1709 10-Ch", + .patch = patch_vt1709_10ch}, +- { .id = 0x1106E714, .name = "VIA VT1709 6-Ch", ++ { .id = 0x1106e714, .name = "VT1709 6-Ch", + .patch = patch_vt1709_6ch}, +- { .id = 0x1106E715, .name = "VIA VT1709 6-Ch", ++ { .id = 0x1106e715, .name = "VT1709 6-Ch", + .patch = patch_vt1709_6ch}, +- { .id = 0x1106E716, .name = "VIA VT1709 6-Ch", ++ { .id = 0x1106e716, .name = "VT1709 6-Ch", + .patch = patch_vt1709_6ch}, +- { .id = 0x1106E717, .name = "VIA VT1709 6-Ch", ++ { .id = 0x1106e717, .name = "VT1709 6-Ch", + .patch = patch_vt1709_6ch}, +- { .id = 0x1106E720, .name = "VIA VT1708B 8-Ch", ++ { .id = 0x1106e720, .name = "VT1708B 8-Ch", + .patch = patch_vt1708B_8ch}, +- { .id = 0x1106E721, .name = "VIA VT1708B 8-Ch", ++ { .id = 0x1106e721, .name = "VT1708B 8-Ch", + .patch = patch_vt1708B_8ch}, +- { .id = 0x1106E722, .name = "VIA VT1708B 8-Ch", ++ { .id = 0x1106e722, .name = "VT1708B 8-Ch", + .patch = patch_vt1708B_8ch}, +- { .id = 0x1106E723, .name = "VIA VT1708B 8-Ch", ++ { .id = 0x1106e723, .name = "VT1708B 8-Ch", + .patch = patch_vt1708B_8ch}, +- { .id = 0x1106E724, .name = "VIA VT1708B 4-Ch", ++ { .id = 0x1106e724, .name = "VT1708B 4-Ch", + .patch = patch_vt1708B_4ch}, +- { .id = 0x1106E725, .name = "VIA VT1708B 4-Ch", ++ { .id = 0x1106e725, .name = "VT1708B 4-Ch", + .patch = patch_vt1708B_4ch}, +- { .id = 0x1106E726, .name = "VIA VT1708B 4-Ch", ++ { .id = 0x1106e726, .name = "VT1708B 4-Ch", + .patch = patch_vt1708B_4ch}, +- { .id = 0x1106E727, .name = "VIA VT1708B 4-Ch", ++ { .id = 0x1106e727, .name = "VT1708B 4-Ch", + .patch = patch_vt1708B_4ch}, +- { .id = 0x11060397, .name = "VIA VT1708S", ++ { .id = 0x11060397, .name = "VT1708S", + .patch = patch_vt1708S}, +- { .id = 0x11061397, .name = "VIA VT1708S", ++ { .id = 0x11061397, .name = "VT1708S", + .patch = patch_vt1708S}, +- { .id = 0x11062397, .name = "VIA VT1708S", ++ { .id = 0x11062397, .name = "VT1708S", + .patch = patch_vt1708S}, +- { .id = 0x11063397, .name = "VIA VT1708S", ++ { .id = 0x11063397, .name = "VT1708S", + .patch = patch_vt1708S}, +- { .id = 0x11064397, .name = "VIA VT1708S", ++ { .id = 0x11064397, .name = "VT1708S", + .patch = patch_vt1708S}, +- { .id = 0x11065397, .name = "VIA VT1708S", ++ { .id = 0x11065397, .name = "VT1708S", + .patch = patch_vt1708S}, +- { .id = 0x11066397, .name = "VIA VT1708S", ++ { .id = 0x11066397, .name = "VT1708S", + .patch = patch_vt1708S}, +- { .id = 0x11067397, .name = "VIA VT1708S", ++ { .id = 0x11067397, .name = "VT1708S", + .patch = patch_vt1708S}, +- { .id = 0x11060398, .name = "VIA VT1702", ++ { .id = 0x11060398, .name = "VT1702", + .patch = patch_vt1702}, +- { .id = 0x11061398, .name = "VIA VT1702", ++ { .id = 0x11061398, .name = "VT1702", + .patch = patch_vt1702}, +- { .id = 0x11062398, .name = "VIA VT1702", ++ { .id = 0x11062398, .name = "VT1702", + .patch = patch_vt1702}, +- { .id = 0x11063398, .name = "VIA VT1702", ++ { .id = 0x11063398, .name = "VT1702", + .patch = patch_vt1702}, +- { .id = 0x11064398, .name = "VIA VT1702", ++ { .id = 0x11064398, .name = "VT1702", + .patch = patch_vt1702}, +- { .id = 0x11065398, .name = "VIA VT1702", ++ { .id = 0x11065398, .name = "VT1702", + .patch = patch_vt1702}, +- { .id = 0x11066398, .name = "VIA VT1702", ++ { .id = 0x11066398, .name = "VT1702", + .patch = patch_vt1702}, +- { .id = 0x11067398, .name = "VIA VT1702", ++ { .id = 0x11067398, .name = "VT1702", + .patch = patch_vt1702}, + {} /* terminator */ + }; ++ ++MODULE_ALIAS("snd-hda-codec-id:1106*"); ++ ++static struct hda_codec_preset_list via_list = { ++ .preset = snd_hda_preset_via, ++ .owner = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("VIA HD-audio codec"); ++ ++static int __init patch_via_init(void) ++{ ++ return snd_hda_add_codec_preset(&via_list); ++} ++ ++static void __exit patch_via_exit(void) ++{ ++ snd_hda_delete_codec_preset(&via_list); ++} ++ ++module_init(patch_via_init) ++module_exit(patch_via_exit) diff --git a/master/series b/master/series index f436bde..b01e0af 100644 --- a/master/series +++ b/master/series @@ -260,3 +260,4 @@ pciback-flr snd-hda-intel-hp-elite-6930p-laptop-mode pass2-driver remove-release-flr +intel-hda-2.6.30