]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'topic/via-cleanup' into topic/hda
authorTakashi Iwai <tiwai@suse.de>
Mon, 27 Jun 2011 12:32:50 +0000 (14:32 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 27 Jun 2011 12:32:50 +0000 (14:32 +0200)
sound/pci/hda/Kconfig
sound/pci/hda/Makefile
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/patch_ca0132.c [new file with mode: 0644]
sound/pci/hda/patch_realtek.c

index 0ea5cc60ac782f1cd9d05161455b1e1a79d4cd85..85217bd96d855bc229f1fb859c3def767e320b09 100644 (file)
@@ -171,6 +171,19 @@ config SND_HDA_CODEC_CA0110
          snd-hda-codec-ca0110.
          This module is automatically loaded at probing.
 
+config SND_HDA_CODEC_CA0132
+       bool "Build Creative CA0132 codec support"
+       depends on SND_HDA_INTEL
+       default y
+       help
+         Say Y here to include Creative CA0132 codec 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-ca0132.
+         This module is automatically loaded at probing.
+
 config SND_HDA_CODEC_CMEDIA
        bool "Build C-Media HD-audio codec support"
        default y
index 17ef3658f34b72d42d1deede9bc5f4efb1054a27..87365d5ea2a9e6cb561667a453232917157ef97a 100644 (file)
@@ -13,6 +13,7 @@ snd-hda-codec-idt-objs :=     patch_sigmatel.o
 snd-hda-codec-si3054-objs :=   patch_si3054.o
 snd-hda-codec-cirrus-objs :=   patch_cirrus.o
 snd-hda-codec-ca0110-objs :=   patch_ca0110.o
+snd-hda-codec-ca0132-objs :=   patch_ca0132.o
 snd-hda-codec-conexant-objs := patch_conexant.o
 snd-hda-codec-via-objs :=      patch_via.o
 snd-hda-codec-hdmi-objs :=     patch_hdmi.o hda_eld.o
@@ -42,6 +43,9 @@ endif
 ifdef CONFIG_SND_HDA_CODEC_CA0110
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
 endif
+ifdef CONFIG_SND_HDA_CODEC_CA0132
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0132.o
+endif
 ifdef CONFIG_SND_HDA_CODEC_CONEXANT
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
 endif
index a2388fc23e391b51a6528ed317b66d78e2a00275..26c420de91c3c3eeb88777f847ae962ba984df40 100644 (file)
@@ -311,35 +311,35 @@ EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
 static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
                                hda_nid_t *conn_list, int max_conns);
 static bool add_conn_list(struct snd_array *array, hda_nid_t nid);
-static int copy_conn_list(hda_nid_t nid, hda_nid_t *dst, int max_dst,
-                         hda_nid_t *src, int len);
 
 /**
  * snd_hda_get_connections - get connection list
  * @codec: the HDA codec
  * @nid: NID to parse
- * @conn_list: connection list array
- * @max_conns: max. number of connections to store
+ * @listp: the pointer to store NID list
  *
  * Parses the connection list of the given widget and stores the list
  * of NIDs.
  *
  * Returns the number of connections, or a negative error code.
  */
-int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
-                            hda_nid_t *conn_list, int max_conns)
+int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
+                         const hda_nid_t **listp)
 {
        struct snd_array *array = &codec->conn_lists;
        int i, len, old_used;
        hda_nid_t list[HDA_MAX_CONNECTIONS];
+       hda_nid_t *p;
 
        /* look up the cached results */
        for (i = 0; i < array->used; ) {
-               hda_nid_t *p = snd_array_elem(array, i);
+               p = snd_array_elem(array, i);
                len = p[1];
-               if (nid == *p)
-                       return copy_conn_list(nid, conn_list, max_conns,
-                                             p + 2, len);
+               if (nid == *p) {
+                       if (listp)
+                               *listp = p + 2;
+                       return len;
+               }
                i += len + 2;
        }
 
@@ -355,12 +355,46 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
                if (!add_conn_list(array, list[i]))
                        goto error_add;
 
-       return copy_conn_list(nid, conn_list, max_conns, list, len);
+       p = snd_array_elem(array, old_used);
+       if (listp)
+               *listp = p + 2;
+       return len;
                
  error_add:
        array->used = old_used;
        return -ENOMEM;
 }
+EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
+
+/**
+ * snd_hda_get_connections - copy connection list
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @conn_list: connection list array
+ * @max_conns: max. number of connections to store
+ *
+ * Parses the connection list of the given widget and stores the list
+ * of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
+ */
+int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
+                            hda_nid_t *conn_list, int max_conns)
+{
+       const hda_nid_t *list;
+       int len = snd_hda_get_conn_list(codec, nid, &list);
+
+       if (len <= 0)
+               return len;
+       if (len > max_conns) {
+               snd_printk(KERN_ERR "hda_codec: "
+                          "Too many connections %d for NID 0x%x\n",
+                          len, nid);
+               return -EINVAL;
+       }
+       memcpy(conn_list, list, len * sizeof(hda_nid_t));
+       return len;
+}
 EXPORT_SYMBOL_HDA(snd_hda_get_connections);
 
 static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
@@ -471,19 +505,6 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid)
        return true;
 }
 
-static int copy_conn_list(hda_nid_t nid, hda_nid_t *dst, int max_dst,
-                         hda_nid_t *src, int len)
-{
-       if (len > max_dst) {
-               snd_printk(KERN_ERR "hda_codec: "
-                          "Too many connections %d for NID 0x%x\n",
-                          len, nid);
-               return -EINVAL;
-       }
-       memcpy(dst, src, len * sizeof(hda_nid_t));
-       return len;
-}
-
 /**
  * snd_hda_queue_unsol_event - add an unsolicited event to queue
  * @bus: the BUS
@@ -4590,7 +4611,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                unsigned int wid_caps = get_wcaps(codec, nid);
                unsigned int wid_type = get_wcaps_type(wid_caps);
                unsigned int def_conf;
-               short assoc, loc;
+               short assoc, loc, conn, dev;
 
                /* read all default configuration for pin complex */
                if (wid_type != AC_WID_PIN)
@@ -4600,10 +4621,19 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                        continue;
 
                def_conf = snd_hda_codec_get_pincfg(codec, nid);
-               if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+               conn = get_defcfg_connect(def_conf);
+               if (conn == AC_JACK_PORT_NONE)
                        continue;
                loc = get_defcfg_location(def_conf);
-               switch (get_defcfg_device(def_conf)) {
+               dev = get_defcfg_device(def_conf);
+
+               /* workaround for buggy BIOS setups */
+               if (dev == AC_JACK_LINE_OUT) {
+                       if (conn == AC_JACK_PORT_FIXED)
+                               dev = AC_JACK_SPEAKER;
+               }
+
+               switch (dev) {
                case AC_JACK_LINE_OUT:
                        seq = get_defcfg_sequence(def_conf);
                        assoc = get_defcfg_association(def_conf);
index 070efac7e20710fb94a2424b277538ff1c04eb2e..c71cd7fb6d11c8924dff64981d97c33ebbd869ee 100644 (file)
@@ -903,6 +903,8 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
                          hda_nid_t *start_id);
 int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
                            hda_nid_t *conn_list, int max_conns);
+int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
+                         const hda_nid_t **listp);
 int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
                                u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
 
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
new file mode 100644 (file)
index 0000000..d9a2254
--- /dev/null
@@ -0,0 +1,1097 @@
+/*
+ * HD audio interface patch for Creative CA0132 chip
+ *
+ * Copyright (c) 2011, Creative Technology Ltd.
+ *
+ * Based on patch_ca0110.c
+ * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  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 <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+#define WIDGET_CHIP_CTRL      0x15
+#define WIDGET_DSP_CTRL       0x16
+
+#define WUH_MEM_CONNID        10
+#define DSP_MEM_CONNID        16
+
+enum hda_cmd_vendor_io {
+       /* for DspIO node */
+       VENDOR_DSPIO_SCP_WRITE_DATA_LOW      = 0x000,
+       VENDOR_DSPIO_SCP_WRITE_DATA_HIGH     = 0x100,
+
+       VENDOR_DSPIO_STATUS                  = 0xF01,
+       VENDOR_DSPIO_SCP_POST_READ_DATA      = 0x702,
+       VENDOR_DSPIO_SCP_READ_DATA           = 0xF02,
+       VENDOR_DSPIO_DSP_INIT                = 0x703,
+       VENDOR_DSPIO_SCP_POST_COUNT_QUERY    = 0x704,
+       VENDOR_DSPIO_SCP_READ_COUNT          = 0xF04,
+
+       /* for ChipIO node */
+       VENDOR_CHIPIO_ADDRESS_LOW            = 0x000,
+       VENDOR_CHIPIO_ADDRESS_HIGH           = 0x100,
+       VENDOR_CHIPIO_STREAM_FORMAT          = 0x200,
+       VENDOR_CHIPIO_DATA_LOW               = 0x300,
+       VENDOR_CHIPIO_DATA_HIGH              = 0x400,
+
+       VENDOR_CHIPIO_GET_PARAMETER          = 0xF00,
+       VENDOR_CHIPIO_STATUS                 = 0xF01,
+       VENDOR_CHIPIO_HIC_POST_READ          = 0x702,
+       VENDOR_CHIPIO_HIC_READ_DATA          = 0xF03,
+
+       VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE   = 0x70A,
+
+       VENDOR_CHIPIO_PLL_PMU_WRITE          = 0x70C,
+       VENDOR_CHIPIO_PLL_PMU_READ           = 0xF0C,
+       VENDOR_CHIPIO_8051_ADDRESS_LOW       = 0x70D,
+       VENDOR_CHIPIO_8051_ADDRESS_HIGH      = 0x70E,
+       VENDOR_CHIPIO_FLAG_SET               = 0x70F,
+       VENDOR_CHIPIO_FLAGS_GET              = 0xF0F,
+       VENDOR_CHIPIO_PARAMETER_SET          = 0x710,
+       VENDOR_CHIPIO_PARAMETER_GET          = 0xF10,
+
+       VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET  = 0x711,
+       VENDOR_CHIPIO_PORT_ALLOC_SET         = 0x712,
+       VENDOR_CHIPIO_PORT_ALLOC_GET         = 0xF12,
+       VENDOR_CHIPIO_PORT_FREE_SET          = 0x713,
+
+       VENDOR_CHIPIO_PARAMETER_EX_ID_GET    = 0xF17,
+       VENDOR_CHIPIO_PARAMETER_EX_ID_SET    = 0x717,
+       VENDOR_CHIPIO_PARAMETER_EX_VALUE_GET = 0xF18,
+       VENDOR_CHIPIO_PARAMETER_EX_VALUE_SET = 0x718
+};
+
+/*
+ *  Control flag IDs
+ */
+enum control_flag_id {
+       /* Connection manager stream setup is bypassed/enabled */
+       CONTROL_FLAG_C_MGR                  = 0,
+       /* DSP DMA is bypassed/enabled */
+       CONTROL_FLAG_DMA                    = 1,
+       /* 8051 'idle' mode is disabled/enabled */
+       CONTROL_FLAG_IDLE_ENABLE            = 2,
+       /* Tracker for the SPDIF-in path is bypassed/enabled */
+       CONTROL_FLAG_TRACKER                = 3,
+       /* DigitalOut to Spdif2Out connection is disabled/enabled */
+       CONTROL_FLAG_SPDIF2OUT              = 4,
+       /* Digital Microphone is disabled/enabled */
+       CONTROL_FLAG_DMIC                   = 5,
+       /* ADC_B rate is 48 kHz/96 kHz */
+       CONTROL_FLAG_ADC_B_96KHZ            = 6,
+       /* ADC_C rate is 48 kHz/96 kHz */
+       CONTROL_FLAG_ADC_C_96KHZ            = 7,
+       /* DAC rate is 48 kHz/96 kHz (affects all DACs) */
+       CONTROL_FLAG_DAC_96KHZ              = 8,
+       /* DSP rate is 48 kHz/96 kHz */
+       CONTROL_FLAG_DSP_96KHZ              = 9,
+       /* SRC clock is 98 MHz/196 MHz (196 MHz forces rate to 96 KHz) */
+       CONTROL_FLAG_SRC_CLOCK_196MHZ       = 10,
+       /* SRC rate is 48 kHz/96 kHz (48 kHz disabled when clock is 196 MHz) */
+       CONTROL_FLAG_SRC_RATE_96KHZ         = 11,
+       /* Decode Loop (DSP->SRC->DSP) is disabled/enabled */
+       CONTROL_FLAG_DECODE_LOOP            = 12,
+       /* De-emphasis filter on DAC-1 disabled/enabled */
+       CONTROL_FLAG_DAC1_DEEMPHASIS        = 13,
+       /* De-emphasis filter on DAC-2 disabled/enabled */
+       CONTROL_FLAG_DAC2_DEEMPHASIS        = 14,
+       /* De-emphasis filter on DAC-3 disabled/enabled */
+       CONTROL_FLAG_DAC3_DEEMPHASIS        = 15,
+       /* High-pass filter on ADC_B disabled/enabled */
+       CONTROL_FLAG_ADC_B_HIGH_PASS        = 16,
+       /* High-pass filter on ADC_C disabled/enabled */
+       CONTROL_FLAG_ADC_C_HIGH_PASS        = 17,
+       /* Common mode on Port_A disabled/enabled */
+       CONTROL_FLAG_PORT_A_COMMON_MODE     = 18,
+       /* Common mode on Port_D disabled/enabled */
+       CONTROL_FLAG_PORT_D_COMMON_MODE     = 19,
+       /* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */
+       CONTROL_FLAG_PORT_A_10KOHM_LOAD     = 20,
+       /* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */
+       CONTROL_FLAG_PORT_D_10K0HM_LOAD     = 21,
+       /* ASI rate is 48kHz/96kHz */
+       CONTROL_FLAG_ASI_96KHZ              = 22,
+       /* DAC power settings able to control attached ports no/yes */
+       CONTROL_FLAG_DACS_CONTROL_PORTS     = 23,
+       /* Clock Stop OK reporting is disabled/enabled */
+       CONTROL_FLAG_CONTROL_STOP_OK_ENABLE = 24,
+       /* Number of control flags */
+       CONTROL_FLAGS_MAX = (CONTROL_FLAG_CONTROL_STOP_OK_ENABLE+1)
+};
+
+/*
+ * Control parameter IDs
+ */
+enum control_parameter_id {
+       /* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */
+       CONTROL_PARAM_SPDIF1_SOURCE            = 2,
+
+       /* Stream Control */
+
+       /* Select stream with the given ID */
+       CONTROL_PARAM_STREAM_ID                = 24,
+       /* Source connection point for the selected stream */
+       CONTROL_PARAM_STREAM_SOURCE_CONN_POINT = 25,
+       /* Destination connection point for the selected stream */
+       CONTROL_PARAM_STREAM_DEST_CONN_POINT   = 26,
+       /* Number of audio channels in the selected stream */
+       CONTROL_PARAM_STREAMS_CHANNELS         = 27,
+       /*Enable control for the selected stream */
+       CONTROL_PARAM_STREAM_CONTROL           = 28,
+
+       /* Connection Point Control */
+
+       /* Select connection point with the given ID */
+       CONTROL_PARAM_CONN_POINT_ID            = 29,
+       /* Connection point sample rate */
+       CONTROL_PARAM_CONN_POINT_SAMPLE_RATE   = 30,
+
+       /* Node Control */
+
+       /* Select HDA node with the given ID */
+       CONTROL_PARAM_NODE_ID                  = 31
+};
+
+/*
+ *  Dsp Io Status codes
+ */
+enum hda_vendor_status_dspio {
+       /* Success */
+       VENDOR_STATUS_DSPIO_OK                       = 0x00,
+       /* Busy, unable to accept new command, the host must retry */
+       VENDOR_STATUS_DSPIO_BUSY                     = 0x01,
+       /* SCP command queue is full */
+       VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL   = 0x02,
+       /* SCP response queue is empty */
+       VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY = 0x03
+};
+
+/*
+ *  Chip Io Status codes
+ */
+enum hda_vendor_status_chipio {
+       /* Success */
+       VENDOR_STATUS_CHIPIO_OK   = 0x00,
+       /* Busy, unable to accept new command, the host must retry */
+       VENDOR_STATUS_CHIPIO_BUSY = 0x01
+};
+
+/*
+ *  CA0132 sample rate
+ */
+enum ca0132_sample_rate {
+       SR_6_000        = 0x00,
+       SR_8_000        = 0x01,
+       SR_9_600        = 0x02,
+       SR_11_025       = 0x03,
+       SR_16_000       = 0x04,
+       SR_22_050       = 0x05,
+       SR_24_000       = 0x06,
+       SR_32_000       = 0x07,
+       SR_44_100       = 0x08,
+       SR_48_000       = 0x09,
+       SR_88_200       = 0x0A,
+       SR_96_000       = 0x0B,
+       SR_144_000      = 0x0C,
+       SR_176_400      = 0x0D,
+       SR_192_000      = 0x0E,
+       SR_384_000      = 0x0F,
+
+       SR_COUNT        = 0x10,
+
+       SR_RATE_UNKNOWN = 0x1F
+};
+
+/*
+ *  Scp Helper function
+ */
+enum get_set {
+       IS_SET = 0,
+       IS_GET = 1,
+};
+
+/*
+ * Duplicated from ca0110 codec
+ */
+
+static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
+{
+       if (pin) {
+               snd_hda_codec_write(codec, pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+               if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+                       snd_hda_codec_write(codec, pin, 0,
+                                           AC_VERB_SET_AMP_GAIN_MUTE,
+                                           AMP_OUT_UNMUTE);
+       }
+       if (dac)
+               snd_hda_codec_write(codec, dac, 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
+}
+
+static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
+{
+       if (pin) {
+               snd_hda_codec_write(codec, pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   PIN_VREF80);
+               if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
+                       snd_hda_codec_write(codec, pin, 0,
+                                           AC_VERB_SET_AMP_GAIN_MUTE,
+                                           AMP_IN_UNMUTE(0));
+       }
+       if (adc)
+               snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_IN_UNMUTE(0));
+}
+
+static char *dirstr[2] = { "Playback", "Capture" };
+
+static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
+                      int chan, int dir)
+{
+       char namestr[44];
+       int type = dir ? HDA_INPUT : HDA_OUTPUT;
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
+       sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+       return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
+                      int chan, int dir)
+{
+       char namestr[44];
+       int type = dir ? HDA_INPUT : HDA_OUTPUT;
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
+       sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
+       return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
+#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0)
+#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1)
+#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1)
+#define add_mono_switch(codec, nid, pfx, chan) \
+       _add_switch(codec, nid, pfx, chan, 0)
+#define add_mono_volume(codec, nid, pfx, chan) \
+       _add_volume(codec, nid, pfx, chan, 0)
+#define add_in_mono_switch(codec, nid, pfx, chan) \
+       _add_switch(codec, nid, pfx, chan, 1)
+#define add_in_mono_volume(codec, nid, pfx, chan) \
+       _add_volume(codec, nid, pfx, chan, 1)
+
+
+/*
+ * CA0132 specific
+ */
+
+struct ca0132_spec {
+       struct auto_pin_cfg autocfg;
+       struct hda_multi_out multiout;
+       hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
+       hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
+       hda_nid_t hp_dac;
+       hda_nid_t input_pins[AUTO_PIN_LAST];
+       hda_nid_t adcs[AUTO_PIN_LAST];
+       hda_nid_t dig_out;
+       hda_nid_t dig_in;
+       unsigned int num_inputs;
+       long curr_hp_switch;
+       long curr_hp_volume[2];
+       long curr_speaker_switch;
+       struct mutex chipio_mutex;
+       const char *input_labels[AUTO_PIN_LAST];
+       struct hda_pcm pcm_rec[2]; /* PCM information */
+};
+
+/* Chip access helper function */
+static int chipio_send(struct hda_codec *codec,
+                      unsigned int reg,
+                      unsigned int data)
+{
+       unsigned int res;
+       int retry = 50;
+
+       /* send bits of data specified by reg */
+       do {
+               res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+                                        reg, data);
+               if (res == VENDOR_STATUS_CHIPIO_OK)
+                       return 0;
+       } while (--retry);
+       return -EIO;
+}
+
+/*
+ * Write chip address through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_write_address(struct hda_codec *codec,
+                               unsigned int chip_addx)
+{
+       int res;
+
+       /* send low 16 bits of the address */
+       res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
+                         chip_addx & 0xffff);
+
+       if (res != -EIO) {
+               /* send high 16 bits of the address */
+               res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
+                                 chip_addx >> 16);
+       }
+
+       return res;
+}
+
+/*
+ * Write data through the vendor widget -- NOT protected by the Mutex!
+ */
+
+static int chipio_write_data(struct hda_codec *codec, unsigned int data)
+{
+       int res;
+
+       /* send low 16 bits of the data */
+       res = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff);
+
+       if (res != -EIO) {
+               /* send high 16 bits of the data */
+               res = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH,
+                                 data >> 16);
+       }
+
+       return res;
+}
+
+/*
+ * Read data through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_read_data(struct hda_codec *codec, unsigned int *data)
+{
+       int res;
+
+       /* post read */
+       res = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0);
+
+       if (res != -EIO) {
+               /* read status */
+               res = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+       }
+
+       if (res != -EIO) {
+               /* read data */
+               *data = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+                                          VENDOR_CHIPIO_HIC_READ_DATA,
+                                          0);
+       }
+
+       return res;
+}
+
+/*
+ * Write given value to the given address through the chip I/O widget.
+ * protected by the Mutex
+ */
+static int chipio_write(struct hda_codec *codec,
+               unsigned int chip_addx, const unsigned int data)
+{
+       struct ca0132_spec *spec = codec->spec;
+       int err;
+
+       mutex_lock(&spec->chipio_mutex);
+
+       /* write the address, and if successful proceed to write data */
+       err = chipio_write_address(codec, chip_addx);
+       if (err < 0)
+               goto exit;
+
+       err = chipio_write_data(codec, data);
+       if (err < 0)
+               goto exit;
+
+exit:
+       mutex_unlock(&spec->chipio_mutex);
+       return err;
+}
+
+/*
+ * Read the given address through the chip I/O widget
+ * protected by the Mutex
+ */
+static int chipio_read(struct hda_codec *codec,
+               unsigned int chip_addx, unsigned int *data)
+{
+       struct ca0132_spec *spec = codec->spec;
+       int err;
+
+       mutex_lock(&spec->chipio_mutex);
+
+       /* write the address, and if successful proceed to write data */
+       err = chipio_write_address(codec, chip_addx);
+       if (err < 0)
+               goto exit;
+
+       err = chipio_read_data(codec, data);
+       if (err < 0)
+               goto exit;
+
+exit:
+       mutex_unlock(&spec->chipio_mutex);
+       return err;
+}
+
+/*
+ * PCM stuffs
+ */
+static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+                                u32 stream_tag,
+                                int channel_id, int format)
+{
+       unsigned int oldval, newval;
+
+       if (!nid)
+               return;
+
+       snd_printdd("ca0132_setup_stream: "
+               "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
+               nid, stream_tag, channel_id, format);
+
+       /* update the format-id if changed */
+       oldval = snd_hda_codec_read(codec, nid, 0,
+                                   AC_VERB_GET_STREAM_FORMAT,
+                                   0);
+       if (oldval != format) {
+               msleep(20);
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_STREAM_FORMAT,
+                                   format);
+       }
+
+       oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+       newval = (stream_tag << 4) | channel_id;
+       if (oldval != newval) {
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_CHANNEL_STREAMID,
+                                   newval);
+       }
+}
+
+static void ca0132_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
+{
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+}
+
+/*
+ * PCM callbacks
+ */
+static int ca0132_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 ca0132_spec *spec = codec->spec;
+
+       ca0132_setup_stream(codec, spec->dacs[0], stream_tag, 0, format);
+
+       return 0;
+}
+
+static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                       struct hda_codec *codec,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       ca0132_cleanup_stream(codec, spec->dacs[0]);
+
+       return 0;
+}
+
+/*
+ * Digital out
+ */
+static int ca0132_dig_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 ca0132_spec *spec = codec->spec;
+
+       ca0132_setup_stream(codec, spec->dig_out, stream_tag, 0, format);
+
+       return 0;
+}
+
+static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                       struct hda_codec *codec,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       ca0132_cleanup_stream(codec, spec->dig_out);
+
+       return 0;
+}
+
+/*
+ * Analog capture
+ */
+static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+                       struct hda_codec *codec,
+                       unsigned int stream_tag,
+                       unsigned int format,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       ca0132_setup_stream(codec, spec->adcs[substream->number],
+                            stream_tag, 0, format);
+
+       return 0;
+}
+
+static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                       struct hda_codec *codec,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       ca0132_cleanup_stream(codec, spec->adcs[substream->number]);
+
+       return 0;
+}
+
+/*
+ * Digital capture
+ */
+static int ca0132_dig_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+                       struct hda_codec *codec,
+                       unsigned int stream_tag,
+                       unsigned int format,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       ca0132_setup_stream(codec, spec->dig_in, stream_tag, 0, format);
+
+       return 0;
+}
+
+static int ca0132_dig_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                       struct hda_codec *codec,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       ca0132_cleanup_stream(codec, spec->dig_in);
+
+       return 0;
+}
+
+/*
+ */
+static struct hda_pcm_stream ca0132_pcm_analog_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .ops = {
+               .prepare = ca0132_playback_pcm_prepare,
+               .cleanup = ca0132_playback_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream ca0132_pcm_analog_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .ops = {
+               .prepare = ca0132_capture_pcm_prepare,
+               .cleanup = ca0132_capture_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream ca0132_pcm_digital_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .ops = {
+               .prepare = ca0132_dig_playback_pcm_prepare,
+               .cleanup = ca0132_dig_playback_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream ca0132_pcm_digital_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .ops = {
+               .prepare = ca0132_dig_capture_pcm_prepare,
+               .cleanup = ca0132_dig_capture_pcm_cleanup
+       },
+};
+
+static int ca0132_build_pcms(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec = codec->spec;
+       struct hda_pcm *info = spec->pcm_rec;
+
+       codec->pcm_info = info;
+       codec->num_pcms = 0;
+
+       info->name = "CA0132 Analog";
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback;
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+               spec->multiout.max_channels;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
+       codec->num_pcms++;
+
+       if (!spec->dig_out && !spec->dig_in)
+               return 0;
+
+       info++;
+       info->name = "CA0132 Digital";
+       info->pcm_type = HDA_PCM_TYPE_SPDIF;
+       if (spec->dig_out) {
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+                       ca0132_pcm_digital_playback;
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
+       }
+       if (spec->dig_in) {
+               info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+                       ca0132_pcm_digital_capture;
+               info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+       }
+       codec->num_pcms++;
+
+       return 0;
+}
+
+#define REG_CODEC_MUTE         0x18b014
+#define REG_CODEC_HP_VOL_L     0x18b070
+#define REG_CODEC_HP_VOL_R     0x18b074
+
+static int ca0132_hp_switch_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ca0132_spec *spec = codec->spec;
+       long *valp = ucontrol->value.integer.value;
+
+       *valp = spec->curr_hp_switch;
+       return 0;
+}
+
+static int ca0132_hp_switch_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ca0132_spec *spec = codec->spec;
+       long *valp = ucontrol->value.integer.value;
+       unsigned int data;
+       int err;
+
+       /* any change? */
+       if (spec->curr_hp_switch == *valp)
+               return 0;
+
+       snd_hda_power_up(codec);
+
+       err = chipio_read(codec, REG_CODEC_MUTE, &data);
+       if (err < 0)
+               return err;
+
+       /* *valp 0 is mute, 1 is unmute */
+       data = (data & 0x7f) | (*valp ? 0 : 0x80);
+       chipio_write(codec, REG_CODEC_MUTE, data);
+       if (err < 0)
+               return err;
+
+       spec->curr_hp_switch = *valp;
+
+       snd_hda_power_down(codec);
+       return 1;
+}
+
+static int ca0132_speaker_switch_get(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ca0132_spec *spec = codec->spec;
+       long *valp = ucontrol->value.integer.value;
+
+       *valp = spec->curr_speaker_switch;
+       return 0;
+}
+
+static int ca0132_speaker_switch_put(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ca0132_spec *spec = codec->spec;
+       long *valp = ucontrol->value.integer.value;
+       unsigned int data;
+       int err;
+
+       /* any change? */
+       if (spec->curr_speaker_switch == *valp)
+               return 0;
+
+       snd_hda_power_up(codec);
+
+       err = chipio_read(codec, REG_CODEC_MUTE, &data);
+       if (err < 0)
+               return err;
+
+       /* *valp 0 is mute, 1 is unmute */
+       data = (data & 0xef) | (*valp ? 0 : 0x10);
+       chipio_write(codec, REG_CODEC_MUTE, data);
+       if (err < 0)
+               return err;
+
+       spec->curr_speaker_switch = *valp;
+
+       snd_hda_power_down(codec);
+       return 1;
+}
+
+static int ca0132_hp_volume_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ca0132_spec *spec = codec->spec;
+       long *valp = ucontrol->value.integer.value;
+
+       *valp++ = spec->curr_hp_volume[0];
+       *valp = spec->curr_hp_volume[1];
+       return 0;
+}
+
+static int ca0132_hp_volume_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ca0132_spec *spec = codec->spec;
+       long *valp = ucontrol->value.integer.value;
+       long left_vol, right_vol;
+       unsigned int data;
+       int val;
+       int err;
+
+       left_vol = *valp++;
+       right_vol = *valp;
+
+       /* any change? */
+       if ((spec->curr_hp_volume[0] == left_vol) &&
+               (spec->curr_hp_volume[1] == right_vol))
+               return 0;
+
+       snd_hda_power_up(codec);
+
+       err = chipio_read(codec, REG_CODEC_HP_VOL_L, &data);
+       if (err < 0)
+               return err;
+
+       val = 31 - left_vol;
+       data = (data & 0xe0) | val;
+       chipio_write(codec, REG_CODEC_HP_VOL_L, data);
+       if (err < 0)
+               return err;
+
+       val = 31 - right_vol;
+       data = (data & 0xe0) | val;
+       chipio_write(codec, REG_CODEC_HP_VOL_R, data);
+       if (err < 0)
+               return err;
+
+       spec->curr_hp_volume[0] = left_vol;
+       spec->curr_hp_volume[1] = right_vol;
+
+       snd_hda_power_down(codec);
+       return 1;
+}
+
+static int add_hp_switch(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_MUTE_MONO("Headphone Playback Switch",
+                                    nid, 1, 0, HDA_OUTPUT);
+       knew.get = ca0132_hp_switch_get;
+       knew.put = ca0132_hp_switch_put;
+       return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int add_hp_volume(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_VOLUME_MONO("Headphone Playback Volume",
+                                      nid, 3, 0, HDA_OUTPUT);
+       knew.get = ca0132_hp_volume_get;
+       knew.put = ca0132_hp_volume_put;
+       return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int add_speaker_switch(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_MUTE_MONO("Speaker Playback Switch",
+                                    nid, 1, 0, HDA_OUTPUT);
+       knew.get = ca0132_speaker_switch_get;
+       knew.put = ca0132_speaker_switch_put;
+       return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static void ca0132_fix_hp_caps(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       unsigned int caps;
+
+       /* set mute-capable, 1db step, 32 steps, ofs 6 */
+       caps = 0x80031f06;
+       snd_hda_override_amp_caps(codec, cfg->hp_pins[0], HDA_OUTPUT, caps);
+}
+
+static int ca0132_build_controls(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i, err;
+
+       if (spec->multiout.num_dacs) {
+               err = add_speaker_switch(codec, spec->out_pins[0]);
+               if (err < 0)
+                       return err;
+       }
+
+       if (cfg->hp_outs) {
+               ca0132_fix_hp_caps(codec);
+               err = add_hp_switch(codec, cfg->hp_pins[0]);
+               if (err < 0)
+                       return err;
+               err = add_hp_volume(codec, cfg->hp_pins[0]);
+               if (err < 0)
+                       return err;
+       }
+
+       for (i = 0; i < spec->num_inputs; i++) {
+               const char *label = spec->input_labels[i];
+
+               err = add_in_switch(codec, spec->adcs[i], label);
+               if (err < 0)
+                       return err;
+               err = add_in_volume(codec, spec->adcs[i], label);
+               if (err < 0)
+                       return err;
+               if (cfg->inputs[i].type == AUTO_PIN_MIC) {
+                       /* add Mic-Boost */
+                       err = add_in_mono_volume(codec, spec->input_pins[i],
+                                                "Mic Boost", 1);
+                       if (err < 0)
+                               return err;
+               }
+       }
+
+       if (spec->dig_out) {
+               err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
+                                                   spec->dig_out);
+               if (err < 0)
+                       return err;
+               err = add_out_volume(codec, spec->dig_out, "IEC958");
+               if (err < 0)
+                       return err;
+       }
+
+       if (spec->dig_in) {
+               err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+               if (err < 0)
+                       return err;
+               err = add_in_volume(codec, spec->dig_in, "IEC958");
+       }
+       return 0;
+}
+
+
+static void ca0132_set_ct_ext(struct hda_codec *codec, int enable)
+{
+       /* Set Creative extension */
+       snd_printdd("SET CREATIVE EXTENSION\n");
+       snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+                           VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE,
+                           enable);
+       msleep(20);
+}
+
+
+static void ca0132_config(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+
+       /* line-outs */
+       cfg->line_outs = 1;
+       cfg->line_out_pins[0] = 0x0b; /* front */
+       cfg->line_out_type = AUTO_PIN_LINE_OUT;
+
+       spec->dacs[0] = 0x02;
+       spec->out_pins[0] = 0x0b;
+       spec->multiout.dac_nids = spec->dacs;
+       spec->multiout.num_dacs = 1;
+       spec->multiout.max_channels = 2;
+
+       /* headphone */
+       cfg->hp_outs = 1;
+       cfg->hp_pins[0] = 0x0f;
+
+       spec->hp_dac = 0;
+       spec->multiout.hp_nid = 0;
+
+       /* inputs */
+       cfg->num_inputs = 2;  /* Mic-in and line-in */
+       cfg->inputs[0].pin = 0x12;
+       cfg->inputs[0].type = AUTO_PIN_MIC;
+       cfg->inputs[1].pin = 0x11;
+       cfg->inputs[1].type = AUTO_PIN_LINE_IN;
+
+       /* Mic-in */
+       spec->input_pins[0] = 0x12;
+       spec->input_labels[0] = "Mic-In";
+       spec->adcs[0] = 0x07;
+
+       /* Line-In */
+       spec->input_pins[1] = 0x11;
+       spec->input_labels[1] = "Line-In";
+       spec->adcs[1] = 0x08;
+       spec->num_inputs = 2;
+}
+
+static void ca0132_init_chip(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       mutex_init(&spec->chipio_mutex);
+}
+
+static void ca0132_exit_chip(struct hda_codec *codec)
+{
+       /* put any chip cleanup stuffs here. */
+}
+
+static int ca0132_init(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
+
+       for (i = 0; i < spec->multiout.num_dacs; i++) {
+               init_output(codec, spec->out_pins[i],
+                           spec->multiout.dac_nids[i]);
+       }
+       init_output(codec, cfg->hp_pins[0], spec->hp_dac);
+       init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
+
+       for (i = 0; i < spec->num_inputs; i++)
+               init_input(codec, spec->input_pins[i], spec->adcs[i]);
+
+       init_input(codec, cfg->dig_in_pin, spec->dig_in);
+
+       ca0132_set_ct_ext(codec, 1);
+
+       return 0;
+}
+
+
+static void ca0132_free(struct hda_codec *codec)
+{
+       ca0132_set_ct_ext(codec, 0);
+       ca0132_exit_chip(codec);
+       kfree(codec->spec);
+}
+
+static struct hda_codec_ops ca0132_patch_ops = {
+       .build_controls = ca0132_build_controls,
+       .build_pcms = ca0132_build_pcms,
+       .init = ca0132_init,
+       .free = ca0132_free,
+};
+
+
+
+static int patch_ca0132(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec;
+
+       snd_printdd("patch_ca0132\n");
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       codec->spec = spec;
+
+       ca0132_init_chip(codec);
+
+       ca0132_config(codec);
+
+       codec->patch_ops = ca0132_patch_ops;
+
+       return 0;
+}
+
+/*
+ * patch entries
+ */
+static struct hda_codec_preset snd_hda_preset_ca0132[] = {
+       { .id = 0x11020011, .name = "CA0132",     .patch = patch_ca0132 },
+       {} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:11020011");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Creative CA0132, CA0132 HD-audio codec");
+
+static struct hda_codec_preset_list ca0132_list = {
+       .preset = snd_hda_preset_ca0132,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_ca0132_init(void)
+{
+       return snd_hda_add_codec_preset(&ca0132_list);
+}
+
+static void __exit patch_ca0132_exit(void)
+{
+       snd_hda_delete_codec_preset(&ca0132_list);
+}
+
+module_init(patch_ca0132_init)
+module_exit(patch_ca0132_exit)
index 4adf9e23f04915f1dbfe84bdb5ab12722bb37793..5e4efb75879e6eefcd8a61b39133d332d6689b24 100644 (file)
@@ -348,6 +348,7 @@ struct alc_spec {
        const hda_nid_t *adc_nids;
        const hda_nid_t *capsrc_nids;
        hda_nid_t dig_in_nid;           /* digital-in NID; optional */
+       hda_nid_t mixer_nid;            /* analog-mixer NID */
 
        /* capture setup for dynamic dual-adc switch */
        unsigned int cur_adc_idx;
@@ -1759,6 +1760,15 @@ do_sku:
        return 0;
 }
 
+static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+       int i;
+       for (i = 0; i < nums; i++)
+               if (list[i] == nid)
+                       return true;
+       return false;
+}
+
 /* check subsystem ID and set up device-specific initialization;
  * return 1 if initialized, 0 if invalid SSID
  */
@@ -1868,9 +1878,9 @@ do_sku:
                        nid = porti;
                else
                        return 1;
-               for (i = 0; i < spec->autocfg.line_outs; i++)
-                       if (spec->autocfg.line_out_pins[i] == nid)
-                               return 1;
+               if (found_in_nid_list(nid, spec->autocfg.line_out_pins,
+                                     spec->autocfg.line_outs))
+                       return 1;
                spec->autocfg.hp_pins[0] = nid;
        }
        return 1;
@@ -2061,15 +2071,23 @@ static void alc_auto_init_digital(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        int i;
-       hda_nid_t pin;
+       hda_nid_t pin, dac;
 
        for (i = 0; i < spec->autocfg.dig_outs; i++) {
                pin = spec->autocfg.dig_out_pins[i];
-               if (pin) {
-                       snd_hda_codec_write(codec, pin, 0,
-                                           AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                           PIN_OUT);
-               }
+               if (!pin)
+                       continue;
+               snd_hda_codec_write(codec, pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+               if (!i)
+                       dac = spec->multiout.dig_out_nid;
+               else
+                       dac = spec->slave_dig_outs[i - 1];
+               if (!dac || !(get_wcaps(codec, dac) & AC_WCAP_OUT_AMP))
+                       continue;
+               snd_hda_codec_write(codec, dac, 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_OUT_UNMUTE);
        }
        pin = spec->autocfg.dig_in_pin;
        if (pin)
@@ -5321,9 +5339,10 @@ static int add_control_with_pfx(struct alc_spec *spec, int type,
 #define ALC880_PIN_CD_NID              0x1c
 
 /* fill in the dac_nids table from the parsed pin configuration */
-static int alc880_auto_fill_dac_nids(struct alc_spec *spec,
-                                    const struct auto_pin_cfg *cfg)
+static int alc880_auto_fill_dac_nids(struct hda_codec *codec)
 {
+       struct alc_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
        hda_nid_t nid;
        int assigned[4];
        int i, j;
@@ -5359,11 +5378,15 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec,
        return 0;
 }
 
-static const char *alc_get_line_out_pfx(struct alc_spec *spec,
-                                       bool can_be_master)
+static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch,
+                                       bool can_be_master, int *index)
 {
        struct auto_pin_cfg *cfg = &spec->autocfg;
+       static const char * const chname[4] = {
+               "Front", "Surround", NULL /*CLFE*/, "Side"
+       };
 
+       *index = 0;
        if (cfg->line_outs == 1 && !spec->multi_ios &&
            !cfg->hp_outs && !cfg->speaker_outs && can_be_master)
                return "Master";
@@ -5374,23 +5397,23 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec,
                        return "Speaker";
                break;
        case AUTO_PIN_HP_OUT:
+               /* for multi-io case, only the primary out */
+               if (ch && spec->multi_ios)
+                       break;
+               *index = ch;
                return "Headphone";
        default:
                if (cfg->line_outs == 1 && !spec->multi_ios)
                        return "PCM";
                break;
        }
-       return NULL;
+       return chname[ch];
 }
 
 /* add playback controls from the parsed DAC table */
 static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
                                             const struct auto_pin_cfg *cfg)
 {
-       static const char * const chname[4] = {
-               "Front", "Surround", NULL /*CLFE*/, "Side"
-       };
-       const char *pfx = alc_get_line_out_pfx(spec, false);
        hda_nid_t nid;
        int i, err, noutputs;
 
@@ -5399,10 +5422,13 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
                noutputs += spec->multi_ios;
 
        for (i = 0; i < noutputs; i++) {
+               const char *name;
+               int index;
                if (!spec->multiout.dac_nids[i])
                        continue;
                nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
-               if (!pfx && i == 2) {
+               name = alc_get_line_out_pfx(spec, i, false, &index);
+               if (!name) {
                        /* Center/LFE */
                        err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
                                              "Center",
@@ -5429,12 +5455,6 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
                        if (err < 0)
                                return err;
                } else {
-                       const char *name = pfx;
-                       int index = i;
-                       if (!name) {
-                               name = chname[i];
-                               index = 0;
-                       }
                        err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
                                                name, index,
                                          HDA_COMPOSE_AMP_VAL(nid, 3, 0,
@@ -5601,6 +5621,19 @@ static int get_pin_type(int line_out_type)
                return PIN_OUT;
 }
 
+static void alc880_auto_init_dac(struct hda_codec *codec, hda_nid_t nid)
+{
+       if (!nid)
+               return;
+       nid = alc880_idx_to_mixer(alc880_dac_to_idx(nid));
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                           AMP_OUT_ZERO);
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                           AMP_IN_MUTE(0));
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                           AMP_IN_MUTE(1));
+}
+
 static void alc880_auto_init_multi_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -5611,12 +5644,16 @@ static void alc880_auto_init_multi_out(struct hda_codec *codec)
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
                alc880_auto_set_output_and_unmute(codec, nid, pin_type, i);
        }
+       /* mute DACs */
+       for (i = 0; i < spec->multiout.num_dacs; i++)
+               alc880_auto_init_dac(codec, spec->multiout.dac_nids[i]);
 }
 
 static void alc880_auto_init_extra_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        hda_nid_t pin;
+       int i;
 
        pin = spec->autocfg.speaker_pins[0];
        if (pin) /* connect to front */
@@ -5624,6 +5661,10 @@ static void alc880_auto_init_extra_out(struct hda_codec *codec)
        pin = spec->autocfg.hp_pins[0];
        if (pin) /* connect to front */
                alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+       /* mute DACs */
+       alc880_auto_init_dac(codec, spec->multiout.hp_nid);
+       for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++)
+               alc880_auto_init_dac(codec, spec->multiout.extra_out_nid[i]);
 }
 
 static void alc880_auto_init_analog_input(struct hda_codec *codec)
@@ -5636,13 +5677,21 @@ static void alc880_auto_init_analog_input(struct hda_codec *codec)
                hda_nid_t nid = cfg->inputs[i].pin;
                if (alc_is_input_pin(codec, nid)) {
                        alc_set_input_pin(codec, nid, cfg->inputs[i].type);
-                       if (nid != ALC880_PIN_CD_NID &&
-                           (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
+                       if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
                                snd_hda_codec_write(codec, nid, 0,
                                                    AC_VERB_SET_AMP_GAIN_MUTE,
                                                    AMP_OUT_MUTE);
                }
        }
+
+       /* mute all loopback inputs */
+       if (spec->mixer_nid) {
+               int nums = snd_hda_get_conn_list(codec, spec->mixer_nid, NULL);
+               for (i = 0; i < nums; i++)
+                       snd_hda_codec_write(codec, spec->mixer_nid, 0,
+                                           AC_VERB_SET_AMP_GAIN_MUTE,
+                                           AMP_IN_MUTE(i));
+       }
 }
 
 static void alc880_auto_init_input_src(struct hda_codec *codec)
@@ -5661,10 +5710,14 @@ static void alc880_auto_init_input_src(struct hda_codec *codec)
                        snd_hda_codec_write(codec, spec->adc_nids[c], 0,
                                            AC_VERB_SET_CONNECT_SEL,
                                            imux->items[0].index);
+               snd_hda_codec_write(codec, spec->adc_nids[c], 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_IN_MUTE(0));
        }
 }
 
-static int alc_auto_add_multi_channel_mode(struct hda_codec *codec);
+static int alc_auto_add_multi_channel_mode(struct hda_codec *codec,
+                                          int (*fill_dac)(struct hda_codec *));
 
 /* parse the BIOS configuration and set up the alc_spec */
 /* return 1 if successful, 0 if the proper config is not found,
@@ -5683,10 +5736,10 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
        if (!spec->autocfg.line_outs)
                return 0; /* can't find valid BIOS pin config */
 
-       err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+       err = alc880_auto_fill_dac_nids(codec);
        if (err < 0)
                return err;
-       err = alc_auto_add_multi_channel_mode(codec);
+       err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids);
        if (err < 0)
                return err;
        err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
@@ -5712,8 +5765,6 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       add_verb(spec, alc880_volume_init_verbs);
-
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
@@ -5983,6 +6034,8 @@ static int patch_alc880(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       spec->mixer_nid = 0x0b;
+
        board_config = snd_hda_check_board_config(codec, ALC880_MODEL_LAST,
                                                  alc880_models,
                                                  alc880_cfg_tbl);
@@ -7183,12 +7236,8 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
        nid = cfg->line_out_pins[0];
        if (nid) {
                const char *pfx;
-               if (!cfg->speaker_pins[0] && !cfg->hp_pins[0])
-                       pfx = "Master";
-               else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
-                       pfx = "Speaker";
-               else
-                       pfx = "Front";
+               int index;
+               pfx = alc_get_line_out_pfx(spec, 0, true, &index);
                err = alc260_add_playback_controls(spec, nid, pfx, &vols);
                if (err < 0)
                        return err;
@@ -7231,10 +7280,24 @@ static void alc260_auto_set_output_and_unmute(struct hda_codec *codec,
        }
 }
 
+static void alc260_auto_init_dac(struct hda_codec *codec, hda_nid_t nid)
+{
+       if (!nid)
+               return;
+       nid += 0x06; /* DAC -> MIX */
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                           AMP_OUT_ZERO);
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                           AMP_IN_MUTE(0));
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                           AMP_IN_MUTE(1));
+}
+
 static void alc260_auto_init_multi_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        hda_nid_t nid;
+       int i;
 
        nid = spec->autocfg.line_out_pins[0];
        if (nid) {
@@ -7249,74 +7312,17 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)
        nid = spec->autocfg.hp_pins[0];
        if (nid)
                alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0);
-}
 
-#define ALC260_PIN_CD_NID              0x16
-static void alc260_auto_init_analog_input(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       struct auto_pin_cfg *cfg = &spec->autocfg;
-       int i;
-
-       for (i = 0; i < cfg->num_inputs; i++) {
-               hda_nid_t nid = cfg->inputs[i].pin;
-               if (nid >= 0x12) {
-                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
-                       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);
-               }
-       }
+       /* mute DACs */
+       for (i = 0; i < spec->multiout.num_dacs; i++)
+               alc260_auto_init_dac(codec, spec->multiout.dac_nids[i]);
+       alc260_auto_init_dac(codec, spec->multiout.extra_out_nid[0]);
+       alc260_auto_init_dac(codec, spec->multiout.hp_nid);
 }
 
+#define alc260_auto_init_analog_input  alc880_auto_init_analog_input
 #define alc260_auto_init_input_src     alc880_auto_init_input_src
 
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb alc260_volume_init_verbs[] = {
-       /*
-        * Unmute ADC0-1 and set the default input to mic-in
-        */
-       {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
-       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        * Note: PASD motherboards uses the Line In 2 as the input for
-        * front panel mic (mic 2)
-        */
-       /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-       /* mute analog inputs */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
-       /*
-        * Set up output mixers (0x08 - 0x0a)
-        */
-       /* set vol=0 to output mixers */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       /* set up input amps for analog loopback */
-       /* Amp Indices: DAC = 0, mixer = 1 */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-       { }
-};
-
 static int alc260_parse_auto_config(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -7343,8 +7349,6 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       add_verb(spec, alc260_volume_init_verbs);
-
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
@@ -7591,6 +7595,8 @@ static int patch_alc260(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       spec->mixer_nid = 0x07;
+
        board_config = snd_hda_check_board_config(codec, ALC260_MODEL_LAST,
                                                  alc260_models,
                                                  alc260_cfg_tbl);
@@ -9038,48 +9044,6 @@ static void alc885_imac24_init_hook(struct hda_codec *codec)
        alc_hp_automute(codec);
 }
 
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb alc883_auto_init_verbs[] = {
-       /*
-        * Unmute ADC0-2 and set the default input to mic-in
-        */
-       {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-       /*
-        * Set up output mixers (0x0c - 0x0f)
-        */
-       /* set vol=0 to output mixers */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       /* set up input amps for analog loopback */
-       /* Amp Indices: DAC = 0, mixer = 1 */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {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)},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-       /* FIXME: use matrix-type input source selection */
-       /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
-       /* Input mixer2 */
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       /* Input mixer3 */
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       { }
-};
-
 /* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */
 static const struct hda_verb alc889A_mb31_ch2_init[] = {
        {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},             /* HP as front */
@@ -11039,6 +11003,9 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
        /* set as output */
        alc_set_pin_output(codec, nid, pin_type);
 
+       if (snd_hda_get_conn_list(codec, nid, NULL) < 2)
+               return;
+
        if (dac == 0x25)
                idx = 4;
        else if (dac >= 0x02 && dac <= 0x05)
@@ -11048,6 +11015,8 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
 }
 
+#define alc882_auto_init_dac   alc880_auto_init_dac
+
 static void alc882_auto_init_multi_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -11060,6 +11029,9 @@ static void alc882_auto_init_multi_out(struct hda_codec *codec)
                        alc882_auto_set_output_and_unmute(codec, nid, pin_type,
                                        spec->multiout.dac_nids[i]);
        }
+       /* mute DACs */
+       for (i = 0; i < spec->multiout.num_dacs; i++)
+               alc882_auto_init_dac(codec, spec->multiout.dac_nids[i]);
 }
 
 static void alc882_auto_init_hp_out(struct hda_codec *codec)
@@ -11091,31 +11063,21 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
                        alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
                }
        }
-}
-
-static void alc882_auto_init_analog_input(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       struct auto_pin_cfg *cfg = &spec->autocfg;
-       int i;
 
-       for (i = 0; i < cfg->num_inputs; i++) {
-               hda_nid_t nid = cfg->inputs[i].pin;
-               alc_set_input_pin(codec, nid, cfg->inputs[i].type);
-               if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
-                       snd_hda_codec_write(codec, nid, 0,
-                                           AC_VERB_SET_AMP_GAIN_MUTE,
-                                           AMP_OUT_MUTE);
-       }
+       /* mute DACs */
+       alc882_auto_init_dac(codec, spec->multiout.hp_nid);
+       for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++)
+               alc882_auto_init_dac(codec, spec->multiout.extra_out_nid[i]);
 }
 
+#define alc882_auto_init_analog_input  alc880_auto_init_analog_input
+
 static void alc882_auto_init_input_src(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        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;
@@ -11126,9 +11088,8 @@ static void alc882_auto_init_input_src(struct hda_codec *codec)
                                    AC_VERB_SET_AMP_GAIN_MUTE,
                                    AMP_IN_MUTE(0));
 
-               conns = snd_hda_get_connections(codec, nid, conn_list,
-                                               ARRAY_SIZE(conn_list));
-               if (conns < 0)
+               conns = snd_hda_get_conn_list(codec, nid, NULL);
+               if (conns <= 0)
                        continue;
                mux_idx = c >= spec->num_mux_defs ? 0 : c;
                imux = &spec->input_mux[mux_idx];
@@ -11215,10 +11176,10 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
        if (!spec->autocfg.line_outs)
                return 0; /* can't find valid BIOS pin config */
 
-       err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+       err = alc880_auto_fill_dac_nids(codec);
        if (err < 0)
                return err;
-       err = alc_auto_add_multi_channel_mode(codec);
+       err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids);
        if (err < 0)
                return err;
        err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
@@ -11244,7 +11205,6 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       add_verb(spec, alc883_auto_init_verbs);
        /* if ADC 0x07 is available, initialize it, too */
        if (get_wcaps_type(get_wcaps(codec, 0x07)) == AC_WID_AUD_IN)
                add_verb(spec, alc882_adc1_init_verbs);
@@ -11285,6 +11245,8 @@ static int patch_alc882(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       spec->mixer_nid = 0x0b;
+
        switch (codec->vendor_id) {
        case 0x10ec0882:
        case 0x10ec0885:
@@ -11356,7 +11318,6 @@ static int patch_alc882(struct hda_codec *codec)
                for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) {
                        const struct hda_input_mux *imux = spec->input_mux;
                        hda_nid_t cap;
-                       hda_nid_t items[16];
                        hda_nid_t nid = alc882_adc_nids[i];
                        unsigned int wcap = get_wcaps(codec, nid);
                        /* get type */
@@ -11367,8 +11328,7 @@ static int patch_alc882(struct hda_codec *codec)
                        err = snd_hda_get_connections(codec, nid, &cap, 1);
                        if (err < 0)
                                continue;
-                       err = snd_hda_get_connections(codec, cap, items,
-                                                     ARRAY_SIZE(items));
+                       err = snd_hda_get_conn_list(codec, cap, NULL);
                        if (err < 0)
                                continue;
                        for (j = 0; j < imux->num_items; j++)
@@ -12257,17 +12217,18 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
 {
        const char *pfx;
        int vbits;
-       int i, err;
+       int i, index, err;
 
        spec->multiout.num_dacs = 1;    /* only use one dac */
        spec->multiout.dac_nids = spec->private_dac_nids;
        spec->private_dac_nids[0] = 2;
 
-       pfx = alc_get_line_out_pfx(spec, true);
-       if (!pfx)
-               pfx = "Front";
        for (i = 0; i < 2; i++) {
-               err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, i);
+               pfx = alc_get_line_out_pfx(spec, i, true, &index);
+               if (!pfx)
+                       pfx = "PCM";
+               err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx,
+                                           index);
                if (err < 0)
                        return err;
                if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
@@ -12287,10 +12248,11 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
        vbits = alc262_check_volbit(cfg->line_out_pins[0]) |
                alc262_check_volbit(cfg->speaker_pins[0]) |
                alc262_check_volbit(cfg->hp_pins[0]);
-       if (vbits == 1 || vbits == 2)
-               pfx = "Master"; /* only one mixer is used */
        vbits = 0;
        for (i = 0; i < 2; i++) {
+               pfx = alc_get_line_out_pfx(spec, i, true, &index);
+               if (!pfx)
+                       pfx = "PCM";
                err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx,
                                             &vbits, i);
                if (err < 0)
@@ -12314,70 +12276,6 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
 #define alc262_auto_create_input_ctls \
        alc882_auto_create_input_ctls
 
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb alc262_volume_init_verbs[] = {
-       /*
-        * Unmute ADC0-2 and set the default input to mic-in
-        */
-       {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        * Note: PASD motherboards uses the Line In 2 as the input for
-        * front panel mic (mic 2)
-        */
-       /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
-       /*
-        * Set up output mixers (0x0c - 0x0f)
-        */
-       /* set vol=0 to output mixers */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
-       /* set up input amps for analog loopback */
-       /* Amp Indices: DAC = 0, mixer = 1 */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-       /* FIXME: use matrix-type input source selection */
-       /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
-       /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-       /* Input mixer2 */
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-       /* Input mixer3 */
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-
-       { }
-};
-
 static const struct hda_verb alc262_HP_BPC_init_verbs[] = {
        /*
         * Unmute ADC0-2 and set the default input to mic-in
@@ -12600,6 +12498,7 @@ static const struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = {
  */
 enum {
        PINFIX_FSC_H270,
+       PINFIX_HP_Z200,
 };
 
 static const struct alc_fixup alc262_fixups[] = {
@@ -12612,9 +12511,17 @@ static const struct alc_fixup alc262_fixups[] = {
                        { }
                }
        },
+       [PINFIX_HP_Z200] = {
+               .type = ALC_FIXUP_PINS,
+               .v.pins = (const struct alc_pincfg[]) {
+                       { 0x16, 0x99130120 }, /* internal speaker */
+                       { }
+               }
+       },
 };
 
 static const struct snd_pci_quirk alc262_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", PINFIX_HP_Z200),
        SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", PINFIX_FSC_H270),
        {}
 };
@@ -12666,7 +12573,6 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       add_verb(spec, alc262_volume_init_verbs);
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
@@ -12731,6 +12637,8 @@ static const struct snd_pci_quirk alc262_cfg_tbl[] = {
                           ALC262_HP_BPC),
        SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1500, "HP z series",
                           ALC262_HP_BPC),
+       SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200",
+                          ALC262_AUTO),
        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),
@@ -13024,6 +12932,9 @@ static int patch_alc262(struct hda_codec *codec)
                return -ENOMEM;
 
        codec->spec = spec;
+
+       spec->mixer_nid = 0x0b;
+
 #if 0
        /* pshou 07/11/05  set a zero PCM sample to DAC when FIFO is
         * under-run
@@ -13440,6 +13351,8 @@ static const struct hda_verb alc268_base_init_verbs[] = {
        { }
 };
 
+/* only for model=test */
+#ifdef CONFIG_SND_DEBUG
 /*
  * generic initialization of ADC, input mixers and output mixers
  */
@@ -13460,12 +13373,15 @@ static const struct hda_verb alc268_volume_init_verbs[] = {
 
        {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
        {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       { }
+};
+#endif /* CONFIG_SND_DEBUG */
 
-       /* set PCBEEP vol = 0, mute connections */
+/* set PCBEEP vol = 0, mute connections */
+static const struct hda_verb alc268_beep_init_verbs[] = {
        {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
        {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
        { }
 };
 
@@ -13625,10 +13541,8 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
        nid = cfg->line_out_pins[0];
        if (nid) {
                const char *name;
-               if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
-                       name = "Speaker";
-               else
-                       name = "Front";
+               int index;
+               name = alc_get_line_out_pfx(spec, 0, true, &index);
                err = alc268_new_analog_output(spec, nid, name, 0);
                if (err < 0)
                        return err;
@@ -13682,6 +13596,14 @@ static void alc268_auto_set_output_and_unmute(struct hda_codec *codec,
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
 }
 
+static void alc268_auto_init_dac(struct hda_codec *codec, hda_nid_t nid)
+{
+       if (!nid)
+               return;
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                           AMP_OUT_MUTE);
+}
+
 static void alc268_auto_init_multi_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -13692,6 +13614,9 @@ static void alc268_auto_init_multi_out(struct hda_codec *codec)
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
                alc268_auto_set_output_and_unmute(codec, nid, pin_type);
        }
+       /* mute DACs */
+       for (i = 0; i < spec->multiout.num_dacs; i++)
+               alc268_auto_init_dac(codec, spec->multiout.dac_nids[i]);
 }
 
 static void alc268_auto_init_hp_out(struct hda_codec *codec)
@@ -13711,6 +13636,10 @@ static void alc268_auto_init_hp_out(struct hda_codec *codec)
        if (spec->autocfg.mono_out_pin)
                snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0,
                                    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+       /* mute DACs */
+       alc268_auto_init_dac(codec, spec->multiout.hp_nid);
+       for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++)
+               alc268_auto_init_dac(codec, spec->multiout.extra_out_nid[i]);
 }
 
 static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
@@ -13804,7 +13733,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
        if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d)
                add_mixer(spec, alc268_beep_mixer);
 
-       add_verb(spec, alc268_volume_init_verbs);
+       add_verb(spec, alc268_beep_init_verbs);
        spec->num_mux_defs = 2;
        spec->input_mux = &spec->private_imux[0];
 
@@ -14029,7 +13958,8 @@ static const struct alc_config_preset alc268_presets[] = {
        [ALC268_TEST] = {
                .mixers = { alc268_test_mixer, alc268_capture_mixer },
                .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
-                               alc268_volume_init_verbs },
+                               alc268_volume_init_verbs,
+                               alc268_beep_init_verbs },
                .num_dacs = ARRAY_SIZE(alc268_dac_nids),
                .dac_nids = alc268_dac_nids,
                .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
@@ -14056,6 +13986,8 @@ static int patch_alc268(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       /* ALC268 has no aa-loopback mixer */
+
        board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST,
                                                  alc268_models,
                                                  alc268_cfg_tbl);
@@ -14767,13 +14699,10 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       if (spec->codec_variant != ALC269_TYPE_NORMAL) {
-               add_verb(spec, alc269vb_init_verbs);
+       if (spec->codec_variant != ALC269_TYPE_NORMAL)
                alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21);
-       } else {
-               add_verb(spec, alc269_init_verbs);
+       else
                alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-       }
 
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
@@ -15223,6 +15152,8 @@ static int patch_alc269(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       spec->mixer_nid = 0x0b;
+
        alc_auto_parse_customize_define(codec);
 
        if (codec->vendor_id == 0x10ec0269) {
@@ -15850,58 +15781,6 @@ static const struct hda_verb alc861_asus_laptop_init_verbs[] = {
        { }
 };
 
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb alc861_auto_init_verbs[] = {
-       /*
-        * Unmute ADC0 and set the default input to mic-in
-        */
-       /* {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-       /* Unmute DAC0~3 & spdif out*/
-       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
-       /* Unmute Mixer 14 (mic) 1c (Line in)*/
-       {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-       /* Unmute Stereo Mixer 15 */
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c},
-
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-
-       {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},  /* set Mic 1 */
-
-       { }
-};
-
 static const struct hda_verb alc861_toshiba_init_verbs[] = {
        {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
 
@@ -15969,7 +15848,7 @@ static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
 {
        struct alc_spec *spec = codec->spec;
        hda_nid_t mix, srcs[5];
-       int i, j, num;
+       int i, num;
 
        if (snd_hda_get_connections(codec, pin, &mix, 1) != 1)
                return 0;
@@ -15981,20 +15860,18 @@ static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
                type = get_wcaps_type(get_wcaps(codec, srcs[i]));
                if (type != AC_WID_AUD_OUT)
                        continue;
-               for (j = 0; j < spec->multiout.num_dacs; j++)
-                       if (spec->multiout.dac_nids[j] == srcs[i])
-                               break;
-               if (j >= spec->multiout.num_dacs)
+               if (!found_in_nid_list(srcs[i], spec->multiout.dac_nids,
+                                      spec->multiout.num_dacs))
                        return srcs[i];
        }
        return 0;
 }
 
 /* fill in the dac_nids table from the parsed pin configuration */
-static int alc861_auto_fill_dac_nids(struct hda_codec *codec,
-                                    const struct auto_pin_cfg *cfg)
+static int alc861_auto_fill_dac_nids(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
        hda_nid_t nid, dac;
 
@@ -16024,10 +15901,6 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec,
                                             const struct auto_pin_cfg *cfg)
 {
        struct alc_spec *spec = codec->spec;
-       static const char * const chname[4] = {
-               "Front", "Surround", NULL /*CLFE*/, "Side"
-       };
-       const char *pfx = alc_get_line_out_pfx(spec, true);
        hda_nid_t nid;
        int i, err, noutputs;
 
@@ -16036,10 +15909,13 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec,
                noutputs += spec->multi_ios;
 
        for (i = 0; i < noutputs; i++) {
+               const char *name;
+               int index;
                nid = spec->multiout.dac_nids[i];
                if (!nid)
                        continue;
-               if (!pfx && i == 2) {
+               name = alc_get_line_out_pfx(spec, i, true, &index);
+               if (!name) {
                        /* Center/LFE */
                        err = alc861_create_out_sw(codec, "Center", nid, 1);
                        if (err < 0)
@@ -16048,12 +15924,6 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec,
                        if (err < 0)
                                return err;
                } else {
-                       const char *name = pfx;
-                       int index = i;
-                       if (!name) {
-                               name = chname[i];
-                               index = 0;
-                       }
                        err = __alc861_create_out_sw(codec, name, nid, index, 3);
                        if (err < 0)
                                return err;
@@ -16122,7 +15992,7 @@ static void alc861_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
-       for (i = 0; i < spec->autocfg.line_outs; i++) {
+       for (i = 0; i < spec->autocfg.line_outs + spec->multi_ios; i++) {
                hda_nid_t nid = spec->autocfg.line_out_pins[i];
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
                if (nid)
@@ -16147,18 +16017,7 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec)
                                                  spec->multiout.dac_nids[0]);
 }
 
-static void alc861_auto_init_analog_input(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       struct auto_pin_cfg *cfg = &spec->autocfg;
-       int i;
-
-       for (i = 0; i < cfg->num_inputs; i++) {
-               hda_nid_t nid = cfg->inputs[i].pin;
-               if (nid >= 0x0c && nid <= 0x11)
-                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
-       }
-}
+#define alc861_auto_init_analog_input  alc880_auto_init_analog_input
 
 /* parse the BIOS configuration and set up the alc_spec */
 /* return 1 if successful, 0 if the proper config is not found,
@@ -16177,10 +16036,10 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
        if (!spec->autocfg.line_outs)
                return 0; /* can't find valid BIOS pin config */
 
-       err = alc861_auto_fill_dac_nids(codec, &spec->autocfg);
+       err = alc861_auto_fill_dac_nids(codec);
        if (err < 0)
                return err;
-       err = alc_auto_add_multi_channel_mode(codec);
+       err = alc_auto_add_multi_channel_mode(codec, alc861_auto_fill_dac_nids);
        if (err < 0)
                return err;
        err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg);
@@ -16200,8 +16059,6 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       add_verb(spec, alc861_auto_init_verbs);
-
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
@@ -16416,6 +16273,8 @@ static int patch_alc861(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       spec->mixer_nid = 0x15;
+
         board_config = snd_hda_check_board_config(codec, ALC861_MODEL_LAST,
                                                  alc861_models,
                                                  alc861_cfg_tbl);
@@ -17101,61 +16960,9 @@ static int alc861vd_auto_create_input_ctls(struct hda_codec *codec,
 }
 
 
-static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec,
-                               hda_nid_t nid, int pin_type, int dac_idx)
-{
-       alc_set_pin_output(codec, nid, pin_type);
-}
-
-static void alc861vd_auto_init_multi_out(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       int i;
-
-       for (i = 0; i <= HDA_SIDE; i++) {
-               hda_nid_t nid = spec->autocfg.line_out_pins[i];
-               int pin_type = get_pin_type(spec->autocfg.line_out_type);
-               if (nid)
-                       alc861vd_auto_set_output_and_unmute(codec, nid,
-                                                           pin_type, i);
-       }
-}
-
-
-static void alc861vd_auto_init_hp_out(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       hda_nid_t pin;
-
-       pin = spec->autocfg.hp_pins[0];
-       if (pin) /* connect to front and use dac 0 */
-               alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
-       pin = spec->autocfg.speaker_pins[0];
-       if (pin)
-               alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
-}
-
-#define ALC861VD_PIN_CD_NID            ALC880_PIN_CD_NID
-
-static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       struct auto_pin_cfg *cfg = &spec->autocfg;
-       int i;
-
-       for (i = 0; i < cfg->num_inputs; i++) {
-               hda_nid_t nid = cfg->inputs[i].pin;
-               if (alc_is_input_pin(codec, nid)) {
-                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
-                       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);
-               }
-       }
-}
-
+#define alc861vd_auto_init_multi_out   alc882_auto_init_multi_out
+#define alc861vd_auto_init_hp_out      alc882_auto_init_hp_out
+#define alc861vd_auto_init_analog_input        alc882_auto_init_analog_input
 #define alc861vd_auto_init_input_src   alc882_auto_init_input_src
 
 #define alc861vd_idx_to_mixer_vol(nid)         ((nid) + 0x02)
@@ -17167,10 +16974,6 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
 static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
                                             const struct auto_pin_cfg *cfg)
 {
-       static const char * const chname[4] = {
-               "Front", "Surround", "CLFE", "Side"
-       };
-       const char *pfx = alc_get_line_out_pfx(spec, true);
        hda_nid_t nid_v, nid_s;
        int i, err, noutputs;
 
@@ -17179,6 +16982,8 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
                noutputs += spec->multi_ios;
 
        for (i = 0; i < noutputs; i++) {
+               const char *name;
+               int index;
                if (!spec->multiout.dac_nids[i])
                        continue;
                nid_v = alc861vd_idx_to_mixer_vol(
@@ -17188,7 +16993,8 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
                                alc880_dac_to_idx(
                                        spec->multiout.dac_nids[i]));
 
-               if (!pfx && i == 2) {
+               name = alc_get_line_out_pfx(spec, i, true, &index);
+               if (!name) {
                        /* Center/LFE */
                        err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
                                              "Center",
@@ -17215,12 +17021,6 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
                        if (err < 0)
                                return err;
                } else {
-                       const char *name = pfx;
-                       int index = i;
-                       if (!name) {
-                               name = chname[i];
-                               index = 0;
-                       }
                        err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
                                                name, index,
                                          HDA_COMPOSE_AMP_VAL(nid_v, 3, 0,
@@ -17300,10 +17100,10 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec)
        if (!spec->autocfg.line_outs)
                return 0; /* can't find valid BIOS pin config */
 
-       err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+       err = alc880_auto_fill_dac_nids(codec);
        if (err < 0)
                return err;
-       err = alc_auto_add_multi_channel_mode(codec);
+       err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids);
        if (err < 0)
                return err;
        err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg);
@@ -17330,8 +17130,6 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       add_verb(spec, alc861vd_volume_init_verbs);
-
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
@@ -17390,6 +17188,8 @@ static int patch_alc861vd(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       spec->mixer_nid = 0x0b;
+
        board_config = snd_hda_check_board_config(codec, ALC861VD_MODEL_LAST,
                                                  alc861vd_models,
                                                  alc861vd_cfg_tbl);
@@ -18955,7 +18755,7 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
 {
        struct alc_spec *spec = codec->spec;
        hda_nid_t srcs[5];
-       int i, j, num;
+       int i, num;
 
        pin = alc_go_down_to_selector(codec, pin);
        num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
@@ -18963,30 +18763,78 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
                hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]);
                if (!nid)
                        continue;
-               for (j = 0; j < spec->multiout.num_dacs; j++)
-                       if (spec->multiout.dac_nids[j] == nid)
-                               break;
-               if (j >= spec->multiout.num_dacs)
-                       return nid;
+               if (found_in_nid_list(nid, spec->multiout.dac_nids,
+                                     spec->multiout.num_dacs))
+                       continue;
+               if (spec->multiout.hp_nid == nid)
+                       continue;
+               if (found_in_nid_list(nid, spec->multiout.extra_out_nid,
+                                     ARRAY_SIZE(spec->multiout.extra_out_nid)))
+                   continue;
+               return nid;
        }
        return 0;
 }
 
+static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
+{
+       hda_nid_t sel = alc_go_down_to_selector(codec, pin);
+       if (snd_hda_get_conn_list(codec, sel, NULL) == 1)
+               return alc_auto_look_for_dac(codec, pin);
+       return 0;
+}
+
 /* fill in the dac_nids table from the parsed pin configuration */
-static int alc662_auto_fill_dac_nids(struct hda_codec *codec,
-                                    const struct auto_pin_cfg *cfg)
+static int alc662_auto_fill_dac_nids(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
+       bool redone;
        int i;
-       hda_nid_t dac;
 
+ again:
+       spec->multiout.num_dacs = 0;
+       spec->multiout.hp_nid = 0;
+       spec->multiout.extra_out_nid[0] = 0;
+       memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
        spec->multiout.dac_nids = spec->private_dac_nids;
+
+       /* fill hard-wired DACs first */
+       if (!redone) {
+               for (i = 0; i < cfg->line_outs; i++)
+                       spec->private_dac_nids[i] =
+                               get_dac_if_single(codec, cfg->line_out_pins[i]);
+               if (cfg->hp_outs)
+                       spec->multiout.hp_nid =
+                               get_dac_if_single(codec, cfg->hp_pins[0]);
+               if (cfg->speaker_outs)
+                       spec->multiout.extra_out_nid[0] =
+                               get_dac_if_single(codec, cfg->speaker_pins[0]);
+       }
+
        for (i = 0; i < cfg->line_outs; i++) {
-               dac = alc_auto_look_for_dac(codec, cfg->line_out_pins[i]);
-               if (!dac)
+               hda_nid_t pin = cfg->line_out_pins[i];
+               if (spec->private_dac_nids[i])
                        continue;
-               spec->private_dac_nids[spec->multiout.num_dacs++] = dac;
+               spec->private_dac_nids[i] = alc_auto_look_for_dac(codec, pin);
+               if (!spec->private_dac_nids[i] && !redone) {
+                       /* if we can't find primary DACs, re-probe without
+                        * checking the hard-wired DACs
+                        */
+                       redone = true;
+                       goto again;
+               }
        }
+
+       for (i = 0; i < cfg->line_outs; i++) {
+               if (spec->private_dac_nids[i])
+                       spec->multiout.num_dacs++;
+               else
+                       memmove(spec->private_dac_nids + i,
+                               spec->private_dac_nids + i + 1,
+                               sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
+       }
+
        return 0;
 }
 
@@ -19018,10 +18866,6 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
                                             const struct auto_pin_cfg *cfg)
 {
        struct alc_spec *spec = codec->spec;
-       static const char * const chname[4] = {
-               "Front", "Surround", NULL /*CLFE*/, "Side"
-       };
-       const char *pfx = alc_get_line_out_pfx(spec, true);
        hda_nid_t nid, mix, pin;
        int i, err, noutputs;
 
@@ -19030,6 +18874,8 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
                noutputs += spec->multi_ios;
 
        for (i = 0; i < noutputs; i++) {
+               const char *name;
+               int index;
                nid = spec->multiout.dac_nids[i];
                if (!nid)
                        continue;
@@ -19040,7 +18886,8 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
                mix = alc_auto_dac_to_mix(codec, pin, nid);
                if (!mix)
                        continue;
-               if (!pfx && i == 2) {
+               name = alc_get_line_out_pfx(spec, i, true, &index);
+               if (!name) {
                        /* Center/LFE */
                        err = alc662_add_vol_ctl(spec, "Center", nid, 1);
                        if (err < 0)
@@ -19055,12 +18902,6 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
                        if (err < 0)
                                return err;
                } else {
-                       const char *name = pfx;
-                       int index = i;
-                       if (!name) {
-                               name = chname[i];
-                               index = 0;
-                       }
                        err = __alc662_add_vol_ctl(spec, name, nid, index, 3);
                        if (err < 0)
                                return err;
@@ -19073,18 +18914,16 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
 }
 
 /* add playback controls for speaker and HP outputs */
-/* return DAC nid if any new DAC is assigned */
 static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
-                                       const char *pfx)
+                                       hda_nid_t dac, const char *pfx)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t nid, mix;
+       hda_nid_t mix;
        int err;
 
        if (!pin)
                return 0;
-       nid = alc_auto_look_for_dac(codec, pin);
-       if (!nid) {
+       if (!dac) {
                /* the corresponding DAC is already occupied */
                if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
                        return 0; /* no way */
@@ -19093,16 +18932,16 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
                                   HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
        }
 
-       mix = alc_auto_dac_to_mix(codec, pin, nid);
+       mix = alc_auto_dac_to_mix(codec, pin, dac);
        if (!mix)
                return 0;
-       err = alc662_add_vol_ctl(spec, pfx, nid, 3);
+       err = alc662_add_vol_ctl(spec, pfx, dac, 3);
        if (err < 0)
                return err;
        err = alc662_add_sw_ctl(spec, pfx, mix, 3);
        if (err < 0)
                return err;
-       return nid;
+       return 0;
 }
 
 /* create playback/capture controls for input pins */
@@ -19165,27 +19004,7 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec)
                                        spec->multiout.extra_out_nid[0]);
 }
 
-#define ALC662_PIN_CD_NID              ALC880_PIN_CD_NID
-
-static void alc662_auto_init_analog_input(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       struct auto_pin_cfg *cfg = &spec->autocfg;
-       int i;
-
-       for (i = 0; i < cfg->num_inputs; i++) {
-               hda_nid_t nid = cfg->inputs[i].pin;
-               if (alc_is_input_pin(codec, nid)) {
-                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
-                       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);
-               }
-       }
-}
-
+#define alc662_auto_init_analog_input  alc882_auto_init_analog_input
 #define alc662_auto_init_input_src     alc882_auto_init_input_src
 
 /*
@@ -19308,15 +19127,29 @@ static const struct snd_kcontrol_new alc_auto_channel_mode_enum = {
        .put = alc_auto_ch_mode_put,
 };
 
-static int alc_auto_add_multi_channel_mode(struct hda_codec *codec)
+static int alc_auto_add_multi_channel_mode(struct hda_codec *codec,
+                                          int (*fill_dac)(struct hda_codec *))
 {
        struct alc_spec *spec = codec->spec;
        struct auto_pin_cfg *cfg = &spec->autocfg;
        unsigned int location, defcfg;
        int num_pins;
 
+       if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && cfg->hp_outs == 1) {
+               /* use HP as primary out */
+               cfg->speaker_outs = cfg->line_outs;
+               memcpy(cfg->speaker_pins, cfg->line_out_pins,
+                      sizeof(cfg->speaker_pins));
+               cfg->line_outs = cfg->hp_outs;
+               memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins));
+               cfg->hp_outs = 0;
+               memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+               cfg->line_out_type = AUTO_PIN_HP_OUT;
+               if (fill_dac)
+                       fill_dac(codec);
+       }
        if (cfg->line_outs != 1 ||
-           cfg->line_out_type != AUTO_PIN_LINE_OUT)
+           cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
                return 0;
 
        defcfg = snd_hda_codec_get_pincfg(codec, cfg->line_out_pins[0]);
@@ -19354,10 +19187,10 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
        if (!spec->autocfg.line_outs)
                return 0; /* can't find valid BIOS pin config */
 
-       err = alc662_auto_fill_dac_nids(codec, &spec->autocfg);
+       err = alc662_auto_fill_dac_nids(codec);
        if (err < 0)
                return err;
-       err = alc_auto_add_multi_channel_mode(codec);
+       err = alc_auto_add_multi_channel_mode(codec, alc662_auto_fill_dac_nids);
        if (err < 0)
                return err;
        err = alc662_auto_create_multi_out_ctls(codec, &spec->autocfg);
@@ -19365,17 +19198,15 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
                return err;
        err = alc662_auto_create_extra_out(codec,
                                           spec->autocfg.speaker_pins[0],
+                                          spec->multiout.extra_out_nid[0],
                                           "Speaker");
        if (err < 0)
                return err;
-       if (err)
-               spec->multiout.extra_out_nid[0] = err;
        err = alc662_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
+                                          spec->multiout.hp_nid,
                                           "Headphone");
        if (err < 0)
                return err;
-       if (err)
-               spec->multiout.hp_nid = err;
        err = alc662_auto_create_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
@@ -19499,6 +19330,8 @@ static int patch_alc662(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       spec->mixer_nid = 0x0b;
+
        alc_auto_parse_customize_define(codec);
 
        alc_fix_pll_init(codec, 0x20, 0x04, 15);
@@ -19865,10 +19698,8 @@ static int alc680_auto_create_multi_out_ctls(struct alc_spec *spec,
        nid = cfg->line_out_pins[0];
        if (nid) {
                const char *name;
-               if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
-                       name = "Speaker";
-               else
-                       name = "Front";
+               int index;
+               name = alc_get_line_out_pfx(spec, 0, true, &index);
                err = alc680_new_analog_output(spec, nid, name, 0);
                if (err < 0)
                        return err;
@@ -19960,8 +19791,6 @@ static int alc680_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       add_verb(spec, alc680_init_verbs);
-
        err = alc_auto_add_mic_boost(codec);
        if (err < 0)
                return err;
@@ -20025,6 +19854,8 @@ static int patch_alc680(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       /* ALC680 has no aa-loopback mixer */
+
        board_config = snd_hda_check_board_config(codec, ALC680_MODEL_LAST,
                                                  alc680_models,
                                                  alc680_cfg_tbl);