--- /dev/null
+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 <linux/rwsem.h> /* struct rw_semaphore */
+ #include <linux/pm.h> /* pm_message_t */
+ #include <linux/device.h>
++#include <linux/stringify.h>
+
+ /* 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 <sound/core.h>
++
++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 <sound/initval.h>
+ #include "hda_local.h"
+ #include <sound/hda_hwdep.h>
+-#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 <wfg@linux.intel.com>
++ *
++ * This driver is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This driver is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/init.h>
++#include <sound/core.h>
++#include <asm/unaligned.h>
++#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 <linux/pci.h>
+ #include <linux/compat.h>
+ #include <linux/mutex.h>
++#include <linux/ctype.h>
+ #include <sound/core.h>
+ #include "hda_codec.h"
+ #include "hda_local.h"
+ #include <sound/hda_hwdep.h>
++#include <sound/minors.h>
++
++/* 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<<i)) ? 1 : 0,
+ (direction & (1<<i)) ? 1 : 0,
+ (wake & (1<<i)) ? 1 : 0,
+ (sticky & (1<<i)) ? 1 : 0,
+- (data & (1<<i)) ? 1 : 0);
++ (data & (1<<i)) ? 1 : 0,
++ (unsol & (1<<i)) ? 1 : 0);
+ /* FIXME: add GPO and GPI pin information */
+ }
+
+@@ -515,15 +463,15 @@ static void print_codec_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+ {
+ struct hda_codec *codec = entry->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 <sound/core.h>
+ #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 <sound/core.h>
+ #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 <sound/core.h>
+ #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 <linux/slab.h>
+ #include <linux/pci.h>
+ #include <sound/core.h>
++#include <sound/jack.h>
++
+ #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 <zhe.jiang@intel.com>
++ * Wu Fengguang <wfg@linux.intel.com>
++ *
++ * Maintained by:
++ * Wu Fengguang <wfg@linux.intel.com>
++ *
++ * 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 <linux/init.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <sound/core.h>
++#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 <sound/core.h>
+ #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 <sound/core.h>
+ #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 <linux/pci.h>
+ #include <sound/core.h>
+ #include <sound/asoundef.h>
++#include <sound/jack.h>
+ #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 <sound/asoundef.h>
+ #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)