]> xenbits.xen.org Git - xenclient/linux-2.6.27-pq.git/commitdiff
Backport intel hda support from Linux 2.6.30.
authorChristian Limpach <Christian.Limpach@citrix.com>
Thu, 25 Jun 2009 00:10:31 +0000 (01:10 +0100)
committerChristian Limpach <Christian.Limpach@citrix.com>
Thu, 25 Jun 2009 00:10:31 +0000 (01:10 +0100)
Required to get sound output on Dell E4300.

master/intel-hda-2.6.30 [new file with mode: 0644]
master/series

diff --git a/master/intel-hda-2.6.30 b/master/intel-hda-2.6.30
new file mode 100644 (file)
index 0000000..a7e075f
--- /dev/null
@@ -0,0 +1,17458 @@
+diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
+index 4a6eba5..094bf89 100644
+--- a/include/linux/pci_ids.h
++++ b/include/linux/pci_ids.h
+@@ -2087,6 +2087,8 @@
+ #define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c
+ #define PCI_DEVICE_ID_MELLANOX_SINAI  0x6274
++#define PCI_VENDOR_ID_DFI             0x15bd
++
+ #define PCI_VENDOR_ID_QUICKNET                0x15e2
+ #define PCI_DEVICE_ID_QUICKNET_XJ     0x0500
+diff --git a/include/sound/core.h b/include/sound/core.h
+index 558b962..56af3dc 100644
+--- a/include/sound/core.h
++++ b/include/sound/core.h
+@@ -28,6 +28,7 @@
+ #include <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, &param) != 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)
index f436bde7e21eaaf1e2d39b37466343475061efe3..b01e0af446ba5f6e324536e60ee4f09499f0858b 100644 (file)
@@ -260,3 +260,4 @@ pciback-flr
 snd-hda-intel-hp-elite-6930p-laptop-mode
 pass2-driver
 remove-release-flr
+intel-hda-2.6.30