]> xenbits.xensource.com Git - people/ssmith/nc2-2.6.27.git/commitdiff
patch intel-hda-2.6.30 base-plus-patchqueue-97b1e293bee58f2cffa45260a51f349b2516de05
authorSteven Smith <ssmith@weybridge.uk.xensource.com>
Tue, 30 Jun 2009 11:55:47 +0000 (12:55 +0100)
committerSteven Smith <ssmith@weybridge.uk.xensource.com>
Tue, 30 Jun 2009 11:55:47 +0000 (12:55 +0100)
26 files changed:
include/linux/pci_ids.h
include/sound/core.h
include/sound/jack.h [new file with mode: 0644]
sound/pci/hda/Kconfig [new file with mode: 0644]
sound/pci/hda/Makefile
sound/pci/hda/hda_beep.c
sound/pci/hda/hda_beep.h
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_eld.c [new file with mode: 0644]
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_hwdep.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/hda_patch.h [deleted file]
sound/pci/hda/hda_proc.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_atihdmi.c
sound/pci/hda/patch_cmedia.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_intelhdmi.c [new file with mode: 0644]
sound/pci/hda/patch_nvhdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_si3054.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c

index 4a6eba532db050bff65def8dd5f65c868299f063..094bf89769cd6abdf9c8416ac5c2b66ca1332929 100644 (file)
 #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
 
index 558b96284bd22eba399ff2e8f28147a5c46b2eb0..56af3dc70213decd0282c80fc9c9baa4461955dc 100644 (file)
@@ -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 (file)
index 0000000..f236e42
--- /dev/null
@@ -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 (file)
index 0000000..eb2a19b
--- /dev/null
@@ -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
index 1980c6d207e7602dce5e8881223a6dff2b194cda..50f9d0967251296807caa86eea762f80c56fe3ff 100644 (file)
@@ -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
index 3ecd7e797deecbbb9fbf5bbfa9ea3c02c00fdaee..4de5bacd392924d94482a45b8885182e966780ab 100644 (file)
@@ -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);
index b9679f081cae9006a3a81b784f5e1e4927f567f5..51bf6a5daf39bd32bf9f1be6c6f94631426878fa 100644 (file)
@@ -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
index b5b1ba54d87e4ed4e8961226c44414d4b9ba02e1..8820faf6c9d82cd556c7de92ec55cb11efa56ebd 100644 (file)
 #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");
index 26f6f9ed04d8845052f902f40e586b93e9257dc3..2fdecf4b0eb631b89ef15f47ef1f37c3ac2e13ed 100644 (file)
@@ -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 */
@@ -519,6 +519,36 @@ enum {
 /* max. codec address */
 #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);
 
@@ -830,6 +909,13 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state);
 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
  */
@@ -837,12 +923,25 @@ int snd_hda_resume(struct hda_bus *bus);
 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 (file)
index 0000000..fcad5ec
--- /dev/null
@@ -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 */
index 59e4389c94a4bb87b1aa85075d2d127e5ae542da..1d5797a966821aa197460ee1836d99452493c595 100644 (file)
@@ -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);
index 6e18a422d993efcf68a2bf4be92d534f96f30d25..1c57505c2874d152ca4e76e3fbdefae5ecd81383 100644 (file)
 #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 */
index d9d2943fb5c735f7c6b6ef4bc935280303624bef..d1c03f916546a010b84bd69e9beb332d758f1550 100644 (file)
@@ -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);
index 3018b76559e387bcac0aa473efcc4a0bc76ef28d..83349013b4df1bcd5e8d5ea762153a1e3049920c 100644 (file)
@@ -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 (file)
index dfbcfa8..0000000
+++ /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[];
index 0890528fc784eba78090caf7ab9a2dd9e72519d4..93d7499350c6609f3e4a5a9af6396bf0ac6c8447 100644 (file)
@@ -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);
 }
index 10fe100c8656ac3a0c7fdd2dcffd672f71a650f3..84cc49ca91487d8535bde13aaf60139bb16c4fa7 100644 (file)
 #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)
index ba61575983fd68965ad5bd6b52e5439cad03da93..233e4778bba9dad5921cf0062ceff19f698f0a9b 100644 (file)
@@ -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)
index 6ef57fbfb6eb7821c7c44c88e8c7c1a357c8945a..c921264bbd719c2364e2b073d7c7db562cb8488f 100644 (file)
@@ -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)
index a50089f62894887452b5b3b07eb8532f12b1b4e2..4fcbe21829abfe27104f7dabeed8fb801a301537 100644 (file)
 #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
 #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 (file)
index 0000000..fcc77fe
--- /dev/null
@@ -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)
index 2eed2c8b98dabc3434ddabfbc2c2db1be63e7e65..d57d8132a06ebedfc6855a4c5e12507aac50651a 100644 (file)
@@ -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)
index 067e6ed7cfa1e1b289dd796f68dc07ad1572dd6d..0fd258eba3a57e917c07cc88fc18096e0b6bc1e0 100644 (file)
@@ -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
@@ -718,6 +762,67 @@ static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol,
          .private_value = nid | (mask<<16) }
 #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
  */
@@ -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 */
 };
 
@@ -7197,25 +7589,12 @@ static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = {
        HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
        HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("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,
-       },
+       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("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
+       HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
        { } /* end */
 };
 
@@ -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 */
 };
 
@@ -9306,16 +9698,77 @@ static struct snd_kcontrol_new alc262_sony_mixer[] = {
        { } /* end */
 };
 
-static struct snd_kcontrol_new alc262_benq_t31_mixer[] = {
-       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
-       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("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
-       HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
-       { } /* end */
-};
+static struct snd_kcontrol_new alc262_benq_t31_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       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("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+       { } /* 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),
        { }
 };
 
@@ -11972,25 +12452,6 @@ static struct snd_kcontrol_new alc269_eeepc_mixer[] = {
        { } /* end */
 };
 
-/* 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),
@@ -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;
+       if (spec->kctls.list)
+               add_mixer(spec, spec->kctls.list);
 
-       /* 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;
-
-       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",
@@ -12759,19 +13327,7 @@ static struct snd_kcontrol_new alc861_toshiba_mixer[] = {
         /* output mixer control */
        HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT),
        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,
-       },
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
 
        { } /* 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)
index 9332b63e406cc963dfab355011b254493808db2f..43b436c5d01bc245a3bbbdfcd6c54f08af0feaa3 100644 (file)
@@ -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)
index 5e89424b61d00509a908e971a47a405c97726205..d2fd8ef6aef846b9c3bf13093348c7d11733b1e3 100644 (file)
 #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)
-{
-       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)
+static void stac92xx_set_config_regs(struct hda_codec *codec,
+                                    unsigned int *pincfgs)
 {
        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;
+}
 
-       if ((spec->multiout.num_dacs - cfg->line_outs) > 0 &&
-           cfg->hp_outs == 1 && !spec->multiout.hp_nid)
-               spec->multiout.hp_nid = nid;
+/* 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;
+
+       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,61 +4386,205 @@ 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;
+
+       tag = (res >> 26) & 0x7f;
+       event = stac_get_event_from_tag(codec, tag);
+       if (!event)
+               return;
 
-       switch ((res >> 26) & 0x70) {
+       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;
 }
-#endif
 
-static struct hda_codec_ops stac92xx_patch_ops = {
-       .build_controls = stac92xx_build_controls,
+
+/*
+ * 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
+
+static struct hda_codec_ops stac92xx_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
+       .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)
index 63e4871e5d8fed3344577c6e91c0a2d4ec82c506..b25a5cc637d6072cbbad235753d64ff6c12c3ff1 100644 (file)
 #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)