Merge branch 'topic/hda-dmic-fix' into topic/hda
authorTakashi Iwai <tiwai@suse.de>
Thu, 13 Aug 2009 16:14:02 +0000 (18:14 +0200)
committerTakashi Iwai <tiwai@suse.de>
Thu, 13 Aug 2009 16:14:02 +0000 (18:14 +0200)
Documentation/sound/alsa/HD-Audio-Models.txt
sound/pci/hda/Kconfig
sound/pci/hda/Makefile
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_eld.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_cirrus.c [new file with mode: 0644]
sound/pci/hda/patch_intelhdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c

index 4c95a6c..775beea 100644 (file)
@@ -383,3 +383,7 @@ STAC9872
 ========
   vaio         VAIO laptop without SPDIF
   auto         BIOS setup (default)
+
+Cirrus Logic CS4206/4207
+========================
+  mbp55                MacBook Pro 5,5
index b8a77f9..55545e0 100644 (file)
@@ -148,6 +148,19 @@ config SND_HDA_ELD
        def_bool y
        depends on SND_HDA_CODEC_INTELHDMI
 
+config SND_HDA_CODEC_CIRRUS
+       bool "Build Cirrus Logic codec support"
+       depends on SND_HDA_INTEL
+       default y
+       help
+         Say Y here to include Cirrus Logic codec support in
+         snd-hda-intel driver, such as CS4206.
+
+         When the HD-audio driver is built as a module, the codec
+         support code is also built as another module,
+         snd-hda-codec-cirrus.
+         This module is automatically loaded at probing.
+
 config SND_HDA_CODEC_CONEXANT
        bool "Build Conexant HD-audio codec support"
        default y
index e3081d4..315a1c4 100644 (file)
@@ -13,6 +13,7 @@ snd-hda-codec-analog-objs :=  patch_analog.o
 snd-hda-codec-idt-objs :=      patch_sigmatel.o
 snd-hda-codec-si3054-objs :=   patch_si3054.o
 snd-hda-codec-atihdmi-objs :=  patch_atihdmi.o
+snd-hda-codec-cirrus-objs :=   patch_cirrus.o
 snd-hda-codec-ca0110-objs :=   patch_ca0110.o
 snd-hda-codec-conexant-objs := patch_conexant.o
 snd-hda-codec-via-objs :=      patch_via.o
@@ -41,6 +42,9 @@ endif
 ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
 endif
+ifdef CONFIG_SND_HDA_CODEC_CIRRUS
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o
+endif
 ifdef CONFIG_SND_HDA_CODEC_CA0110
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
 endif
index 3a603cd..24401d5 100644 (file)
@@ -44,6 +44,7 @@ struct hda_vendor_id {
 /* codec vendor labels */
 static struct hda_vendor_id hda_vendor_ids[] = {
        { 0x1002, "ATI" },
+       { 0x1013, "Cirrus Logic" },
        { 0x1057, "Motorola" },
        { 0x1095, "Silicon Image" },
        { 0x10de, "Nvidia" },
@@ -184,7 +185,7 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
        mutex_lock(&bus->cmd_mutex);
        err = bus->ops.command(bus, cmd);
        if (!err && res)
-               *res = bus->ops.get_response(bus);
+               *res = bus->ops.get_response(bus, codec->addr);
        mutex_unlock(&bus->cmd_mutex);
        snd_hda_power_down(codec);
        if (res && *res == -1 && bus->rirb_error) {
index 72c9975..99552fb 100644 (file)
@@ -568,7 +568,7 @@ struct hda_bus_ops {
        /* send a single command */
        int (*command)(struct hda_bus *bus, unsigned int cmd);
        /* get a response from the last command */
-       unsigned int (*get_response)(struct hda_bus *bus);
+       unsigned int (*get_response)(struct hda_bus *bus, unsigned int addr);
        /* free the private data */
        void (*private_free)(struct hda_bus *);
        /* attach a PCM stream */
index fcad5ec..9446a5a 100644 (file)
@@ -508,7 +508,7 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
        char name[64];
        char *sname;
        long long val;
-       int n;
+       unsigned int n;
 
        while (!snd_info_get_line(buffer, line, sizeof(line))) {
                if (sscanf(line, "%s %llx", name, &val) != 2)
@@ -539,7 +539,7 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
                                sname++;
                                n = 10 * n + name[4] - '0';
                        }
-                       if (n < 0 || n > 31) /* double the CEA limit */
+                       if (n >= ELD_MAX_SAD)
                                continue;
                        if (!strcmp(sname, "_coding_type"))
                                e->sad[n].format = val;
index 4db854b..20a66f8 100644 (file)
@@ -260,7 +260,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 
 /* STATESTS int mask: S3,SD2,SD1,SD0 */
 #define AZX_MAX_CODECS         4
-#define STATESTS_INT_MASK      0x0f
+#define STATESTS_INT_MASK      ((1 << AZX_MAX_CODECS) - 1)
 
 /* SD_CTL bits */
 #define SD_CTL_STREAM_RESET    0x01    /* stream reset bit */
@@ -368,8 +368,8 @@ struct azx_rb {
        dma_addr_t addr;        /* physical address of CORB/RIRB buffer */
        /* for RIRB */
        unsigned short rp, wp;  /* read/write pointers */
-       int cmds;               /* number of pending requests */
-       u32 res;                /* last read value */
+       int cmds[AZX_MAX_CODECS];       /* number of pending requests */
+       u32 res[AZX_MAX_CODECS];        /* last read value */
 };
 
 struct azx {
@@ -425,7 +425,7 @@ struct azx {
        unsigned int probing :1; /* codec probing phase */
 
        /* for debugging */
-       unsigned int last_cmd;  /* last issued command (to sync) */
+       unsigned int last_cmd[AZX_MAX_CODECS];
 
        /* for pending irqs */
        struct work_struct irq_pending_work;
@@ -520,6 +520,7 @@ static int azx_alloc_cmd_io(struct azx *chip)
 
 static void azx_init_cmd_io(struct azx *chip)
 {
+       spin_lock_irq(&chip->reg_lock);
        /* CORB set up */
        chip->corb.addr = chip->rb.addr;
        chip->corb.buf = (u32 *)chip->rb.area;
@@ -538,7 +539,8 @@ static void azx_init_cmd_io(struct azx *chip)
        /* RIRB set up */
        chip->rirb.addr = chip->rb.addr + 2048;
        chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
-       chip->rirb.wp = chip->rirb.rp = chip->rirb.cmds = 0;
+       chip->rirb.wp = chip->rirb.rp = 0;
+       memset(chip->rirb.cmds, 0, sizeof(chip->rirb.cmds));
        azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
        azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
 
@@ -550,30 +552,60 @@ static void azx_init_cmd_io(struct azx *chip)
        azx_writew(chip, RINTCNT, 1);
        /* enable rirb dma and response irq */
        azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
+       spin_unlock_irq(&chip->reg_lock);
 }
 
 static void azx_free_cmd_io(struct azx *chip)
 {
+       spin_lock_irq(&chip->reg_lock);
        /* disable ringbuffer DMAs */
        azx_writeb(chip, RIRBCTL, 0);
        azx_writeb(chip, CORBCTL, 0);
+       spin_unlock_irq(&chip->reg_lock);
+}
+
+static unsigned int azx_command_addr(u32 cmd)
+{
+       unsigned int addr = cmd >> 28;
+
+       if (addr >= AZX_MAX_CODECS) {
+               snd_BUG();
+               addr = 0;
+       }
+
+       return addr;
+}
+
+static unsigned int azx_response_addr(u32 res)
+{
+       unsigned int addr = res & 0xf;
+
+       if (addr >= AZX_MAX_CODECS) {
+               snd_BUG();
+               addr = 0;
+       }
+
+       return addr;
 }
 
 /* send a command */
 static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
 {
        struct azx *chip = bus->private_data;
+       unsigned int addr = azx_command_addr(val);
        unsigned int wp;
 
+       spin_lock_irq(&chip->reg_lock);
+
        /* add command to corb */
        wp = azx_readb(chip, CORBWP);
        wp++;
        wp %= ICH6_MAX_CORB_ENTRIES;
 
-       spin_lock_irq(&chip->reg_lock);
-       chip->rirb.cmds++;
+       chip->rirb.cmds[addr]++;
        chip->corb.buf[wp] = cpu_to_le32(val);
        azx_writel(chip, CORBWP, wp);
+
        spin_unlock_irq(&chip->reg_lock);
 
        return 0;
@@ -585,13 +617,14 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
 static void azx_update_rirb(struct azx *chip)
 {
        unsigned int rp, wp;
+       unsigned int addr;
        u32 res, res_ex;
 
        wp = azx_readb(chip, RIRBWP);
        if (wp == chip->rirb.wp)
                return;
        chip->rirb.wp = wp;
-               
+
        while (chip->rirb.rp != wp) {
                chip->rirb.rp++;
                chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES;
@@ -599,18 +632,24 @@ static void azx_update_rirb(struct azx *chip)
                rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
                res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
                res = le32_to_cpu(chip->rirb.buf[rp]);
+               addr = azx_response_addr(res_ex);
                if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
                        snd_hda_queue_unsol_event(chip->bus, res, res_ex);
-               else if (chip->rirb.cmds) {
-                       chip->rirb.res = res;
+               else if (chip->rirb.cmds[addr]) {
+                       chip->rirb.res[addr] = res;
                        smp_wmb();
-                       chip->rirb.cmds--;
-               }
+                       chip->rirb.cmds[addr]--;
+               } else
+                       snd_printk(KERN_ERR SFX "spurious response %#x:%#x, "
+                                  "last cmd=%#08x\n",
+                                  res, res_ex,
+                                  chip->last_cmd[addr]);
        }
 }
 
 /* receive a response */
-static unsigned int azx_rirb_get_response(struct hda_bus *bus)
+static unsigned int azx_rirb_get_response(struct hda_bus *bus,
+                                         unsigned int addr)
 {
        struct azx *chip = bus->private_data;
        unsigned long timeout;
@@ -623,10 +662,10 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
                        azx_update_rirb(chip);
                        spin_unlock_irq(&chip->reg_lock);
                }
-               if (!chip->rirb.cmds) {
+               if (!chip->rirb.cmds[addr]) {
                        smp_rmb();
                        bus->rirb_error = 0;
-                       return chip->rirb.res; /* the last value */
+                       return chip->rirb.res[addr]; /* the last value */
                }
                if (time_after(jiffies, timeout))
                        break;
@@ -640,7 +679,8 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
 
        if (chip->msi) {
                snd_printk(KERN_WARNING SFX "No response from codec, "
-                          "disabling MSI: last cmd=0x%08x\n", chip->last_cmd);
+                          "disabling MSI: last cmd=0x%08x\n",
+                          chip->last_cmd[addr]);
                free_irq(chip->irq, chip);
                chip->irq = -1;
                pci_disable_msi(chip->pci);
@@ -655,7 +695,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
        if (!chip->polling_mode) {
                snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
                           "switching to polling mode: last cmd=0x%08x\n",
-                          chip->last_cmd);
+                          chip->last_cmd[addr]);
                chip->polling_mode = 1;
                goto again;
        }
@@ -679,7 +719,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
 
        snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
                   "switching to single_cmd mode: last cmd=0x%08x\n",
-                  chip->last_cmd);
+                  chip->last_cmd[addr]);
        chip->single_cmd = 1;
        bus->response_reset = 0;
        /* re-initialize CORB/RIRB */
@@ -699,7 +739,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
  */
 
 /* receive a response */
-static int azx_single_wait_for_response(struct azx *chip)
+static int azx_single_wait_for_response(struct azx *chip, unsigned int addr)
 {
        int timeout = 50;
 
@@ -707,7 +747,7 @@ static int azx_single_wait_for_response(struct azx *chip)
                /* check IRV busy bit */
                if (azx_readw(chip, IRS) & ICH6_IRS_VALID) {
                        /* reuse rirb.res as the response return value */
-                       chip->rirb.res = azx_readl(chip, IR);
+                       chip->rirb.res[addr] = azx_readl(chip, IR);
                        return 0;
                }
                udelay(1);
@@ -715,7 +755,7 @@ static int azx_single_wait_for_response(struct azx *chip)
        if (printk_ratelimit())
                snd_printd(SFX "get_response timeout: IRS=0x%x\n",
                           azx_readw(chip, IRS));
-       chip->rirb.res = -1;
+       chip->rirb.res[addr] = -1;
        return -EIO;
 }
 
@@ -723,6 +763,7 @@ static int azx_single_wait_for_response(struct azx *chip)
 static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
 {
        struct azx *chip = bus->private_data;
+       unsigned int addr = azx_command_addr(val);
        int timeout = 50;
 
        bus->rirb_error = 0;
@@ -735,7 +776,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
                        azx_writel(chip, IC, val);
                        azx_writew(chip, IRS, azx_readw(chip, IRS) |
                                   ICH6_IRS_BUSY);
-                       return azx_single_wait_for_response(chip);
+                       return azx_single_wait_for_response(chip, addr);
                }
                udelay(1);
        }
@@ -746,10 +787,11 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
 }
 
 /* receive a response */
-static unsigned int azx_single_get_response(struct hda_bus *bus)
+static unsigned int azx_single_get_response(struct hda_bus *bus,
+                                           unsigned int addr)
 {
        struct azx *chip = bus->private_data;
-       return chip->rirb.res;
+       return chip->rirb.res[addr];
 }
 
 /*
@@ -764,7 +806,7 @@ static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
 {
        struct azx *chip = bus->private_data;
 
-       chip->last_cmd = val;
+       chip->last_cmd[azx_command_addr(val)] = val;
        if (chip->single_cmd)
                return azx_single_send_cmd(bus, val);
        else
@@ -772,13 +814,14 @@ static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
 }
 
 /* get a response */
-static unsigned int azx_get_response(struct hda_bus *bus)
+static unsigned int azx_get_response(struct hda_bus *bus,
+                                    unsigned int addr)
 {
        struct azx *chip = bus->private_data;
        if (chip->single_cmd)
-               return azx_single_get_response(bus);
+               return azx_single_get_response(bus, addr);
        else
-               return azx_rirb_get_response(bus);
+               return azx_rirb_get_response(bus, addr);
 }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -1250,10 +1293,12 @@ static int probe_codec(struct azx *chip, int addr)
                (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
        unsigned int res;
 
+       mutex_lock(&chip->bus->cmd_mutex);
        chip->probing = 1;
        azx_send_cmd(chip->bus, cmd);
-       res = azx_get_response(chip->bus);
+       res = azx_get_response(chip->bus, addr);
        chip->probing = 0;
+       mutex_unlock(&chip->bus->cmd_mutex);
        if (res == -1)
                return -EIO;
        snd_printdd(SFX "codec #%d probed OK\n", addr);
@@ -2254,6 +2299,30 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
        }
 }
 
+/*
+ * white-list for enable_msi
+ */
+static struct snd_pci_quirk msi_white_list[] __devinitdata = {
+       SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1),
+       {}
+};
+
+static void __devinit check_msi(struct azx *chip)
+{
+       const struct snd_pci_quirk *q;
+
+       chip->msi = enable_msi;
+       if (chip->msi)
+               return;
+       q = snd_pci_quirk_lookup(chip->pci, msi_white_list);
+       if (q) {
+               printk(KERN_INFO
+                      "hda_intel: msi for device %04x:%04x set to %d\n",
+                      q->subvendor, q->subdevice, q->value);
+               chip->msi = q->value;
+       }
+}
+
 
 /*
  * constructor
@@ -2288,7 +2357,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
        chip->pci = pci;
        chip->irq = -1;
        chip->driver_type = driver_type;
-       chip->msi = enable_msi;
+       check_msi(chip);
        chip->dev_index = dev;
        INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
 
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
new file mode 100644 (file)
index 0000000..8ba3068
--- /dev/null
@@ -0,0 +1,1194 @@
+/*
+ * HD audio interface patch for Cirrus Logic CS420x chip
+ *
+ * Copyright (c) 2009 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 <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+/*
+ */
+
+struct cs_spec {
+       int board_config;
+       struct auto_pin_cfg autocfg;
+       struct hda_multi_out multiout;
+       struct snd_kcontrol *vmaster_sw;
+       struct snd_kcontrol *vmaster_vol;
+
+       hda_nid_t dac_nid[AUTO_CFG_MAX_OUTS];
+       hda_nid_t slave_dig_outs[2];
+
+       unsigned int input_idx[AUTO_PIN_LAST];
+       unsigned int capsrc_idx[AUTO_PIN_LAST];
+       hda_nid_t adc_nid[AUTO_PIN_LAST];
+       unsigned int adc_idx[AUTO_PIN_LAST];
+       unsigned int num_inputs;
+       unsigned int cur_input;
+       unsigned int automic_idx;
+       hda_nid_t cur_adc;
+       unsigned int cur_adc_stream_tag;
+       unsigned int cur_adc_format;
+       hda_nid_t dig_in;
+
+       struct hda_bind_ctls *capture_bind[2];
+
+       unsigned int gpio_mask;
+       unsigned int gpio_dir;
+       unsigned int gpio_data;
+
+       struct hda_pcm pcm_rec[2];      /* PCM information */
+
+       unsigned int hp_detect:1;
+       unsigned int mic_detect:1;
+};
+
+/* available models */
+enum {
+       CS420X_MBP55,
+       CS420X_AUTO,
+       CS420X_MODELS
+};
+
+/* Vendor-specific processing widget */
+#define CS420X_VENDOR_NID      0x11
+#define CS_DIG_OUT1_PIN_NID    0x10
+#define CS_DIG_OUT2_PIN_NID    0x15
+#define CS_DMIC1_PIN_NID       0x12
+#define CS_DMIC2_PIN_NID       0x0e
+
+/* coef indices */
+#define IDX_SPDIF_STAT         0x0000
+#define IDX_SPDIF_CTL          0x0001
+#define IDX_ADC_CFG            0x0002
+/* SZC bitmask, 4 modes below:
+ * 0 = immediate,
+ * 1 = digital immediate, analog zero-cross
+ * 2 = digtail & analog soft-ramp
+ * 3 = digital soft-ramp, analog zero-cross
+ */
+#define   CS_COEF_ADC_SZC_MASK         (3 << 0)
+#define   CS_COEF_ADC_MIC_SZC_MODE     (3 << 0) /* SZC setup for mic */
+#define   CS_COEF_ADC_LI_SZC_MODE      (3 << 0) /* SZC setup for line-in */
+/* PGA mode: 0 = differential, 1 = signle-ended */
+#define   CS_COEF_ADC_MIC_PGA_MODE     (1 << 5) /* PGA setup for mic */
+#define   CS_COEF_ADC_LI_PGA_MODE      (1 << 6) /* PGA setup for line-in */
+#define IDX_DAC_CFG            0x0003
+/* SZC bitmask, 4 modes below:
+ * 0 = Immediate
+ * 1 = zero-cross
+ * 2 = soft-ramp
+ * 3 = soft-ramp on zero-cross
+ */
+#define   CS_COEF_DAC_HP_SZC_MODE      (3 << 0) /* nid 0x02 */
+#define   CS_COEF_DAC_LO_SZC_MODE      (3 << 2) /* nid 0x03 */
+#define   CS_COEF_DAC_SPK_SZC_MODE     (3 << 4) /* nid 0x04 */
+
+#define IDX_BEEP_CFG           0x0004
+/* 0x0008 - test reg key */
+/* 0x0009 - 0x0014 -> 12 test regs */
+/* 0x0015 - visibility reg */
+
+
+static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
+{
+       snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+                           AC_VERB_SET_COEF_INDEX, idx);
+       return snd_hda_codec_read(codec, CS420X_VENDOR_NID, 0,
+                                 AC_VERB_GET_PROC_COEF, 0);
+}
+
+static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
+                                     unsigned int coef)
+{
+       snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+                           AC_VERB_SET_COEF_INDEX, idx);
+       snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+                           AC_VERB_SET_PROC_COEF, coef);
+}
+
+
+#define HP_EVENT       1
+#define MIC_EVENT      2
+
+/*
+ * PCM callbacks
+ */
+static int cs_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                               struct hda_codec *codec,
+                               struct snd_pcm_substream *substream)
+{
+       struct cs_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+                                            hinfo);
+}
+
+static int cs_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 cs_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+                                               stream_tag, format, substream);
+}
+
+static int cs_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                  struct hda_codec *codec,
+                                  struct snd_pcm_substream *substream)
+{
+       struct cs_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int cs_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                   struct hda_codec *codec,
+                                   struct snd_pcm_substream *substream)
+{
+       struct cs_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int cs_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+                                    struct hda_codec *codec,
+                                    struct snd_pcm_substream *substream)
+{
+       struct cs_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int cs_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 cs_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+                                            format, substream);
+}
+
+static int cs_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      struct snd_pcm_substream *substream)
+{
+       struct cs_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int cs_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 cs_spec *spec = codec->spec;
+       spec->cur_adc = spec->adc_nid[spec->cur_input];
+       spec->cur_adc_stream_tag = stream_tag;
+       spec->cur_adc_format = format;
+       snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+       return 0;
+}
+
+static int cs_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                 struct hda_codec *codec,
+                                 struct snd_pcm_substream *substream)
+{
+       struct cs_spec *spec = codec->spec;
+       snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+       spec->cur_adc = 0;
+       return 0;
+}
+
+/*
+ */
+static struct hda_pcm_stream cs_pcm_analog_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .ops = {
+               .open = cs_playback_pcm_open,
+               .prepare = cs_playback_pcm_prepare,
+               .cleanup = cs_playback_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream cs_pcm_analog_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .ops = {
+               .prepare = cs_capture_pcm_prepare,
+               .cleanup = cs_capture_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream cs_pcm_digital_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .ops = {
+               .open = cs_dig_playback_pcm_open,
+               .close = cs_dig_playback_pcm_close,
+               .prepare = cs_dig_playback_pcm_prepare,
+               .cleanup = cs_dig_playback_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream cs_pcm_digital_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+};
+
+static int cs_build_pcms(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       struct hda_pcm *info = spec->pcm_rec;
+
+       codec->pcm_info = info;
+       codec->num_pcms = 0;
+
+       info->name = "Cirrus Analog";
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cs_pcm_analog_playback;
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dac_nid[0];
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+               spec->multiout.max_channels;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE] = cs_pcm_analog_capture;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
+               spec->adc_nid[spec->cur_input];
+       codec->num_pcms++;
+
+       if (!spec->multiout.dig_out_nid && !spec->dig_in)
+               return 0;
+
+       info++;
+       info->name = "Cirrus Digital";
+       info->pcm_type = spec->autocfg.dig_out_type[0];
+       if (!info->pcm_type)
+               info->pcm_type = HDA_PCM_TYPE_SPDIF;
+       if (spec->multiout.dig_out_nid) {
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+                       cs_pcm_digital_playback;
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+                       spec->multiout.dig_out_nid;
+       }
+       if (spec->dig_in) {
+               info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+                       cs_pcm_digital_capture;
+               info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+       }
+       codec->num_pcms++;
+
+       return 0;
+}
+
+/*
+ * parse codec topology
+ */
+
+static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin)
+{
+       hda_nid_t dac;
+       if (!pin)
+               return 0;
+       if (snd_hda_get_connections(codec, pin, &dac, 1) != 1)
+               return 0;
+       return dac;
+}
+
+static int is_ext_mic(struct hda_codec *codec, unsigned int idx)
+{
+       struct cs_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       hda_nid_t pin = cfg->input_pins[idx];
+       unsigned int val = snd_hda_query_pin_caps(codec, pin);
+       if (!(val & AC_PINCAP_PRES_DETECT))
+               return 0;
+       val = snd_hda_codec_get_pincfg(codec, pin);
+       return (get_defcfg_connect(val) == AC_JACK_PORT_COMPLEX);
+}
+
+static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
+                        unsigned int *idxp)
+{
+       int i;
+       hda_nid_t nid;
+
+       nid = codec->start_nid;
+       for (i = 0; i < codec->num_nodes; i++, nid++) {
+               hda_nid_t pins[2];
+               unsigned int type;
+               int j, nums;
+               type = (get_wcaps(codec, nid) & AC_WCAP_TYPE)
+                       >> AC_WCAP_TYPE_SHIFT;
+               if (type != AC_WID_AUD_IN)
+                       continue;
+               nums = snd_hda_get_connections(codec, nid, pins,
+                                              ARRAY_SIZE(pins));
+               if (nums <= 0)
+                       continue;
+               for (j = 0; j < nums; j++) {
+                       if (pins[j] == pin) {
+                               *idxp = j;
+                               return nid;
+                       }
+               }
+       }
+       return 0;
+}
+
+static int is_active_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int val;
+       val = snd_hda_codec_get_pincfg(codec, nid);
+       return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
+}
+
+static int parse_output(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i, extra_nids;
+       hda_nid_t dac;
+
+       for (i = 0; i < cfg->line_outs; i++) {
+               dac = get_dac(codec, cfg->line_out_pins[i]);
+               if (!dac)
+                       break;
+               spec->dac_nid[i] = dac;
+       }
+       spec->multiout.num_dacs = i;
+       spec->multiout.dac_nids = spec->dac_nid;
+       spec->multiout.max_channels = i * 2;
+
+       /* add HP and speakers */
+       extra_nids = 0;
+       for (i = 0; i < cfg->hp_outs; i++) {
+               dac = get_dac(codec, cfg->hp_pins[i]);
+               if (!dac)
+                       break;
+               if (!i)
+                       spec->multiout.hp_nid = dac;
+               else
+                       spec->multiout.extra_out_nid[extra_nids++] = dac;
+       }
+       for (i = 0; i < cfg->speaker_outs; i++) {
+               dac = get_dac(codec, cfg->speaker_pins[i]);
+               if (!dac)
+                       break;
+               spec->multiout.extra_out_nid[extra_nids++] = dac;
+       }
+
+       if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+               cfg->speaker_outs = cfg->line_outs;
+               memcpy(cfg->speaker_pins, cfg->line_out_pins,
+                      sizeof(cfg->speaker_pins));
+               cfg->line_outs = 0;
+       }
+
+       return 0;
+}
+
+static int parse_input(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
+
+       for (i = 0; i < AUTO_PIN_LAST; i++) {
+               hda_nid_t pin = cfg->input_pins[i];
+               if (!pin)
+                       continue;
+               spec->input_idx[spec->num_inputs] = i;
+               spec->capsrc_idx[i] = spec->num_inputs++;
+               spec->cur_input = i;
+               spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]);
+       }
+       if (!spec->num_inputs)
+               return 0;
+
+       /* check whether the automatic mic switch is available */
+       if (spec->num_inputs == 2 &&
+           spec->adc_nid[AUTO_PIN_MIC] && spec->adc_nid[AUTO_PIN_FRONT_MIC]) {
+               if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_FRONT_MIC])) {
+                       if (!is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+                               spec->mic_detect = 1;
+                               spec->automic_idx = AUTO_PIN_FRONT_MIC;
+                       }
+               } else {
+                       if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+                               spec->mic_detect = 1;
+                               spec->automic_idx = AUTO_PIN_MIC;
+                       }
+               }
+       }
+       return 0;
+}
+
+
+static int parse_digital_output(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       hda_nid_t nid;
+
+       if (!cfg->dig_outs)
+               return 0;
+       if (snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) < 1)
+               return 0;
+       spec->multiout.dig_out_nid = nid;
+       spec->multiout.share_spdif = 1;
+       if (cfg->dig_outs > 1 &&
+           snd_hda_get_connections(codec, cfg->dig_out_pins[1], &nid, 1) > 0) {
+               spec->slave_dig_outs[0] = nid;
+               codec->slave_dig_outs = spec->slave_dig_outs;
+       }
+       return 0;
+}
+
+static int parse_digital_input(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int idx;
+
+       if (cfg->dig_in_pin)
+               spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx);
+       return 0;
+}
+
+/*
+ * create mixer controls
+ */
+
+static const char *dir_sfx[2] = { "Playback", "Capture" };
+
+static int add_mute(struct hda_codec *codec, const char *name, int index,
+                   unsigned int pval, int dir, struct snd_kcontrol **kctlp)
+{
+       char tmp[44];
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_MUTE_IDX(tmp, index, 0, 0, HDA_OUTPUT);
+       knew.private_value = pval;
+       snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]);
+       *kctlp = snd_ctl_new1(&knew, codec);
+       return snd_hda_ctl_add(codec, *kctlp);
+}
+
+static int add_volume(struct hda_codec *codec, const char *name,
+                     int index, unsigned int pval, int dir,
+                     struct snd_kcontrol **kctlp)
+{
+       char tmp[32];
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_VOLUME_IDX(tmp, index, 0, 0, HDA_OUTPUT);
+       knew.private_value = pval;
+       snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]);
+       *kctlp = snd_ctl_new1(&knew, codec);
+       return snd_hda_ctl_add(codec, *kctlp);
+}
+
+static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
+{
+       unsigned int caps;
+
+       /* set the upper-limit for mixer amp to 0dB */
+       caps = query_amp_caps(codec, dac, HDA_OUTPUT);
+       caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT);
+       caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f)
+               << AC_AMPCAP_NUM_STEPS_SHIFT;
+       snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps);
+}
+
+static int add_vmaster(struct hda_codec *codec, hda_nid_t dac)
+{
+       struct cs_spec *spec = codec->spec;
+       unsigned int tlv[4];
+       int err;
+
+       spec->vmaster_sw =
+               snd_ctl_make_virtual_master("Master Playback Switch", NULL);
+       err = snd_hda_ctl_add(codec, spec->vmaster_sw);
+       if (err < 0)
+               return err;
+
+       snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv);
+       spec->vmaster_vol =
+               snd_ctl_make_virtual_master("Master Playback Volume", tlv);
+       err = snd_hda_ctl_add(codec, spec->vmaster_vol);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+static int add_output(struct hda_codec *codec, hda_nid_t dac, int idx,
+                     int num_ctls, int type)
+{
+       struct cs_spec *spec = codec->spec;
+       const char *name;
+       int err, index;
+       struct snd_kcontrol *kctl;
+       static char *speakers[] = {
+               "Front Speaker", "Surround Speaker", "Bass Speaker"
+       };
+       static char *line_outs[] = {
+               "Front Line-Out", "Surround Line-Out", "Bass Line-Out"
+       };
+
+       fix_volume_caps(codec, dac);
+       if (!spec->vmaster_sw) {
+               err = add_vmaster(codec, dac);
+               if (err < 0)
+                       return err;
+       }
+
+       index = 0;
+       switch (type) {
+       case AUTO_PIN_HP_OUT:
+               name = "Headphone";
+               index = idx;
+               break;
+       case AUTO_PIN_SPEAKER_OUT:
+               if (num_ctls > 1)
+                       name = speakers[idx];
+               else
+                       name = "Speaker";
+               break;
+       default:
+               if (num_ctls > 1)
+                       name = line_outs[idx];
+               else
+                       name = "Line-Out";
+               break;
+       }
+
+       err = add_mute(codec, name, index,
+                      HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
+       if (err < 0)
+               return err;
+       err = snd_ctl_add_slave(spec->vmaster_sw, kctl);
+       if (err < 0)
+               return err;
+
+       err = add_volume(codec, name, index,
+                        HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
+       if (err < 0)
+               return err;
+       err = snd_ctl_add_slave(spec->vmaster_vol, kctl);
+       if (err < 0)
+               return err;
+
+       return 0;
+}              
+
+static int build_output(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i, err;
+
+       for (i = 0; i < cfg->line_outs; i++) {
+               err = add_output(codec, get_dac(codec, cfg->line_out_pins[i]),
+                                i, cfg->line_outs, cfg->line_out_type);
+               if (err < 0)
+                       return err;
+       }
+       for (i = 0; i < cfg->hp_outs; i++) {
+               err = add_output(codec, get_dac(codec, cfg->hp_pins[i]),
+                                i, cfg->hp_outs, AUTO_PIN_HP_OUT);
+               if (err < 0)
+                       return err;
+       }
+       for (i = 0; i < cfg->speaker_outs; i++) {
+               err = add_output(codec, get_dac(codec, cfg->speaker_pins[i]),
+                                i, cfg->speaker_outs, AUTO_PIN_SPEAKER_OUT);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+/*
+ */
+
+static struct snd_kcontrol_new cs_capture_ctls[] = {
+       HDA_BIND_SW("Capture Switch", 0),
+       HDA_BIND_VOL("Capture Volume", 0),
+};
+
+static int change_cur_input(struct hda_codec *codec, unsigned int idx,
+                           int force)
+{
+       struct cs_spec *spec = codec->spec;
+       
+       if (spec->cur_input == idx && !force)
+               return 0;
+       if (spec->cur_adc && spec->cur_adc != spec->adc_nid[idx]) {
+               /* stream is running, let's swap the current ADC */
+               snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+               spec->cur_adc = spec->adc_nid[idx];
+               snd_hda_codec_setup_stream(codec, spec->cur_adc,
+                                          spec->cur_adc_stream_tag, 0,
+                                          spec->cur_adc_format);
+       }
+       snd_hda_codec_write(codec, spec->cur_adc, 0,
+                           AC_VERB_SET_CONNECT_SEL,
+                           spec->adc_idx[idx]);
+       spec->cur_input = idx;
+       return 1;
+}
+
+static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_info *uinfo)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct cs_spec *spec = codec->spec;
+       unsigned int idx;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = spec->num_inputs;
+       if (uinfo->value.enumerated.item >= spec->num_inputs)
+               uinfo->value.enumerated.item = spec->num_inputs - 1;
+       idx = spec->input_idx[uinfo->value.enumerated.item];
+       strcpy(uinfo->value.enumerated.name, auto_pin_cfg_labels[idx]);
+       return 0;
+}
+
+static int cs_capture_source_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct cs_spec *spec = codec->spec;
+       ucontrol->value.enumerated.item[0] = spec->capsrc_idx[spec->cur_input];
+       return 0;
+}
+
+static int cs_capture_source_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct cs_spec *spec = codec->spec;
+       unsigned int idx = ucontrol->value.enumerated.item[0];
+
+       if (idx >= spec->num_inputs)
+               return -EINVAL;
+       idx = spec->input_idx[idx];
+       return change_cur_input(codec, idx, 0);
+}
+
+static struct snd_kcontrol_new cs_capture_source = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Capture Source",
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .info = cs_capture_source_info,
+       .get = cs_capture_source_get,
+       .put = cs_capture_source_put,
+};
+
+static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
+                                              struct hda_ctl_ops *ops)
+{
+       struct cs_spec *spec = codec->spec;
+       struct hda_bind_ctls *bind;
+       int i, n;
+
+       bind = kzalloc(sizeof(*bind) + sizeof(long) * (spec->num_inputs + 1),
+                      GFP_KERNEL);
+       if (!bind)
+               return NULL;
+       bind->ops = ops;
+       n = 0;
+       for (i = 0; i < AUTO_PIN_LAST; i++) {
+               if (!spec->adc_nid[i])
+                       continue;
+               bind->values[n++] =
+                       HDA_COMPOSE_AMP_VAL(spec->adc_nid[i], 3,
+                                           spec->adc_idx[i], HDA_INPUT);
+       }
+       return bind;
+}
+
+static int build_input(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       int i, err;
+
+       if (!spec->num_inputs)
+               return 0;
+
+       /* make bind-capture */
+       spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw);
+       spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol);
+       for (i = 0; i < 2; i++) {
+               struct snd_kcontrol *kctl;
+               if (!spec->capture_bind[i])
+                       return -ENOMEM;
+               kctl = snd_ctl_new1(&cs_capture_ctls[i], codec);
+               if (!kctl)
+                       return -ENOMEM;
+               kctl->private_value = (long)spec->capture_bind[i];
+               err = snd_hda_ctl_add(codec, kctl);
+               if (err < 0)
+                       return err;
+       }
+       
+       if (spec->num_inputs > 1 && !spec->mic_detect) {
+               err = snd_hda_ctl_add(codec,
+                                     snd_ctl_new1(&cs_capture_source, codec));
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+/*
+ */
+
+static int build_digital_output(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       int err;
+
+       if (!spec->multiout.dig_out_nid)
+               return 0;
+
+       err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+       if (err < 0)
+               return err;
+       err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+static int build_digital_input(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       if (spec->dig_in)
+               return snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+       return 0;
+}
+
+/*
+ * auto-mute and auto-mic switching
+ */
+
+static void cs_automute(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       unsigned int caps, present, hp_present;
+       hda_nid_t nid;
+       int i;
+
+       hp_present = 0;
+       for (i = 0; i < cfg->hp_outs; i++) {
+               nid = cfg->hp_pins[i];
+               caps = snd_hda_query_pin_caps(codec, nid);
+               if (!(caps & AC_PINCAP_PRES_DETECT))
+                       continue;
+               if (caps & AC_PINCAP_TRIG_REQ)
+                       snd_hda_codec_read(codec, nid, 0,
+                                          AC_VERB_SET_PIN_SENSE, 0);
+               present = snd_hda_codec_read(codec, nid, 0,
+                                            AC_VERB_GET_PIN_SENSE, 0);
+               hp_present |= (present & AC_PINSENSE_PRESENCE) != 0;
+               if (hp_present)
+                       break;
+       }
+       for (i = 0; i < cfg->speaker_outs; i++) {
+               nid = cfg->speaker_pins[i];
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   hp_present ? 0 : PIN_OUT);
+       }
+       if (spec->board_config == CS420X_MBP55) {
+               unsigned int gpio = hp_present ? 0x02 : 0x08;
+               snd_hda_codec_write(codec, 0x01, 0,
+                                   AC_VERB_SET_GPIO_DATA, gpio);
+       }
+}
+
+static void cs_automic(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       hda_nid_t nid;
+       unsigned int caps, present;
+       
+       nid = cfg->input_pins[spec->automic_idx];
+       caps = snd_hda_query_pin_caps(codec, nid);
+       if (caps & AC_PINCAP_TRIG_REQ)
+               snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
+       present = snd_hda_codec_read(codec, nid, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0);
+       if (present & AC_PINSENSE_PRESENCE)
+               change_cur_input(codec, spec->automic_idx, 0);
+       else {
+               unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ?
+                       AUTO_PIN_FRONT_MIC : AUTO_PIN_MIC;
+               change_cur_input(codec, imic, 0);
+       }
+}
+
+/*
+ */
+
+static void init_output(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
+
+       /* mute first */
+       for (i = 0; i < spec->multiout.num_dacs; i++)
+               snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+       if (spec->multiout.hp_nid)
+               snd_hda_codec_write(codec, spec->multiout.hp_nid, 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+       for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
+               if (!spec->multiout.extra_out_nid[i])
+                       break;
+               snd_hda_codec_write(codec, spec->multiout.extra_out_nid[i], 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+       }
+
+       /* set appropriate pin controls */
+       for (i = 0; i < cfg->line_outs; i++)
+               snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+       for (i = 0; i < cfg->hp_outs; i++) {
+               hda_nid_t nid = cfg->hp_pins[i];
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+               if (!cfg->speaker_outs)
+                       continue;
+               if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
+                       snd_hda_codec_write(codec, nid, 0,
+                                           AC_VERB_SET_UNSOLICITED_ENABLE,
+                                           AC_USRSP_EN | HP_EVENT);
+                       spec->hp_detect = 1;
+               }
+       }
+       for (i = 0; i < cfg->speaker_outs; i++)
+               snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+       if (spec->hp_detect)
+               cs_automute(codec);
+}
+
+static void init_input(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       unsigned int coef;
+       int i;
+
+       for (i = 0; i < AUTO_PIN_LAST; i++) {
+               unsigned int ctl;
+               hda_nid_t pin = cfg->input_pins[i];
+               if (!pin || !spec->adc_nid[i])
+                       continue;
+               /* set appropriate pin control and mute first */
+               ctl = PIN_IN;
+               if (i <= AUTO_PIN_FRONT_MIC) {
+                       unsigned int caps = snd_hda_query_pin_caps(codec, pin);
+                       caps >>= AC_PINCAP_VREF_SHIFT;
+                       if (caps & AC_PINCAP_VREF_80)
+                               ctl = PIN_VREF80;
+               }
+               snd_hda_codec_write(codec, pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
+               snd_hda_codec_write(codec, spec->adc_nid[i], 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_IN_MUTE(spec->adc_idx[i]));
+               if (spec->mic_detect && spec->automic_idx == i)
+                       snd_hda_codec_write(codec, pin, 0,
+                                           AC_VERB_SET_UNSOLICITED_ENABLE,
+                                           AC_USRSP_EN | MIC_EVENT);
+       }
+       change_cur_input(codec, spec->cur_input, 1);
+       if (spec->mic_detect)
+               cs_automic(codec);
+
+       coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */
+       if (is_active_pin(codec, CS_DMIC2_PIN_NID))
+               coef |= 0x0500; /* DMIC2 enable 2 channels, disable GPIO1 */
+       if (is_active_pin(codec, CS_DMIC1_PIN_NID))
+               coef |= 0x1800; /* DMIC1 enable 2 channels, disable GPIO0 
+                                * No effect if SPDIF_OUT2 is slected in 
+                                * IDX_SPDIF_CTL.
+                                 */
+       cs_vendor_coef_set(codec, IDX_ADC_CFG, coef);
+}
+
+static struct hda_verb cs_coef_init_verbs[] = {
+       {0x11, AC_VERB_SET_PROC_STATE, 1},
+       {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
+       {0x11, AC_VERB_SET_PROC_COEF,
+        (0x002a /* DAC1/2/3 SZCMode Soft Ramp */
+         | 0x0040 /* Mute DACs on FIFO error */
+         | 0x1000 /* Enable DACs High Pass Filter */
+         | 0x0400 /* Disable Coefficient Auto increment */
+         )},
+       /* Beep */
+       {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
+       {0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */
+
+       {} /* terminator */
+};
+
+/* SPDIF setup */
+static void init_digital(struct hda_codec *codec)
+{
+       unsigned int coef;
+
+       coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */
+       coef |= 0x0008; /* Replace with mute on error */
+       if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID))
+               coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2
+                                * SPDIF_OUT2 is shared with GPIO1 and
+                                * DMIC_SDA2.
+                                */
+       cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef);
+}
+
+static int cs_init(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+
+       snd_hda_sequence_write(codec, cs_coef_init_verbs);
+
+       if (spec->gpio_mask) {
+               snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
+                                   spec->gpio_mask);
+               snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
+                                   spec->gpio_dir);
+               snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+                                   spec->gpio_data);
+       }
+
+       init_output(codec);
+       init_input(codec);
+       init_digital(codec);
+       return 0;
+}
+
+static int cs_build_controls(struct hda_codec *codec)
+{
+       int err;
+
+       err = build_output(codec);
+       if (err < 0)
+               return err;
+       err = build_input(codec);
+       if (err < 0)
+               return err;
+       err = build_digital_output(codec);
+       if (err < 0)
+               return err;
+       err = build_digital_input(codec);
+       if (err < 0)
+               return err;
+       return cs_init(codec);
+}
+
+static void cs_free(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       kfree(spec->capture_bind[0]);
+       kfree(spec->capture_bind[1]);
+       kfree(codec->spec);
+}
+
+static void cs_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       switch ((res >> 26) & 0x7f) {
+       case HP_EVENT:
+               cs_automute(codec);
+               break;
+       case MIC_EVENT:
+               cs_automic(codec);
+               break;
+       }
+}
+
+static struct hda_codec_ops cs_patch_ops = {
+       .build_controls = cs_build_controls,
+       .build_pcms = cs_build_pcms,
+       .init = cs_init,
+       .free = cs_free,
+       .unsol_event = cs_unsol_event,
+};
+
+static int cs_parse_auto_config(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       int err;
+
+       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+       if (err < 0)
+               return err;
+
+       err = parse_output(codec);
+       if (err < 0)
+               return err;
+       err = parse_input(codec);
+       if (err < 0)
+               return err;
+       err = parse_digital_output(codec);
+       if (err < 0)
+               return err;
+       err = parse_digital_input(codec);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+static const char *cs420x_models[CS420X_MODELS] = {
+       [CS420X_MBP55] = "mbp55",
+       [CS420X_AUTO] = "auto",
+};
+
+
+static struct snd_pci_quirk cs420x_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
+       {} /* terminator */
+};
+
+struct cs_pincfg {
+       hda_nid_t nid;
+       u32 val;
+};
+
+static struct cs_pincfg mbp55_pincfgs[] = {
+       { 0x09, 0x012b4030 },
+       { 0x0a, 0x90100121 },
+       { 0x0b, 0x90100120 },
+       { 0x0c, 0x400000f0 },
+       { 0x0d, 0x90a00110 },
+       { 0x0e, 0x400000f0 },
+       { 0x0f, 0x400000f0 },
+       { 0x10, 0x014be040 },
+       { 0x12, 0x400000f0 },
+       { 0x15, 0x400000f0 },
+       {} /* terminator */
+};
+
+static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
+       [CS420X_MBP55] = mbp55_pincfgs,
+};
+
+static void fix_pincfg(struct hda_codec *codec, int model)
+{
+       const struct cs_pincfg *cfg = cs_pincfgs[model];
+       if (!cfg)
+               return;
+       for (; cfg->nid; cfg++)
+               snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
+}
+
+
+static int patch_cs420x(struct hda_codec *codec)
+{
+       struct cs_spec *spec;
+       int err;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       codec->spec = spec;
+
+       spec->board_config =
+               snd_hda_check_board_config(codec, CS420X_MODELS,
+                                          cs420x_models, cs420x_cfg_tbl);
+       if (spec->board_config >= 0)
+               fix_pincfg(codec, spec->board_config);
+
+       switch (spec->board_config) {
+       case CS420X_MBP55:
+               /* GPIO1 = headphones */
+               /* GPIO3 = speakers */
+               spec->gpio_mask = 0x0a;
+               spec->gpio_dir = 0x0a;
+               break;
+       }
+
+       err = cs_parse_auto_config(codec);
+       if (err < 0)
+               goto error;
+
+       codec->patch_ops = cs_patch_ops;
+
+       return 0;
+
+ error:
+       kfree(codec->spec);
+       codec->spec = NULL;
+       return err;
+}
+
+
+/*
+ * patch entries
+ */
+static struct hda_codec_preset snd_hda_preset_cirrus[] = {
+       { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
+       { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
+       {} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:10134206");
+MODULE_ALIAS("snd-hda-codec-id:10134207");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
+
+static struct hda_codec_preset_list cirrus_list = {
+       .preset = snd_hda_preset_cirrus,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_cirrus_init(void)
+{
+       return snd_hda_add_codec_preset(&cirrus_list);
+}
+
+static void __exit patch_cirrus_exit(void)
+{
+       snd_hda_delete_codec_preset(&cirrus_list);
+}
+
+module_init(patch_cirrus_init)
+module_exit(patch_cirrus_exit)
index 032850e..01a18ed 100644 (file)
@@ -33,8 +33,8 @@
 #include "hda_codec.h"
 #include "hda_local.h"
 
-#define CVT_NID                0x02    /* audio converter */
-#define PIN_NID                0x03    /* HDMI output pin */
+static hda_nid_t cvt_nid;      /* audio converter */
+static hda_nid_t pin_nid;      /* HDMI output pin */
 
 #define INTEL_HDMI_EVENT_TAG           0x08
 
@@ -44,30 +44,6 @@ struct intel_hdmi_spec {
        struct hdmi_eld sink_eld;
 };
 
-static struct hda_verb pinout_enable_verb[] = {
-       {PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {} /* terminator */
-};
-
-static struct hda_verb unsolicited_response_verb[] = {
-       {PIN_NID, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN |
-                                                 INTEL_HDMI_EVENT_TAG},
-       {}
-};
-
-static struct hda_verb def_chan_map[] = {
-       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x00},
-       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x11},
-       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x22},
-       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x33},
-       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x44},
-       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x55},
-       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x66},
-       {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x77},
-       {}
-};
-
-
 struct hdmi_audio_infoframe {
        u8 type; /* 0x84 */
        u8 ver;  /* 0x01 */
@@ -244,11 +220,12 @@ static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid,
 static void hdmi_enable_output(struct hda_codec *codec)
 {
        /* Unmute */
-       if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
-               snd_hda_codec_write(codec, PIN_NID, 0,
+       if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
+               snd_hda_codec_write(codec, pin_nid, 0,
                                AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
        /* Enable pin out */
-       snd_hda_sequence_write(codec, pinout_enable_verb);
+       snd_hda_codec_write(codec, pin_nid, 0,
+                           AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
 }
 
 /*
@@ -256,8 +233,8 @@ static void hdmi_enable_output(struct hda_codec *codec)
  */
 static void hdmi_start_infoframe_trans(struct hda_codec *codec)
 {
-       hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
-       snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+       hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+       snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
                                                AC_DIPXMIT_BEST);
 }
 
@@ -266,20 +243,20 @@ static void hdmi_start_infoframe_trans(struct hda_codec *codec)
  */
 static void hdmi_stop_infoframe_trans(struct hda_codec *codec)
 {
-       hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
-       snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+       hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+       snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
                                                AC_DIPXMIT_DISABLE);
 }
 
 static int hdmi_get_channel_count(struct hda_codec *codec)
 {
-       return 1 + snd_hda_codec_read(codec, CVT_NID, 0,
+       return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
                                        AC_VERB_GET_CVT_CHAN_COUNT, 0);
 }
 
 static void hdmi_set_channel_count(struct hda_codec *codec, int chs)
 {
-       snd_hda_codec_write(codec, CVT_NID, 0,
+       snd_hda_codec_write(codec, cvt_nid, 0,
                                        AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
 
        if (chs != hdmi_get_channel_count(codec))
@@ -294,7 +271,7 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec)
        int slot;
 
        for (i = 0; i < 8; i++) {
-               slot = snd_hda_codec_read(codec, CVT_NID, 0,
+               slot = snd_hda_codec_read(codec, cvt_nid, 0,
                                                AC_VERB_GET_HDMI_CHAN_SLOT, i);
                printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
                                                slot >> 4, slot & 0x7);
@@ -307,7 +284,7 @@ static void hdmi_parse_eld(struct hda_codec *codec)
        struct intel_hdmi_spec *spec = codec->spec;
        struct hdmi_eld *eld = &spec->sink_eld;
 
-       if (!snd_hdmi_get_eld(eld, codec, PIN_NID))
+       if (!snd_hdmi_get_eld(eld, codec, pin_nid))
                snd_hdmi_show_eld(eld);
 }
 
@@ -322,11 +299,11 @@ static void hdmi_debug_dip_size(struct hda_codec *codec)
        int i;
        int size;
 
-       size = snd_hdmi_get_eld_size(codec, PIN_NID);
+       size = snd_hdmi_get_eld_size(codec, pin_nid);
        printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
 
        for (i = 0; i < 8; i++) {
-               size = snd_hda_codec_read(codec, PIN_NID, 0,
+               size = snd_hda_codec_read(codec, pin_nid, 0,
                                                AC_VERB_GET_HDMI_DIP_SIZE, i);
                printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
        }
@@ -340,15 +317,15 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec)
        int size;
        int pi, bi;
        for (i = 0; i < 8; i++) {
-               size = snd_hda_codec_read(codec, PIN_NID, 0,
+               size = snd_hda_codec_read(codec, pin_nid, 0,
                                                AC_VERB_GET_HDMI_DIP_SIZE, i);
                if (size == 0)
                        continue;
 
-               hdmi_set_dip_index(codec, PIN_NID, i, 0x0);
+               hdmi_set_dip_index(codec, pin_nid, i, 0x0);
                for (j = 1; j < 1000; j++) {
-                       hdmi_write_dip_byte(codec, PIN_NID, 0x0);
-                       hdmi_get_dip_index(codec, PIN_NID, &pi, &bi);
+                       hdmi_write_dip_byte(codec, pin_nid, 0x0);
+                       hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
                        if (pi != i)
                                snd_printd(KERN_INFO "dip index %d: %d != %d\n",
                                                bi, pi, i);
@@ -376,9 +353,9 @@ static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
                sum += params[i];
        ai->checksum = - sum;
 
-       hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
+       hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
        for (i = 0; i < sizeof(ai); i++)
-               hdmi_write_dip_byte(codec, PIN_NID, params[i]);
+               hdmi_write_dip_byte(codec, pin_nid, params[i]);
 }
 
 /*
@@ -465,6 +442,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
 static void hdmi_setup_channel_mapping(struct hda_codec *codec,
                                        struct hdmi_audio_infoframe *ai)
 {
+       int i;
+
        if (!ai->CA)
                return;
 
@@ -473,7 +452,11 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec,
         * ALSA sequence is front/surr/clfe/side?
         */
 
-       snd_hda_sequence_write(codec, def_chan_map);
+       for (i = 0; i < 8; i++)
+               snd_hda_codec_write(codec, cvt_nid, 0,
+                                   AC_VERB_SET_HDMI_CHAN_SLOT,
+                                   (i << 4) | i);
+
        hdmi_debug_channel_mapping(codec);
 }
 
@@ -597,7 +580,6 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = {
        .substreams = 1,
        .channels_min = 2,
        .channels_max = 8,
-       .nid = CVT_NID, /* NID to query formats and rates and setup streams */
        .ops = {
                .open    = intel_hdmi_playback_pcm_open,
                .close   = intel_hdmi_playback_pcm_close,
@@ -613,6 +595,9 @@ static int intel_hdmi_build_pcms(struct hda_codec *codec)
        codec->num_pcms = 1;
        codec->pcm_info = info;
 
+       /* NID to query formats and rates and setup streams */
+       intel_hdmi_pcm_playback.nid = cvt_nid;
+
        info->name = "INTEL HDMI";
        info->pcm_type = HDA_PCM_TYPE_HDMI;
        info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
@@ -636,8 +621,9 @@ static int intel_hdmi_init(struct hda_codec *codec)
 {
        hdmi_enable_output(codec);
 
-       snd_hda_sequence_write(codec, unsolicited_response_verb);
-
+       snd_hda_codec_write(codec, pin_nid, 0,
+                           AC_VERB_SET_UNSOLICITED_ENABLE,
+                           AC_USRSP_EN | INTEL_HDMI_EVENT_TAG);
        return 0;
 }
 
@@ -657,7 +643,7 @@ static struct hda_codec_ops intel_hdmi_patch_ops = {
        .unsol_event            = intel_hdmi_unsol_event,
 };
 
-static int patch_intel_hdmi(struct hda_codec *codec)
+static int do_patch_intel_hdmi(struct hda_codec *codec)
 {
        struct intel_hdmi_spec *spec;
 
@@ -667,7 +653,7 @@ static int patch_intel_hdmi(struct hda_codec *codec)
 
        spec->multiout.num_dacs = 0;      /* no analog */
        spec->multiout.max_channels = 8;
-       spec->multiout.dig_out_nid = CVT_NID;
+       spec->multiout.dig_out_nid = cvt_nid;
 
        codec->spec = spec;
        codec->patch_ops = intel_hdmi_patch_ops;
@@ -679,13 +665,27 @@ static int patch_intel_hdmi(struct hda_codec *codec)
        return 0;
 }
 
+static int patch_intel_hdmi(struct hda_codec *codec)
+{
+       cvt_nid = 0x02;
+       pin_nid = 0x03;
+       return do_patch_intel_hdmi(codec);
+}
+
+static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec)
+{
+       cvt_nid = 0x02;
+       pin_nid = 0x04;
+       return do_patch_intel_hdmi(codec);
+}
+
 static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
        { .id = 0x808629fb, .name = "G45 DEVCL",  .patch = patch_intel_hdmi },
        { .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
        { .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
        { .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
        { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi },
-       { .id = 0x80860054, .name = "G45 DEVIBX", .patch = patch_intel_hdmi },
+       { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak },
        { .id = 0x10951392, .name = "SiI1392 HDMI",     .patch = patch_intel_hdmi },
        {} /* terminator */
 };
index 587d94f..7ff2934 100644 (file)
@@ -258,6 +258,14 @@ enum {
        ALC_INIT_GPIO3,
 };
 
+struct alc_mic_route {
+       hda_nid_t pin;
+       unsigned char mux_idx;
+       unsigned char amix_idx;
+};
+
+#define MUX_IDX_UNDEF  ((unsigned char)-1)
+
 struct alc_spec {
        /* codec parameterization */
        struct snd_kcontrol_new *mixers[5];     /* mixer arrays */
@@ -271,13 +279,13 @@ struct alc_spec {
                                                 */
        unsigned int num_init_verbs;
 
-       char stream_name_analog[16];    /* analog PCM stream */
+       char stream_name_analog[32];    /* analog PCM stream */
        struct hda_pcm_stream *stream_analog_playback;
        struct hda_pcm_stream *stream_analog_capture;
        struct hda_pcm_stream *stream_analog_alt_playback;
        struct hda_pcm_stream *stream_analog_alt_capture;
 
-       char stream_name_digital[16];   /* digital PCM stream */
+       char stream_name_digital[32];   /* digital PCM stream */
        struct hda_pcm_stream *stream_digital_playback;
        struct hda_pcm_stream *stream_digital_capture;
 
@@ -300,6 +308,8 @@ struct alc_spec {
        unsigned int num_mux_defs;
        const struct hda_input_mux *input_mux;
        unsigned int cur_mux[3];
+       struct alc_mic_route ext_mic;
+       struct alc_mic_route int_mic;
 
        /* channel model */
        const struct hda_channel_mode *channel_mode;
@@ -327,6 +337,7 @@ struct alc_spec {
        unsigned int sense_updated: 1;
        unsigned int jack_present: 1;
        unsigned int master_sw: 1;
+       unsigned int auto_mic:1;
 
        /* other flags */
        unsigned int no_analog :1; /* digital I/O only */
@@ -368,6 +379,7 @@ struct alc_config_preset {
        unsigned int num_mux_defs;
        const struct hda_input_mux *input_mux;
        void (*unsol_event)(struct hda_codec *, unsigned int);
+       void (*setup)(struct hda_codec *);
        void (*init_hook)(struct hda_codec *);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        struct hda_amp_list *loopbacks;
@@ -557,7 +569,7 @@ static int alc_pin_mode_get(struct snd_kcontrol *kcontrol,
 
        /* Find enumerated value for current pinctl setting */
        i = alc_pin_mode_min(dir);
-       while (alc_pin_mode_values[i] != pinctl && i <= alc_pin_mode_max(dir))
+       while (i <= alc_pin_mode_max(dir) && alc_pin_mode_values[i] != pinctl)
                i++;
        *valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir);
        return 0;
@@ -840,9 +852,10 @@ static void print_realtek_coef(struct snd_info_buffer *buffer,
 /*
  * set up from the preset table
  */
-static void setup_preset(struct alc_spec *spec,
+static void setup_preset(struct hda_codec *codec,
                         const struct alc_config_preset *preset)
 {
+       struct alc_spec *spec = codec->spec;
        int i;
 
        for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
@@ -884,6 +897,9 @@ static void setup_preset(struct alc_spec *spec,
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        spec->loopback.amplist = preset->loopbacks;
 #endif
+
+       if (preset->setup)
+               preset->setup(codec);
 }
 
 /* Enable GPIO mask and set output */
@@ -963,30 +979,64 @@ static void alc_automute_pin(struct hda_codec *codec)
        }
 }
 
-#if 0 /* it's broken in some cases -- temporarily disabled */
+static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
+                               hda_nid_t nid)
+{
+       hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+       int i, nums;
+
+       nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+       for (i = 0; i < nums; i++)
+               if (conn[i] == nid)
+                       return i;
+       return -1;
+}
+
 static void alc_mic_automute(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int present;
-       unsigned int mic_nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
-       unsigned int fmic_nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
-       unsigned int mix_nid = spec->capsrc_nids[0];
-       unsigned int capsrc_idx_mic, capsrc_idx_fmic;
-
-       capsrc_idx_mic = mic_nid - 0x18;
-       capsrc_idx_fmic = fmic_nid - 0x18;
-       present = snd_hda_codec_read(codec, mic_nid, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                   0x7000 | (capsrc_idx_mic << 8) | (present ? 0 : 0x80));
-       snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                   0x7000 | (capsrc_idx_fmic << 8) | (present ? 0x80 : 0));
-       snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, capsrc_idx_fmic,
-                        HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+       struct alc_mic_route *dead, *alive;
+       unsigned int present, type;
+       hda_nid_t cap_nid;
+
+       if (!spec->auto_mic)
+               return;
+       if (!spec->int_mic.pin || !spec->ext_mic.pin)
+               return;
+       if (snd_BUG_ON(!spec->adc_nids))
+               return;
+
+       cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
+
+       present = snd_hda_codec_read(codec, spec->ext_mic.pin, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0);
+       present &= AC_PINSENSE_PRESENCE;
+       if (present) {
+               alive = &spec->ext_mic;
+               dead = &spec->int_mic;
+       } else {
+               alive = &spec->int_mic;
+               dead = &spec->ext_mic;
+       }
+
+       type = get_wcaps_type(get_wcaps(codec, cap_nid));
+       if (type == AC_WID_AUD_MIX) {
+               /* Matrix-mixer style (e.g. ALC882) */
+               snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
+                                        alive->mux_idx,
+                                        HDA_AMP_MUTE, 0);
+               snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
+                                        dead->mux_idx,
+                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
+       } else {
+               /* MUX style (e.g. ALC880) */
+               snd_hda_codec_write_cache(codec, cap_nid, 0,
+                                         AC_VERB_SET_CONNECT_SEL,
+                                         alive->mux_idx);
+       }
+
+       /* FIXME: analog mixer */
 }
-#else
-#define alc_mic_automute(codec) do {} while(0) /* NOP */
-#endif /* disabled */
 
 /* unsolicited event for HP jack sensing */
 static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
@@ -1142,6 +1192,55 @@ static void alc_init_auto_hp(struct hda_codec *codec)
        spec->unsol_event = alc_sku_unsol_event;
 }
 
+static void alc_init_auto_mic(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       hda_nid_t fixed, ext;
+       int i;
+
+       /* there must be only two mic inputs exclusively */
+       for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++)
+               if (cfg->input_pins[i])
+                       return;
+
+       fixed = ext = 0;
+       for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) {
+               hda_nid_t nid = cfg->input_pins[i];
+               unsigned int defcfg;
+               if (!nid)
+                       return;
+               defcfg = snd_hda_codec_get_pincfg(codec, nid);
+               switch (get_defcfg_connect(defcfg)) {
+               case AC_JACK_PORT_FIXED:
+                       if (fixed)
+                               return; /* already occupied */
+                       fixed = nid;
+                       break;
+               case AC_JACK_PORT_COMPLEX:
+                       if (ext)
+                               return; /* already occupied */
+                       ext = nid;
+                       break;
+               default:
+                       return; /* invalid entry */
+               }
+       }
+       if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
+               return; /* no unsol support */
+       snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x\n",
+                   ext, fixed);
+       spec->ext_mic.pin = ext;
+       spec->int_mic.pin = fixed;
+       spec->ext_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
+       spec->int_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
+       spec->auto_mic = 1;
+       snd_hda_codec_write_cache(codec, spec->ext_mic.pin, 0,
+                                 AC_VERB_SET_UNSOLICITED_ENABLE,
+                                 AC_USRSP_EN | ALC880_MIC_EVENT);
+       spec->unsol_event = alc_sku_unsol_event;
+}
+
 /* check subsystem ID and set up device-specific initialization;
  * return 1 if initialized, 0 if invalid SSID
  */
@@ -1243,6 +1342,7 @@ do_sku:
        }
 
        alc_init_auto_hp(codec);
+       alc_init_auto_mic(codec);
        return 1;
 }
 
@@ -1255,6 +1355,7 @@ static void alc_ssid_check(struct hda_codec *codec,
                           "Enable default setup for auto mode as fallback\n");
                spec->init_amp = ALC_INIT_DEFAULT;
                alc_init_auto_hp(codec);
+               alc_init_auto_mic(codec);
        }
 }
 
@@ -1436,7 +1537,7 @@ static void alc_automute_amp_unsol_event(struct hda_codec *codec,
                alc_automute_amp(codec);
 }
 
-static void alc889_automute_init(struct hda_codec *codec)
+static void alc889_automute_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
@@ -1446,16 +1547,15 @@ static void alc889_automute_init(struct hda_codec *codec)
        spec->autocfg.speaker_pins[2] = 0x17;
        spec->autocfg.speaker_pins[3] = 0x19;
        spec->autocfg.speaker_pins[4] = 0x1a;
-       alc_automute_amp(codec);
 }
 
 static void alc889_intel_init_hook(struct hda_codec *codec)
 {
        alc889_coef_init(codec);
-       alc889_automute_init(codec);
+       alc_automute_amp(codec);
 }
 
-static void alc888_fujitsu_xa3530_init_hook(struct hda_codec *codec)
+static void alc888_fujitsu_xa3530_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
@@ -1463,7 +1563,6 @@ static void alc888_fujitsu_xa3530_init_hook(struct hda_codec *codec)
        spec->autocfg.hp_pins[1] = 0x1b; /* hp */
        spec->autocfg.speaker_pins[0] = 0x14; /* speaker */
        spec->autocfg.speaker_pins[1] = 0x15; /* bass */
-       alc_automute_amp(codec);
 }
 
 /*
@@ -1662,16 +1761,15 @@ static struct snd_kcontrol_new alc888_base_mixer[] = {
        { } /* end */
 };
 
-static void alc888_acer_aspire_4930g_init_hook(struct hda_codec *codec)
+static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
-       alc_automute_amp(codec);
 }
 
-static void alc888_acer_aspire_6530g_init_hook(struct hda_codec *codec)
+static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
@@ -1679,10 +1777,9 @@ static void alc888_acer_aspire_6530g_init_hook(struct hda_codec *codec)
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x16;
        spec->autocfg.speaker_pins[2] = 0x17;
-       alc_automute_amp(codec);
 }
 
-static void alc889_acer_aspire_8930g_init_hook(struct hda_codec *codec)
+static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
@@ -1690,7 +1787,6 @@ static void alc889_acer_aspire_8930g_init_hook(struct hda_codec *codec)
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x16;
        spec->autocfg.speaker_pins[2] = 0x1b;
-       alc_automute_amp(codec);
 }
 
 /*
@@ -2670,13 +2766,17 @@ static void alc880_uniwill_mic_automute(struct hda_codec *codec)
        snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
 }
 
-static void alc880_uniwill_init_hook(struct hda_codec *codec)
+static void alc880_uniwill_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x16;
+}
+
+static void alc880_uniwill_init_hook(struct hda_codec *codec)
+{
        alc_automute_amp(codec);
        alc880_uniwill_mic_automute(codec);
 }
@@ -2697,13 +2797,12 @@ static void alc880_uniwill_unsol_event(struct hda_codec *codec,
        }
 }
 
-static void alc880_uniwill_p53_init_hook(struct hda_codec *codec)
+static void alc880_uniwill_p53_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x15;
-       alc_automute_amp(codec);
 }
 
 static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
@@ -2966,13 +3065,12 @@ static struct hda_verb alc880_lg_init_verbs[] = {
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_init_hook(struct hda_codec *codec)
+static void alc880_lg_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x1b;
        spec->autocfg.speaker_pins[0] = 0x17;
-       alc_automute_amp(codec);
 }
 
 /*
@@ -3051,13 +3149,12 @@ static struct hda_verb alc880_lg_lw_init_verbs[] = {
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_lw_init_hook(struct hda_codec *codec)
+static void alc880_lg_lw_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x1b;
        spec->autocfg.speaker_pins[0] = 0x14;
-       alc_automute_amp(codec);
 }
 
 static struct snd_kcontrol_new alc880_medion_rim_mixer[] = {
@@ -3123,13 +3220,12 @@ static void alc880_medion_rim_unsol_event(struct hda_codec *codec,
                alc880_medion_rim_automute(codec);
 }
 
-static void alc880_medion_rim_init_hook(struct hda_codec *codec)
+static void alc880_medion_rim_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x1b;
-       alc880_medion_rim_automute(codec);
 }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -3996,7 +4092,8 @@ static struct alc_config_preset alc880_presets[] = {
                .channel_mode = alc880_2_jack_modes,
                .input_mux = &alc880_f1734_capture_source,
                .unsol_event = alc880_uniwill_p53_unsol_event,
-               .init_hook = alc880_uniwill_p53_init_hook,
+               .setup = alc880_uniwill_p53_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC880_ASUS] = {
                .mixers = { alc880_asus_mixer },
@@ -4073,6 +4170,7 @@ static struct alc_config_preset alc880_presets[] = {
                .need_dac_fix = 1,
                .input_mux = &alc880_capture_source,
                .unsol_event = alc880_uniwill_unsol_event,
+               .setup = alc880_uniwill_setup,
                .init_hook = alc880_uniwill_init_hook,
        },
        [ALC880_UNIWILL_P53] = {
@@ -4085,7 +4183,8 @@ static struct alc_config_preset alc880_presets[] = {
                .channel_mode = alc880_threestack_modes,
                .input_mux = &alc880_capture_source,
                .unsol_event = alc880_uniwill_p53_unsol_event,
-               .init_hook = alc880_uniwill_p53_init_hook,
+               .setup = alc880_uniwill_p53_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC880_FUJITSU] = {
                .mixers = { alc880_fujitsu_mixer },
@@ -4099,7 +4198,8 @@ static struct alc_config_preset alc880_presets[] = {
                .channel_mode = alc880_2_jack_modes,
                .input_mux = &alc880_capture_source,
                .unsol_event = alc880_uniwill_p53_unsol_event,
-               .init_hook = alc880_uniwill_p53_init_hook,
+               .setup = alc880_uniwill_p53_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC880_CLEVO] = {
                .mixers = { alc880_three_stack_mixer },
@@ -4125,7 +4225,8 @@ static struct alc_config_preset alc880_presets[] = {
                .need_dac_fix = 1,
                .input_mux = &alc880_lg_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc880_lg_init_hook,
+               .setup = alc880_lg_setup,
+               .init_hook = alc_automute_amp,
 #ifdef CONFIG_SND_HDA_POWER_SAVE
                .loopbacks = alc880_lg_loopbacks,
 #endif
@@ -4141,7 +4242,8 @@ static struct alc_config_preset alc880_presets[] = {
                .channel_mode = alc880_lg_lw_modes,
                .input_mux = &alc880_lg_lw_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc880_lg_lw_init_hook,
+               .setup = alc880_lg_lw_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC880_MEDION_RIM] = {
                .mixers = { alc880_medion_rim_mixer },
@@ -4155,7 +4257,8 @@ static struct alc_config_preset alc880_presets[] = {
                .channel_mode = alc880_2_jack_modes,
                .input_mux = &alc880_medion_rim_capture_source,
                .unsol_event = alc880_medion_rim_unsol_event,
-               .init_hook = alc880_medion_rim_init_hook,
+               .setup = alc880_medion_rim_setup,
+               .init_hook = alc880_medion_rim_automute,
        },
 #ifdef CONFIG_SND_DEBUG
        [ALC880_TEST] = {
@@ -4560,8 +4663,42 @@ static void alc880_auto_init(struct hda_codec *codec)
                alc_inithook(codec);
 }
 
-static void set_capture_mixer(struct alc_spec *spec)
+/* check the ADC/MUX contains all input pins; some ADC/MUX contains only
+ * one of two digital mic pins, e.g. on ALC272
+ */
+static void fixup_automic_adc(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < spec->num_adc_nids; i++) {
+               hda_nid_t cap = spec->capsrc_nids ?
+                       spec->capsrc_nids[i] : spec->adc_nids[i];
+               int iidx, eidx;
+
+               iidx = get_connection_index(codec, cap, spec->int_mic.pin);
+               if (iidx < 0)
+                       continue;
+               eidx = get_connection_index(codec, cap, spec->ext_mic.pin);
+               if (eidx < 0)
+                       continue;
+               spec->int_mic.mux_idx = iidx;
+               spec->ext_mic.mux_idx = eidx;
+               if (spec->capsrc_nids)
+                       spec->capsrc_nids += i;
+               spec->adc_nids += i;
+               spec->num_adc_nids = 1;
+               return;
+       }
+       snd_printd(KERN_INFO "hda_codec: %s: "
+                  "No ADC/MUX containing both 0x%x and 0x%x pins\n",
+                  codec->chip_name, spec->int_mic.pin, spec->ext_mic.pin);
+       spec->auto_mic = 0; /* disable auto-mic to be sure */
+}
+
+static void set_capture_mixer(struct hda_codec *codec)
 {
+       struct alc_spec *spec = codec->spec;
        static struct snd_kcontrol_new *caps[2][3] = {
                { alc_capture_mixer_nosrc1,
                  alc_capture_mixer_nosrc2,
@@ -4572,7 +4709,10 @@ static void set_capture_mixer(struct alc_spec *spec)
        };
        if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) {
                int mux;
-               if (spec->input_mux && spec->input_mux->num_items > 1)
+               if (spec->auto_mic) {
+                       mux = 0;
+                       fixup_automic_adc(codec);
+               } else if (spec->input_mux && spec->input_mux->num_items > 1)
                        mux = 1;
                else
                        mux = 0;
@@ -4629,7 +4769,7 @@ static int patch_alc880(struct hda_codec *codec)
        }
 
        if (board_config != ALC880_AUTO)
-               setup_preset(spec, &alc880_presets[board_config]);
+               setup_preset(codec, &alc880_presets[board_config]);
 
        spec->stream_analog_playback = &alc880_pcm_analog_playback;
        spec->stream_analog_capture = &alc880_pcm_analog_capture;
@@ -4651,7 +4791,7 @@ static int patch_alc880(struct hda_codec *codec)
                        spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
                }
        }
-       set_capture_mixer(spec);
+       set_capture_mixer(codec);
        set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 
        spec->vmaster_nid = 0x0c;
@@ -6273,7 +6413,7 @@ static int patch_alc260(struct hda_codec *codec)
        }
 
        if (board_config != ALC260_AUTO)
-               setup_preset(spec, &alc260_presets[board_config]);
+               setup_preset(codec, &alc260_presets[board_config]);
 
        spec->stream_analog_playback = &alc260_pcm_analog_playback;
        spec->stream_analog_capture = &alc260_pcm_analog_capture;
@@ -6294,7 +6434,7 @@ static int patch_alc260(struct hda_codec *codec)
                        spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
                }
        }
-       set_capture_mixer(spec);
+       set_capture_mixer(codec);
        set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
 
        spec->vmaster_nid = 0x08;
@@ -7313,23 +7453,21 @@ static struct hda_verb alc885_imac24_init_verbs[] = {
 };
 
 /* Toggle speaker-output according to the hp-jack state */
-static void alc885_imac24_automute_init_hook(struct hda_codec *codec)
+static void alc885_imac24_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x18;
        spec->autocfg.speaker_pins[1] = 0x1a;
-       alc_automute_amp(codec);
 }
 
-static void alc885_mbp3_init_hook(struct hda_codec *codec)
+static void alc885_mbp3_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
-       alc_automute_amp(codec);
 }
 
 
@@ -7357,13 +7495,12 @@ static void alc882_targa_automute(struct hda_codec *codec)
                                  spec->jack_present ? 1 : 3);
 }
 
-static void alc882_targa_init_hook(struct hda_codec *codec)
+static void alc882_targa_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x1b;
-       alc882_targa_automute(codec);
 }
 
 static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res)
@@ -7451,7 +7588,7 @@ static void alc885_macpro_init_hook(struct hda_codec *codec)
 static void alc885_imac24_init_hook(struct hda_codec *codec)
 {
        alc885_macpro_init_hook(codec);
-       alc885_imac24_automute_init_hook(codec);
+       alc_automute_amp(codec);
 }
 
 /*
@@ -7941,14 +8078,13 @@ static struct snd_kcontrol_new alc883_chmode_mixer[] = {
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_mitac_init_hook(struct hda_codec *codec)
+static void alc883_mitac_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x17;
-       alc_automute_amp(codec);
 }
 
 /* auto-toggle front mic */
@@ -8103,7 +8239,7 @@ static struct hda_verb alc883_vaiott_verbs[] = {
        { } /* end */
 };
 
-static void alc888_3st_hp_init_hook(struct hda_codec *codec)
+static void alc888_3st_hp_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
@@ -8111,7 +8247,6 @@ static void alc888_3st_hp_init_hook(struct hda_codec *codec)
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x16;
        spec->autocfg.speaker_pins[2] = 0x18;
-       alc_automute_amp(codec);
 }
 
 static struct hda_verb alc888_3st_hp_verbs[] = {
@@ -8208,13 +8343,12 @@ static struct hda_verb alc883_medion_md2_verbs[] = {
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_medion_md2_init_hook(struct hda_codec *codec)
+static void alc883_medion_md2_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x15;
-       alc_automute_amp(codec);
 }
 
 /* toggle speaker-output according to the hp-jack state */
@@ -8231,12 +8365,16 @@ static void alc883_clevo_m720_mic_automute(struct hda_codec *codec)
                                 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
 }
 
-static void alc883_clevo_m720_init_hook(struct hda_codec *codec)
+static void alc883_clevo_m720_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
+}
+
+static void alc883_clevo_m720_init_hook(struct hda_codec *codec)
+{
        alc_automute_amp(codec);
        alc883_clevo_m720_mic_automute(codec);
 }
@@ -8255,22 +8393,20 @@ static void alc883_clevo_m720_unsol_event(struct hda_codec *codec,
 }
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_2ch_fujitsu_pi2515_init_hook(struct hda_codec *codec)
+static void alc883_2ch_fujitsu_pi2515_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x15;
-       alc_automute_amp(codec);
 }
 
-static void alc883_haier_w66_init_hook(struct hda_codec *codec)
+static void alc883_haier_w66_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x1b;
        spec->autocfg.speaker_pins[0] = 0x14;
-       alc_automute_amp(codec);
 }
 
 static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
@@ -8309,14 +8445,13 @@ static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec,
 }
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_acer_aspire_init_hook(struct hda_codec *codec)
+static void alc883_acer_aspire_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x15;
        spec->autocfg.speaker_pins[1] = 0x16;
-       alc_automute_amp(codec);
 }
 
 static struct hda_verb alc883_acer_eapd_verbs[] = {
@@ -8337,7 +8472,7 @@ static struct hda_verb alc883_acer_eapd_verbs[] = {
        { }
 };
 
-static void alc888_6st_dell_init_hook(struct hda_codec *codec)
+static void alc888_6st_dell_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
@@ -8346,10 +8481,9 @@ static void alc888_6st_dell_init_hook(struct hda_codec *codec)
        spec->autocfg.speaker_pins[1] = 0x15;
        spec->autocfg.speaker_pins[2] = 0x16;
        spec->autocfg.speaker_pins[3] = 0x17;
-       alc_automute_amp(codec);
 }
 
-static void alc888_lenovo_sky_init_hook(struct hda_codec *codec)
+static void alc888_lenovo_sky_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
@@ -8359,17 +8493,15 @@ static void alc888_lenovo_sky_init_hook(struct hda_codec *codec)
        spec->autocfg.speaker_pins[2] = 0x16;
        spec->autocfg.speaker_pins[3] = 0x17;
        spec->autocfg.speaker_pins[4] = 0x1a;
-       alc_automute_amp(codec);
 }
 
-static void alc883_vaiott_init_hook(struct hda_codec *codec)
+static void alc883_vaiott_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x17;
-       alc_automute_amp(codec);
 }
 
 static struct hda_verb alc888_asus_m90v_verbs[] = {
@@ -8382,19 +8514,7 @@ static struct hda_verb alc888_asus_m90v_verbs[] = {
        { } /* end */
 };
 
-static void alc883_nb_mic_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x18, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                           0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
-       snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                           0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
-}
-
-static void alc883_M90V_init_hook(struct hda_codec *codec)
+static void alc883_mode2_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
@@ -8402,26 +8522,11 @@ static void alc883_M90V_init_hook(struct hda_codec *codec)
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x15;
        spec->autocfg.speaker_pins[2] = 0x16;
-       alc_automute_pin(codec);
-}
-
-static void alc883_mode2_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
-{
-       switch (res >> 26) {
-       case ALC880_MIC_EVENT:
-               alc883_nb_mic_automute(codec);
-               break;
-       default:
-               alc_sku_unsol_event(codec, res);
-               break;
-       }
-}
-
-static void alc883_mode2_inithook(struct hda_codec *codec)
-{
-       alc883_M90V_init_hook(codec);
-       alc883_nb_mic_automute(codec);
+       spec->ext_mic.pin = 0x18;
+       spec->int_mic.pin = 0x19;
+       spec->ext_mic.mux_idx = 0;
+       spec->int_mic.mux_idx = 1;
+       spec->auto_mic = 1;
 }
 
 static struct hda_verb alc888_asus_eee1601_verbs[] = {
@@ -8752,7 +8857,8 @@ static struct alc_config_preset alc882_presets[] = {
                .dig_out_nid = ALC882_DIGOUT_NID,
                .dig_in_nid = ALC882_DIGIN_NID,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc885_mbp3_init_hook,
+               .setup = alc885_mbp3_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC885_MB5] = {
                .mixers = { alc885_mb5_mixer, alc882_chmode_mixer },
@@ -8789,6 +8895,7 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc882_ch_modes,
                .input_mux = &alc882_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
+               .setup = alc885_imac24_setup,
                .init_hook = alc885_imac24_init_hook,
        },
        [ALC882_TARGA] = {
@@ -8806,7 +8913,8 @@ static struct alc_config_preset alc882_presets[] = {
                .need_dac_fix = 1,
                .input_mux = &alc882_capture_source,
                .unsol_event = alc882_targa_unsol_event,
-               .init_hook = alc882_targa_init_hook,
+               .setup = alc882_targa_setup,
+               .init_hook = alc882_targa_automute,
        },
        [ALC882_ASUS_A7J] = {
                .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer },
@@ -8897,7 +9005,8 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc889_8ch_intel_modes,
                .capsrc_nids = alc889_capsrc_nids,
                .input_mux = &alc889_capture_source,
-               .init_hook = alc889_automute_init,
+               .setup = alc889_automute_setup,
+               .init_hook = alc_automute_amp,
                .unsol_event = alc_automute_amp_unsol_event,
                .need_dac_fix = 1,
        },
@@ -8916,6 +9025,7 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc889_8ch_intel_modes,
                .capsrc_nids = alc889_capsrc_nids,
                .input_mux = &alc889_capture_source,
+               .setup = alc889_automute_setup,
                .init_hook = alc889_intel_init_hook,
                .unsol_event = alc_automute_amp_unsol_event,
                .need_dac_fix = 1,
@@ -8943,7 +9053,8 @@ static struct alc_config_preset alc882_presets[] = {
                .need_dac_fix = 1,
                .input_mux = &alc883_capture_source,
                .unsol_event = alc883_targa_unsol_event,
-               .init_hook = alc883_targa_init_hook,
+               .setup = alc882_targa_setup,
+               .init_hook = alc882_targa_automute,
        },
        [ALC883_TARGA_2ch_DIG] = {
                .mixers = { alc883_targa_2ch_mixer},
@@ -8958,7 +9069,8 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
                .unsol_event = alc883_targa_unsol_event,
-               .init_hook = alc883_targa_init_hook,
+               .setup = alc882_targa_setup,
+               .init_hook = alc882_targa_automute,
        },
        [ALC883_TARGA_8ch_DIG] = {
                .mixers = { alc883_base_mixer, alc883_chmode_mixer },
@@ -8976,7 +9088,8 @@ static struct alc_config_preset alc882_presets[] = {
                .need_dac_fix = 1,
                .input_mux = &alc883_capture_source,
                .unsol_event = alc883_targa_unsol_event,
-               .init_hook = alc883_targa_init_hook,
+               .setup = alc882_targa_setup,
+               .init_hook = alc882_targa_automute,
        },
        [ALC883_ACER] = {
                .mixers = { alc883_base_mixer },
@@ -9002,7 +9115,8 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc883_acer_aspire_init_hook,
+               .setup = alc883_acer_aspire_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC888_ACER_ASPIRE_4930G] = {
                .mixers = { alc888_base_mixer,
@@ -9022,7 +9136,8 @@ static struct alc_config_preset alc882_presets[] = {
                        ARRAY_SIZE(alc888_2_capture_sources),
                .input_mux = alc888_2_capture_sources,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc888_acer_aspire_4930g_init_hook,
+               .setup = alc888_acer_aspire_4930g_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC888_ACER_ASPIRE_6530G] = {
                .mixers = { alc888_acer_aspire_6530_mixer },
@@ -9040,7 +9155,8 @@ static struct alc_config_preset alc882_presets[] = {
                        ARRAY_SIZE(alc888_2_capture_sources),
                .input_mux = alc888_acer_aspire_6530_sources,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc888_acer_aspire_6530g_init_hook,
+               .setup = alc888_acer_aspire_6530g_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC888_ACER_ASPIRE_8930G] = {
                .mixers = { alc888_base_mixer,
@@ -9061,7 +9177,8 @@ static struct alc_config_preset alc882_presets[] = {
                        ARRAY_SIZE(alc889_capture_sources),
                .input_mux = alc889_capture_sources,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc889_acer_aspire_8930g_init_hook,
+               .setup = alc889_acer_aspire_8930g_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC883_MEDION] = {
                .mixers = { alc883_fivestack_mixer,
@@ -9086,7 +9203,8 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc883_medion_md2_init_hook,
+               .setup = alc883_medion_md2_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC883_LAPTOP_EAPD] = {
                .mixers = { alc883_base_mixer },
@@ -9107,6 +9225,7 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
                .unsol_event = alc883_clevo_m720_unsol_event,
+               .setup = alc883_clevo_m720_setup,
                .init_hook = alc883_clevo_m720_init_hook,
        },
        [ALC883_LENOVO_101E_2ch] = {
@@ -9132,7 +9251,8 @@ static struct alc_config_preset alc882_presets[] = {
                .need_dac_fix = 1,
                .input_mux = &alc883_lenovo_nb0763_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc883_medion_md2_init_hook,
+               .setup = alc883_medion_md2_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC888_LENOVO_MS7195_DIG] = {
                .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -9157,7 +9277,8 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc883_haier_w66_init_hook,
+               .setup = alc883_haier_w66_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC888_3ST_HP] = {
                .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -9169,7 +9290,8 @@ static struct alc_config_preset alc882_presets[] = {
                .need_dac_fix = 1,
                .input_mux = &alc883_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc888_3st_hp_init_hook,
+               .setup = alc888_3st_hp_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC888_6ST_DELL] = {
                .mixers = { alc883_base_mixer, alc883_chmode_mixer },
@@ -9182,7 +9304,8 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc883_sixstack_modes,
                .input_mux = &alc883_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc888_6st_dell_init_hook,
+               .setup = alc888_6st_dell_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC883_MITAC] = {
                .mixers = { alc883_mitac_mixer },
@@ -9193,7 +9316,8 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc883_mitac_init_hook,
+               .setup = alc883_mitac_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC883_FUJITSU_PI2515] = {
                .mixers = { alc883_2ch_fujitsu_pi2515_mixer },
@@ -9206,7 +9330,8 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_fujitsu_pi2515_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc883_2ch_fujitsu_pi2515_init_hook,
+               .setup = alc883_2ch_fujitsu_pi2515_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC888_FUJITSU_XA3530] = {
                .mixers = { alc888_base_mixer, alc883_chmode_mixer },
@@ -9224,7 +9349,8 @@ static struct alc_config_preset alc882_presets[] = {
                        ARRAY_SIZE(alc888_2_capture_sources),
                .input_mux = alc888_2_capture_sources,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc888_fujitsu_xa3530_init_hook,
+               .setup = alc888_fujitsu_xa3530_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC888_LENOVO_SKY] = {
                .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
@@ -9237,7 +9363,8 @@ static struct alc_config_preset alc882_presets[] = {
                .need_dac_fix = 1,
                .input_mux = &alc883_lenovo_sky_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc888_lenovo_sky_init_hook,
+               .setup = alc888_lenovo_sky_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC888_ASUS_M90V] = {
                .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -9250,8 +9377,9 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc883_3ST_6ch_modes,
                .need_dac_fix = 1,
                .input_mux = &alc883_fujitsu_pi2515_capture_source,
-               .unsol_event = alc883_mode2_unsol_event,
-               .init_hook = alc883_mode2_inithook,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc883_mode2_setup,
+               .init_hook = alc_inithook,
        },
        [ALC888_ASUS_EEE1601] = {
                .mixers = { alc883_asus_eee1601_mixer },
@@ -9304,7 +9432,8 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc883_vaiott_init_hook,
+               .setup = alc883_vaiott_setup,
+               .init_hook = alc_automute_amp,
        },
 };
 
@@ -9587,7 +9716,7 @@ static int patch_alc882(struct hda_codec *codec)
        }
 
        if (board_config != ALC882_AUTO)
-               setup_preset(spec, &alc882_presets[board_config]);
+               setup_preset(codec, &alc882_presets[board_config]);
 
        spec->stream_analog_playback = &alc882_pcm_analog_playback;
        spec->stream_analog_capture = &alc882_pcm_analog_capture;
@@ -9623,7 +9752,7 @@ static int patch_alc882(struct hda_codec *codec)
                spec->capsrc_nids = spec->private_capsrc_nids;
        }
 
-       set_capture_mixer(spec);
+       set_capture_mixer(codec);
        set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 
        spec->vmaster_nid = 0x0c;
@@ -9816,13 +9945,12 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = {
 };
 
 /* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_hp_t5735_init_hook(struct hda_codec *codec)
+static void alc262_hp_t5735_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x0c; /* HACK: not actually a pin */
-       alc_automute_amp(codec);
 }
 
 static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = {
@@ -9979,22 +10107,20 @@ static void alc262_hippo_unsol_event(struct hda_codec *codec, unsigned int res)
        alc262_hippo_automute(codec);
 }
 
-static void alc262_hippo_init_hook(struct hda_codec *codec)
+static void alc262_hippo_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
-       alc262_hippo_automute(codec);
 }
 
-static void alc262_hippo1_init_hook(struct hda_codec *codec)
+static void alc262_hippo1_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x1b;
        spec->autocfg.speaker_pins[0] = 0x14;
-       alc262_hippo_automute(codec);
 }
 
 
@@ -10051,13 +10177,12 @@ static struct hda_verb alc262_tyan_verbs[] = {
 };
 
 /* unsolicited event for HP jack sensing */
-static void alc262_tyan_init_hook(struct hda_codec *codec)
+static void alc262_tyan_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x1b;
        spec->autocfg.speaker_pins[0] = 0x15;
-       alc_automute_amp(codec);
 }
 
 
@@ -10169,14 +10294,6 @@ static struct hda_verb alc262_sony_unsol_verbs[] = {
        {}
 };
 
-static struct hda_input_mux alc262_dmic_capture_source = {
-       .num_items = 2,
-       .items = {
-               { "Int DMic", 0x9 },
-               { "Mic", 0x0 },
-       },
-};
-
 static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = {
        HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
@@ -10198,35 +10315,17 @@ static struct hda_verb alc262_toshiba_s06_verbs[] = {
        {}
 };
 
-static void alc262_dmic_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x18, 0,
-                                       AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_write(codec, 0x22, 0,
-                               AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x09);
-}
-
-
-/* unsolicited event for HP jack sensing */
-static void alc262_toshiba_s06_unsol_event(struct hda_codec *codec,
-                                      unsigned int res)
-{
-       if ((res >> 26) == ALC880_MIC_EVENT)
-               alc262_dmic_automute(codec);
-       else
-               alc_sku_unsol_event(codec, res);
-}
-
-static void alc262_toshiba_s06_init_hook(struct hda_codec *codec)
+static void alc262_toshiba_s06_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
-       alc_automute_pin(codec);
-       alc262_dmic_automute(codec);
+       spec->ext_mic.pin = 0x18;
+       spec->ext_mic.mux_idx = 0;
+       spec->int_mic.pin = 0x12;
+       spec->int_mic.mux_idx = 9;
+       spec->auto_mic = 1;
 }
 
 /*
@@ -11199,7 +11298,8 @@ static struct alc_config_preset alc262_presets[] = {
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
                .unsol_event = alc262_hippo_unsol_event,
-               .init_hook = alc262_hippo_init_hook,
+               .setup = alc262_hippo_setup,
+               .init_hook = alc262_hippo_automute,
        },
        [ALC262_HIPPO_1] = {
                .mixers = { alc262_hippo1_mixer },
@@ -11212,7 +11312,8 @@ static struct alc_config_preset alc262_presets[] = {
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
                .unsol_event = alc262_hippo_unsol_event,
-               .init_hook = alc262_hippo1_init_hook,
+               .setup = alc262_hippo1_setup,
+               .init_hook = alc262_hippo_automute,
        },
        [ALC262_FUJITSU] = {
                .mixers = { alc262_fujitsu_mixer },
@@ -11275,7 +11376,8 @@ static struct alc_config_preset alc262_presets[] = {
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc262_hp_t5735_init_hook,
+               .setup = alc262_hp_t5735_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC262_HP_RP5700] = {
                .mixers = { alc262_hp_rp5700_mixer },
@@ -11306,7 +11408,8 @@ static struct alc_config_preset alc262_presets[] = {
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
                .unsol_event = alc262_hippo_unsol_event,
-               .init_hook = alc262_hippo_init_hook,
+               .setup = alc262_hippo_setup,
+               .init_hook = alc262_hippo_automute,
        },
        [ALC262_BENQ_T31] = {
                .mixers = { alc262_benq_t31_mixer },
@@ -11319,7 +11422,8 @@ static struct alc_config_preset alc262_presets[] = {
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
                .unsol_event = alc262_hippo_unsol_event,
-               .init_hook = alc262_hippo_init_hook,
+               .setup = alc262_hippo_setup,
+               .init_hook = alc262_hippo_automute,
        },
        [ALC262_ULTRA] = {
                .mixers = { alc262_ultra_mixer },
@@ -11371,9 +11475,9 @@ static struct alc_config_preset alc262_presets[] = {
                .dig_out_nid = ALC262_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
-               .input_mux = &alc262_dmic_capture_source,
-               .unsol_event = alc262_toshiba_s06_unsol_event,
-               .init_hook = alc262_toshiba_s06_init_hook,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc262_toshiba_s06_setup,
+               .init_hook = alc_inithook,
        },
        [ALC262_TOSHIBA_RX1] = {
                .mixers = { alc262_toshiba_rx1_mixer },
@@ -11385,7 +11489,8 @@ static struct alc_config_preset alc262_presets[] = {
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
                .unsol_event = alc262_hippo_unsol_event,
-               .init_hook = alc262_hippo_init_hook,
+               .setup = alc262_hippo_setup,
+               .init_hook = alc262_hippo_automute,
        },
        [ALC262_TYAN] = {
                .mixers = { alc262_tyan_mixer },
@@ -11398,7 +11503,8 @@ static struct alc_config_preset alc262_presets[] = {
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc262_tyan_init_hook,
+               .setup = alc262_tyan_setup,
+               .init_hook = alc_automute_amp,
        },
 };
 
@@ -11461,7 +11567,7 @@ static int patch_alc262(struct hda_codec *codec)
        }
 
        if (board_config != ALC262_AUTO)
-               setup_preset(spec, &alc262_presets[board_config]);
+               setup_preset(codec, &alc262_presets[board_config]);
 
        spec->stream_analog_playback = &alc262_pcm_analog_playback;
        spec->stream_analog_capture = &alc262_pcm_analog_capture;
@@ -11502,7 +11608,7 @@ static int patch_alc262(struct hda_codec *codec)
                }
        }
        if (!spec->cap_mixer && !spec->no_analog)
-               set_capture_mixer(spec);
+               set_capture_mixer(codec);
        if (!spec->no_analog)
                set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 
@@ -11594,14 +11700,6 @@ static struct hda_verb alc268_toshiba_verbs[] = {
        { } /* end */
 };
 
-static struct hda_input_mux alc268_acer_lc_capture_source = {
-       .num_items = 2,
-       .items = {
-               { "i-Mic", 0x6 },
-               { "E-Mic", 0x0 },
-       },
-};
-
 /* Acer specific */
 /* bind volumes of both NID 0x02 and 0x03 */
 static struct hda_bind_ctls alc268_acer_bind_master_vol = {
@@ -11720,7 +11818,8 @@ static struct hda_verb alc268_acer_verbs[] = {
 
 /* unsolicited event for HP jack sensing */
 #define alc268_toshiba_unsol_event     alc262_hippo_unsol_event
-#define alc268_toshiba_init_hook       alc262_hippo_init_hook
+#define alc268_toshiba_setup           alc262_hippo_setup
+#define alc268_toshiba_automute                alc262_hippo_automute
 
 static void alc268_acer_unsol_event(struct hda_codec *codec,
                                       unsigned int res)
@@ -11750,30 +11849,33 @@ static void alc268_aspire_one_speaker_automute(struct hda_codec *codec)
                                AMP_IN_MUTE(0), bits);
 }
 
-
-static void alc268_acer_mic_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x18, 0,
-                               AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CONNECT_SEL,
-                           present ? 0x0 : 0x6);
-}
-
 static void alc268_acer_lc_unsol_event(struct hda_codec *codec,
                                    unsigned int res)
 {
-       if ((res >> 26) == ALC880_HP_EVENT)
+       switch (res >> 26) {
+       case ALC880_HP_EVENT:
                alc268_aspire_one_speaker_automute(codec);
-       if ((res >> 26) == ALC880_MIC_EVENT)
-               alc268_acer_mic_automute(codec);
+               break;
+       case ALC880_MIC_EVENT:
+               alc_mic_automute(codec);
+               break;
+       }
+}
+
+static void alc268_acer_lc_setup(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       spec->ext_mic.pin = 0x18;
+       spec->ext_mic.mux_idx = 0;
+       spec->int_mic.pin = 0x12;
+       spec->int_mic.mux_idx = 6;
+       spec->auto_mic = 1;
 }
 
 static void alc268_acer_lc_init_hook(struct hda_codec *codec)
 {
        alc268_aspire_one_speaker_automute(codec);
-       alc268_acer_mic_automute(codec);
+       alc_mic_automute(codec);
 }
 
 static struct snd_kcontrol_new alc268_dell_mixer[] = {
@@ -11791,17 +11893,22 @@ static struct hda_verb alc268_dell_verbs[] = {
        {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
        {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
        {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+       {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
        { }
 };
 
 /* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc268_dell_init_hook(struct hda_codec *codec)
+static void alc268_dell_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
-       alc_automute_pin(codec);
+       spec->ext_mic.pin = 0x18;
+       spec->ext_mic.mux_idx = 0;
+       spec->int_mic.pin = 0x19;
+       spec->int_mic.mux_idx = 1;
+       spec->auto_mic = 1;
 }
 
 static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = {
@@ -11822,38 +11929,16 @@ static struct hda_verb alc267_quanta_il1_verbs[] = {
        { }
 };
 
-static void alc267_quanta_il1_mic_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x18, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_write(codec, 0x23, 0,
-                           AC_VERB_SET_CONNECT_SEL,
-                           present ? 0x00 : 0x01);
-}
-
-static void alc267_quanta_il1_init_hook(struct hda_codec *codec)
+static void alc267_quanta_il1_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
-       alc_automute_pin(codec);
-       alc267_quanta_il1_mic_automute(codec);
-}
-
-static void alc267_quanta_il1_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
-{
-       switch (res >> 26) {
-       case ALC880_MIC_EVENT:
-               alc267_quanta_il1_mic_automute(codec);
-               break;
-       default:
-               alc_sku_unsol_event(codec, res);
-               break;
-       }
+       spec->ext_mic.pin = 0x18;
+       spec->ext_mic.mux_idx = 0;
+       spec->int_mic.pin = 0x19;
+       spec->int_mic.mux_idx = 1;
+       spec->auto_mic = 1;
 }
 
 /*
@@ -12147,32 +12232,40 @@ static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec,
                                                const struct auto_pin_cfg *cfg)
 {
        struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, idx1;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               switch(cfg->input_pins[i]) {
-               case 0x18:
-                       idx1 = 0;       /* Mic 1 */
-                       break;
-               case 0x19:
-                       idx1 = 1;       /* Mic 2 */
-                       break;
-               case 0x1a:
-                       idx1 = 2;       /* Line In */
-                       break;
-               case 0x1c:
-                       idx1 = 3;       /* CD */
-                       break;
-               case 0x12:
-               case 0x13:
-                       idx1 = 6;       /* digital mics */
-                       break;
-               default:
-                       continue;
+       int i, idx1, dmic_nid;
+
+       dmic_nid = 0x12;
+       while (dmic_nid <= 0x13) {
+               for (i = 0; i < AUTO_PIN_LAST; i++) {
+                       switch (cfg->input_pins[i]) {
+                       case 0x18:
+                               idx1 = 0;       /* Mic 1 */
+                               break;
+                       case 0x19:
+                               idx1 = 1;       /* Mic 2 */
+                               break;
+                       case 0x1a:
+                               idx1 = 2;       /* Line In */
+                               break;
+                       case 0x1c:
+                               idx1 = 3;       /* CD */
+                               break;
+                       case 0x12:
+                       case 0x13:
+                               if (cfg->input_pins[i] != dmic_nid)
+                                       continue;
+                               idx1 = 6;       /* digital mics */
+                               break;
+                       default:
+                               continue;
+                       }
+                       imux->items[imux->num_items].label =
+                               auto_pin_cfg_labels[i];
+                       imux->items[imux->num_items].index = idx1;
+                       imux->num_items++;
                }
-               imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
-               imux->items[imux->num_items].index = idx1;
-               imux->num_items++;
+               imux++;
+               dmic_nid++;
        }
        return 0;
 }
@@ -12270,7 +12363,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
                add_mixer(spec, alc268_beep_mixer);
 
        add_verb(spec, alc268_volume_init_verbs);
-       spec->num_mux_defs = 1;
+       spec->num_mux_defs = 2;
        spec->input_mux = &spec->private_imux[0];
 
        err = alc_auto_add_mic_boost(codec);
@@ -12352,8 +12445,9 @@ static struct alc_config_preset alc268_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc268_modes),
                .channel_mode = alc268_modes,
                .input_mux = &alc268_capture_source,
-               .unsol_event = alc267_quanta_il1_unsol_event,
-               .init_hook = alc267_quanta_il1_init_hook,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc267_quanta_il1_setup,
+               .init_hook = alc_inithook,
        },
        [ALC268_3ST] = {
                .mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
@@ -12385,7 +12479,8 @@ static struct alc_config_preset alc268_presets[] = {
                .channel_mode = alc268_modes,
                .input_mux = &alc268_capture_source,
                .unsol_event = alc268_toshiba_unsol_event,
-               .init_hook = alc268_toshiba_init_hook,
+               .setup = alc268_toshiba_setup,
+               .init_hook = alc268_toshiba_automute,
        },
        [ALC268_ACER] = {
                .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer,
@@ -12435,8 +12530,8 @@ static struct alc_config_preset alc268_presets[] = {
                .hp_nid = 0x03,
                .num_channel_mode = ARRAY_SIZE(alc268_modes),
                .channel_mode = alc268_modes,
-               .input_mux = &alc268_acer_lc_capture_source,
                .unsol_event = alc268_acer_lc_unsol_event,
+               .setup = alc268_acer_lc_setup,
                .init_hook = alc268_acer_lc_init_hook,
        },
        [ALC268_DELL] = {
@@ -12449,8 +12544,8 @@ static struct alc_config_preset alc268_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc268_modes),
                .channel_mode = alc268_modes,
                .unsol_event = alc_sku_unsol_event,
-               .init_hook = alc268_dell_init_hook,
-               .input_mux = &alc268_capture_source,
+               .setup = alc268_dell_setup,
+               .init_hook = alc_inithook,
        },
        [ALC268_ZEPTO] = {
                .mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
@@ -12467,8 +12562,8 @@ static struct alc_config_preset alc268_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc268_modes),
                .channel_mode = alc268_modes,
                .input_mux = &alc268_capture_source,
-               .unsol_event = alc268_toshiba_unsol_event,
-               .init_hook = alc268_toshiba_init_hook
+               .setup = alc268_toshiba_setup,
+               .init_hook = alc268_toshiba_automute,
        },
 #ifdef CONFIG_SND_DEBUG
        [ALC268_TEST] = {
@@ -12526,7 +12621,7 @@ static int patch_alc268(struct hda_codec *codec)
        }
 
        if (board_config != ALC268_AUTO)
-               setup_preset(spec, &alc268_presets[board_config]);
+               setup_preset(codec, &alc268_presets[board_config]);
 
        spec->stream_analog_playback = &alc268_pcm_analog_playback;
        spec->stream_analog_capture = &alc268_pcm_analog_capture;
@@ -12578,6 +12673,8 @@ static int patch_alc268(struct hda_codec *codec)
                for (i = 0; i < spec->num_adc_nids; i++)
                        snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i],
                                0, AC_VERB_SET_CONNECT_SEL,
+                               i < spec->num_mux_defs ?
+                               spec->input_mux[i].items[0].index :
                                spec->input_mux->items[0].index);
        }
 
@@ -12612,22 +12709,6 @@ static hda_nid_t alc269_capsrc_nids[1] = {
  *       not a mux!
  */
 
-static struct hda_input_mux alc269_eeepc_dmic_capture_source = {
-       .num_items = 2,
-       .items = {
-               { "i-Mic", 0x5 },
-               { "e-Mic", 0x0 },
-       },
-};
-
-static struct hda_input_mux alc269_eeepc_amic_capture_source = {
-       .num_items = 2,
-       .items = {
-               { "i-Mic", 0x1 },
-               { "e-Mic", 0x0 },
-       },
-};
-
 #define alc269_modes           alc260_modes
 #define alc269_capture_source  alc880_lg_lw_capture_source
 
@@ -12789,16 +12870,6 @@ static void alc269_lifebook_speaker_automute(struct hda_codec *codec)
                        AC_VERB_SET_PROC_COEF, 0x480);
 }
 
-static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x18, 0,
-                               AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_write(codec, 0x23, 0,
-                           AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x1);
-}
-
 static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
 {
        unsigned int present_laptop;
@@ -12825,10 +12896,14 @@ static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
 static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
                                    unsigned int res)
 {
-       if ((res >> 26) == ALC880_HP_EVENT)
+       switch (res >> 26) {
+       case ALC880_HP_EVENT:
                alc269_quanta_fl1_speaker_automute(codec);
-       if ((res >> 26) == ALC880_MIC_EVENT)
-               alc269_quanta_fl1_mic_automute(codec);
+               break;
+       case ALC880_MIC_EVENT:
+               alc_mic_automute(codec);
+               break;
+       }
 }
 
 static void alc269_lifebook_unsol_event(struct hda_codec *codec,
@@ -12840,10 +12915,20 @@ static void alc269_lifebook_unsol_event(struct hda_codec *codec,
                alc269_lifebook_mic_autoswitch(codec);
 }
 
+static void alc269_quanta_fl1_setup(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       spec->ext_mic.pin = 0x18;
+       spec->ext_mic.mux_idx = 0;
+       spec->int_mic.pin = 0x19;
+       spec->int_mic.mux_idx = 1;
+       spec->auto_mic = 1;
+}
+
 static void alc269_quanta_fl1_init_hook(struct hda_codec *codec)
 {
        alc269_quanta_fl1_speaker_automute(codec);
-       alc269_quanta_fl1_mic_automute(codec);
+       alc_mic_automute(codec);
 }
 
 static void alc269_lifebook_init_hook(struct hda_codec *codec)
@@ -12888,60 +12973,44 @@ static void alc269_speaker_automute(struct hda_codec *codec)
                                AMP_IN_MUTE(0), bits);
 }
 
-static void alc269_eeepc_dmic_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x18, 0,
-                               AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_write(codec, 0x23, 0,
-                               AC_VERB_SET_CONNECT_SEL,  (present ? 0 : 5));
-}
-
-static void alc269_eeepc_amic_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x18, 0,
-                               AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                               0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
-       snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                               0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
-}
-
 /* unsolicited event for HP jack sensing */
-static void alc269_eeepc_dmic_unsol_event(struct hda_codec *codec,
+static void alc269_eeepc_unsol_event(struct hda_codec *codec,
                                     unsigned int res)
 {
-       if ((res >> 26) == ALC880_HP_EVENT)
+       switch (res >> 26) {
+       case ALC880_HP_EVENT:
                alc269_speaker_automute(codec);
-
-       if ((res >> 26) == ALC880_MIC_EVENT)
-               alc269_eeepc_dmic_automute(codec);
+               break;
+       case ALC880_MIC_EVENT:
+               alc_mic_automute(codec);
+               break;
+       }
 }
 
-static void alc269_eeepc_dmic_inithook(struct hda_codec *codec)
+static void alc269_eeepc_dmic_setup(struct hda_codec *codec)
 {
-       alc269_speaker_automute(codec);
-       alc269_eeepc_dmic_automute(codec);
+       struct alc_spec *spec = codec->spec;
+       spec->ext_mic.pin = 0x18;
+       spec->ext_mic.mux_idx = 0;
+       spec->int_mic.pin = 0x12;
+       spec->int_mic.mux_idx = 5;
+       spec->auto_mic = 1;
 }
 
-/* unsolicited event for HP jack sensing */
-static void alc269_eeepc_amic_unsol_event(struct hda_codec *codec,
-                                    unsigned int res)
+static void alc269_eeepc_amic_setup(struct hda_codec *codec)
 {
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc269_speaker_automute(codec);
-
-       if ((res >> 26) == ALC880_MIC_EVENT)
-               alc269_eeepc_amic_automute(codec);
+       struct alc_spec *spec = codec->spec;
+       spec->ext_mic.pin = 0x18;
+       spec->ext_mic.mux_idx = 0;
+       spec->int_mic.pin = 0x19;
+       spec->int_mic.mux_idx = 1;
+       spec->auto_mic = 1;
 }
 
-static void alc269_eeepc_amic_inithook(struct hda_codec *codec)
+static void alc269_eeepc_inithook(struct hda_codec *codec)
 {
        alc269_speaker_automute(codec);
-       alc269_eeepc_amic_automute(codec);
+       alc_mic_automute(codec);
 }
 
 /*
@@ -13171,7 +13240,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
                return err;
 
        if (!spec->cap_mixer && !spec->no_analog)
-               set_capture_mixer(spec);
+               set_capture_mixer(codec);
 
        alc_ssid_check(codec, 0x15, 0x1b, 0x14);
 
@@ -13247,6 +13316,7 @@ static struct alc_config_preset alc269_presets[] = {
                .channel_mode = alc269_modes,
                .input_mux = &alc269_capture_source,
                .unsol_event = alc269_quanta_fl1_unsol_event,
+               .setup = alc269_quanta_fl1_setup,
                .init_hook = alc269_quanta_fl1_init_hook,
        },
        [ALC269_ASUS_EEEPC_P703] = {
@@ -13259,9 +13329,9 @@ static struct alc_config_preset alc269_presets[] = {
                .hp_nid = 0x03,
                .num_channel_mode = ARRAY_SIZE(alc269_modes),
                .channel_mode = alc269_modes,
-               .input_mux = &alc269_eeepc_amic_capture_source,
-               .unsol_event = alc269_eeepc_amic_unsol_event,
-               .init_hook = alc269_eeepc_amic_inithook,
+               .unsol_event = alc269_eeepc_unsol_event,
+               .setup = alc269_eeepc_amic_setup,
+               .init_hook = alc269_eeepc_inithook,
        },
        [ALC269_ASUS_EEEPC_P901] = {
                .mixers = { alc269_eeepc_mixer },
@@ -13273,9 +13343,9 @@ static struct alc_config_preset alc269_presets[] = {
                .hp_nid = 0x03,
                .num_channel_mode = ARRAY_SIZE(alc269_modes),
                .channel_mode = alc269_modes,
-               .input_mux = &alc269_eeepc_dmic_capture_source,
-               .unsol_event = alc269_eeepc_dmic_unsol_event,
-               .init_hook = alc269_eeepc_dmic_inithook,
+               .unsol_event = alc269_eeepc_unsol_event,
+               .setup = alc269_eeepc_dmic_setup,
+               .init_hook = alc269_eeepc_inithook,
        },
        [ALC269_FUJITSU] = {
                .mixers = { alc269_fujitsu_mixer },
@@ -13287,9 +13357,9 @@ static struct alc_config_preset alc269_presets[] = {
                .hp_nid = 0x03,
                .num_channel_mode = ARRAY_SIZE(alc269_modes),
                .channel_mode = alc269_modes,
-               .input_mux = &alc269_eeepc_dmic_capture_source,
-               .unsol_event = alc269_eeepc_dmic_unsol_event,
-               .init_hook = alc269_eeepc_dmic_inithook,
+               .unsol_event = alc269_eeepc_unsol_event,
+               .setup = alc269_eeepc_dmic_setup,
+               .init_hook = alc269_eeepc_inithook,
        },
        [ALC269_LIFEBOOK] = {
                .mixers = { alc269_lifebook_mixer },
@@ -13350,7 +13420,7 @@ static int patch_alc269(struct hda_codec *codec)
        }
 
        if (board_config != ALC269_AUTO)
-               setup_preset(spec, &alc269_presets[board_config]);
+               setup_preset(codec, &alc269_presets[board_config]);
 
        if (codec->subsystem_id == 0x17aa3bf8) {
                /* Due to a hardware problem on Lenovo Ideadpad, we need to
@@ -13369,9 +13439,11 @@ static int patch_alc269(struct hda_codec *codec)
        spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
        spec->capsrc_nids = alc269_capsrc_nids;
        if (!spec->cap_mixer)
-               set_capture_mixer(spec);
+               set_capture_mixer(codec);
        set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
 
+       spec->vmaster_nid = 0x02;
+
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC269_AUTO)
                spec->init_hook = alc269_auto_init;
@@ -14282,7 +14354,7 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
 
        spec->adc_nids = alc861_adc_nids;
        spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
-       set_capture_mixer(spec);
+       set_capture_mixer(codec);
 
        alc_ssid_check(codec, 0x0e, 0x0f, 0x0b);
 
@@ -14501,7 +14573,7 @@ static int patch_alc861(struct hda_codec *codec)
        }
 
        if (board_config != ALC861_AUTO)
-               setup_preset(spec, &alc861_presets[board_config]);
+               setup_preset(codec, &alc861_presets[board_config]);
 
        spec->stream_analog_playback = &alc861_pcm_analog_playback;
        spec->stream_analog_capture = &alc861_pcm_analog_capture;
@@ -14904,12 +14976,15 @@ static void alc861vd_lenovo_mic_automute(struct hda_codec *codec)
                                 HDA_AMP_MUTE, bits);
 }
 
-static void alc861vd_lenovo_init_hook(struct hda_codec *codec)
+static void alc861vd_lenovo_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-
        spec->autocfg.hp_pins[0] = 0x1b;
        spec->autocfg.speaker_pins[0] = 0x14;
+}
+
+static void alc861vd_lenovo_init_hook(struct hda_codec *codec)
+{
        alc_automute_amp(codec);
        alc861vd_lenovo_mic_automute(codec);
 }
@@ -14973,13 +15048,12 @@ static struct hda_verb alc861vd_dallas_verbs[] = {
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc861vd_dallas_init_hook(struct hda_codec *codec)
+static void alc861vd_dallas_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
-       alc_automute_amp(codec);
 }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -15018,7 +15092,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
        SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
        SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
        /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/
-       SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS),
+       SND_PCI_QUIRK(0x1179, 0xff01, "Toshiba A135", ALC861VD_LENOVO),
        SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO),
        SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS),
        SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG),
@@ -15093,6 +15167,7 @@ static struct alc_config_preset alc861vd_presets[] = {
                .channel_mode = alc861vd_3stack_2ch_modes,
                .input_mux = &alc861vd_capture_source,
                .unsol_event = alc861vd_lenovo_unsol_event,
+               .setup = alc861vd_lenovo_setup,
                .init_hook = alc861vd_lenovo_init_hook,
        },
        [ALC861VD_DALLAS] = {
@@ -15104,7 +15179,8 @@ static struct alc_config_preset alc861vd_presets[] = {
                .channel_mode = alc861vd_3stack_2ch_modes,
                .input_mux = &alc861vd_dallas_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc861vd_dallas_init_hook,
+               .setup = alc861vd_dallas_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC861VD_HP] = {
                .mixers = { alc861vd_hp_mixer },
@@ -15116,7 +15192,8 @@ static struct alc_config_preset alc861vd_presets[] = {
                .channel_mode = alc861vd_3stack_2ch_modes,
                .input_mux = &alc861vd_hp_capture_source,
                .unsol_event = alc_automute_amp_unsol_event,
-               .init_hook = alc861vd_dallas_init_hook,
+               .setup = alc861vd_dallas_setup,
+               .init_hook = alc_automute_amp,
        },
        [ALC660VD_ASUS_V1S] = {
                .mixers = { alc861vd_lenovo_mixer },
@@ -15131,6 +15208,7 @@ static struct alc_config_preset alc861vd_presets[] = {
                .channel_mode = alc861vd_3stack_2ch_modes,
                .input_mux = &alc861vd_capture_source,
                .unsol_event = alc861vd_lenovo_unsol_event,
+               .setup = alc861vd_lenovo_setup,
                .init_hook = alc861vd_lenovo_init_hook,
        },
 };
@@ -15425,7 +15503,7 @@ static int patch_alc861vd(struct hda_codec *codec)
        }
 
        if (board_config != ALC861VD_AUTO)
-               setup_preset(spec, &alc861vd_presets[board_config]);
+               setup_preset(codec, &alc861vd_presets[board_config]);
 
        if (codec->vendor_id == 0x10ec0660) {
                /* always turn on EAPD */
@@ -15438,11 +15516,14 @@ static int patch_alc861vd(struct hda_codec *codec)
        spec->stream_digital_playback = &alc861vd_pcm_digital_playback;
        spec->stream_digital_capture = &alc861vd_pcm_digital_capture;
 
-       spec->adc_nids = alc861vd_adc_nids;
-       spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
-       spec->capsrc_nids = alc861vd_capsrc_nids;
+       if (!spec->adc_nids) {
+               spec->adc_nids = alc861vd_adc_nids;
+               spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
+       }
+       if (!spec->capsrc_nids)
+               spec->capsrc_nids = alc861vd_capsrc_nids;
 
-       set_capture_mixer(spec);
+       set_capture_mixer(codec);
        set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 
        spec->vmaster_nid = 0x02;
@@ -15483,9 +15564,9 @@ static hda_nid_t alc272_dac_nids[2] = {
        0x02, 0x03
 };
 
-static hda_nid_t alc662_adc_nids[1] = {
+static hda_nid_t alc662_adc_nids[2] = {
        /* ADC1-2 */
-       0x09,
+       0x09, 0x08
 };
 
 static hda_nid_t alc272_adc_nids[1] = {
@@ -15493,7 +15574,7 @@ static hda_nid_t alc272_adc_nids[1] = {
        0x08,
 };
 
-static hda_nid_t alc662_capsrc_nids[1] = { 0x22 };
+static hda_nid_t alc662_capsrc_nids[2] = { 0x22, 0x23 };
 static hda_nid_t alc272_capsrc_nids[1] = { 0x23 };
 
 
@@ -15517,14 +15598,6 @@ static struct hda_input_mux alc662_lenovo_101e_capture_source = {
        },
 };
 
-static struct hda_input_mux alc662_eeepc_capture_source = {
-       .num_items = 2,
-       .items = {
-               { "i-Mic", 0x1 },
-               { "e-Mic", 0x0 },
-       },
-};
-
 static struct hda_input_mux alc663_capture_source = {
        .num_items = 3,
        .items = {
@@ -15534,23 +15607,7 @@ static struct hda_input_mux alc663_capture_source = {
        },
 };
 
-static struct hda_input_mux alc663_m51va_capture_source = {
-       .num_items = 2,
-       .items = {
-               { "Ext-Mic", 0x0 },
-               { "D-Mic", 0x9 },
-       },
-};
-
-#if 1 /* set to 0 for testing other input sources below */
-static struct hda_input_mux alc272_nc10_capture_source = {
-       .num_items = 2,
-       .items = {
-               { "Autoselect Mic", 0x0 },
-               { "Internal Mic", 0x1 },
-       },
-};
-#else
+#if 0 /* set to 1 for testing other input sources below */
 static struct hda_input_mux alc272_nc10_capture_source = {
        .num_items = 16,
        .items = {
@@ -16219,47 +16276,44 @@ static void alc662_lenovo_101e_unsol_event(struct hda_codec *codec,
                alc662_lenovo_101e_ispeaker_automute(codec);
 }
 
-static void alc662_eeepc_mic_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x18, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                           0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
-       snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                           0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
-       snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                           0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
-       snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                           0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
-}
-
 /* unsolicited event for HP jack sensing */
 static void alc662_eeepc_unsol_event(struct hda_codec *codec,
                                     unsigned int res)
 {
        if ((res >> 26) == ALC880_MIC_EVENT)
-               alc662_eeepc_mic_automute(codec);
+               alc_mic_automute(codec);
        else
                alc262_hippo_unsol_event(codec, res);
 }
 
+static void alc662_eeepc_setup(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       alc262_hippo1_setup(codec);
+       spec->ext_mic.pin = 0x18;
+       spec->ext_mic.mux_idx = 0;
+       spec->int_mic.pin = 0x19;
+       spec->int_mic.mux_idx = 1;
+       spec->auto_mic = 1;
+}
+
 static void alc662_eeepc_inithook(struct hda_codec *codec)
 {
-       alc262_hippo1_init_hook(codec);
-       alc662_eeepc_mic_automute(codec);
+       alc262_hippo_automute(codec);
+       alc_mic_automute(codec);
 }
 
-static void alc662_eeepc_ep20_inithook(struct hda_codec *codec)
+static void alc662_eeepc_ep20_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x1b;
-       alc262_hippo_master_update(codec);
 }
 
+#define alc662_eeepc_ep20_inithook     alc262_hippo_master_update
+
 static void alc663_m51va_speaker_automute(struct hda_codec *codec)
 {
        unsigned int present;
@@ -16370,23 +16424,6 @@ static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec)
        }
 }
 
-static void alc663_m51va_mic_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_codec_read(codec, 0x18, 0,
-                       AC_VERB_GET_PIN_SENSE, 0)
-                       & AC_PINSENSE_PRESENCE;
-       snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                       0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
-       snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                       0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
-       snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                       0x7000 | (0x09 << 8) | (present ? 0x80 : 0));
-       snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                       0x7000 | (0x09 << 8) | (present ? 0x80 : 0));
-}
-
 static void alc663_m51va_unsol_event(struct hda_codec *codec,
                                           unsigned int res)
 {
@@ -16395,36 +16432,32 @@ static void alc663_m51va_unsol_event(struct hda_codec *codec,
                alc663_m51va_speaker_automute(codec);
                break;
        case ALC880_MIC_EVENT:
-               alc663_m51va_mic_automute(codec);
+               alc_mic_automute(codec);
                break;
        }
 }
 
+static void alc663_m51va_setup(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       spec->ext_mic.pin = 0x18;
+       spec->ext_mic.mux_idx = 0;
+       spec->int_mic.pin = 0x12;
+       spec->int_mic.mux_idx = 1;
+       spec->auto_mic = 1;
+}
+
 static void alc663_m51va_inithook(struct hda_codec *codec)
 {
        alc663_m51va_speaker_automute(codec);
-       alc663_m51va_mic_automute(codec);
+       alc_mic_automute(codec);
 }
 
 /* ***************** Mode1 ******************************/
-static void alc663_mode1_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
-{
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc663_m51va_speaker_automute(codec);
-               break;
-       case ALC880_MIC_EVENT:
-               alc662_eeepc_mic_automute(codec);
-               break;
-       }
-}
+#define alc663_mode1_unsol_event       alc663_m51va_unsol_event
+#define alc663_mode1_setup             alc663_m51va_setup
+#define alc663_mode1_inithook          alc663_m51va_inithook
 
-static void alc663_mode1_inithook(struct hda_codec *codec)
-{
-       alc663_m51va_speaker_automute(codec);
-       alc662_eeepc_mic_automute(codec);
-}
 /* ***************** Mode2 ******************************/
 static void alc662_mode2_unsol_event(struct hda_codec *codec,
                                           unsigned int res)
@@ -16434,15 +16467,17 @@ static void alc662_mode2_unsol_event(struct hda_codec *codec,
                alc662_f5z_speaker_automute(codec);
                break;
        case ALC880_MIC_EVENT:
-               alc662_eeepc_mic_automute(codec);
+               alc_mic_automute(codec);
                break;
        }
 }
 
+#define alc662_mode2_setup     alc663_m51va_setup
+
 static void alc662_mode2_inithook(struct hda_codec *codec)
 {
        alc662_f5z_speaker_automute(codec);
-       alc662_eeepc_mic_automute(codec);
+       alc_mic_automute(codec);
 }
 /* ***************** Mode3 ******************************/
 static void alc663_mode3_unsol_event(struct hda_codec *codec,
@@ -16453,15 +16488,17 @@ static void alc663_mode3_unsol_event(struct hda_codec *codec,
                alc663_two_hp_m1_speaker_automute(codec);
                break;
        case ALC880_MIC_EVENT:
-               alc662_eeepc_mic_automute(codec);
+               alc_mic_automute(codec);
                break;
        }
 }
 
+#define alc663_mode3_setup     alc663_m51va_setup
+
 static void alc663_mode3_inithook(struct hda_codec *codec)
 {
        alc663_two_hp_m1_speaker_automute(codec);
-       alc662_eeepc_mic_automute(codec);
+       alc_mic_automute(codec);
 }
 /* ***************** Mode4 ******************************/
 static void alc663_mode4_unsol_event(struct hda_codec *codec,
@@ -16472,15 +16509,17 @@ static void alc663_mode4_unsol_event(struct hda_codec *codec,
                alc663_21jd_two_speaker_automute(codec);
                break;
        case ALC880_MIC_EVENT:
-               alc662_eeepc_mic_automute(codec);
+               alc_mic_automute(codec);
                break;
        }
 }
 
+#define alc663_mode4_setup     alc663_m51va_setup
+
 static void alc663_mode4_inithook(struct hda_codec *codec)
 {
        alc663_21jd_two_speaker_automute(codec);
-       alc662_eeepc_mic_automute(codec);
+       alc_mic_automute(codec);
 }
 /* ***************** Mode5 ******************************/
 static void alc663_mode5_unsol_event(struct hda_codec *codec,
@@ -16491,15 +16530,17 @@ static void alc663_mode5_unsol_event(struct hda_codec *codec,
                alc663_15jd_two_speaker_automute(codec);
                break;
        case ALC880_MIC_EVENT:
-               alc662_eeepc_mic_automute(codec);
+               alc_mic_automute(codec);
                break;
        }
 }
 
+#define alc663_mode5_setup     alc663_m51va_setup
+
 static void alc663_mode5_inithook(struct hda_codec *codec)
 {
        alc663_15jd_two_speaker_automute(codec);
-       alc662_eeepc_mic_automute(codec);
+       alc_mic_automute(codec);
 }
 /* ***************** Mode6 ******************************/
 static void alc663_mode6_unsol_event(struct hda_codec *codec,
@@ -16510,15 +16551,17 @@ static void alc663_mode6_unsol_event(struct hda_codec *codec,
                alc663_two_hp_m2_speaker_automute(codec);
                break;
        case ALC880_MIC_EVENT:
-               alc662_eeepc_mic_automute(codec);
+               alc_mic_automute(codec);
                break;
        }
 }
 
+#define alc663_mode6_setup     alc663_m51va_setup
+
 static void alc663_mode6_inithook(struct hda_codec *codec)
 {
        alc663_two_hp_m2_speaker_automute(codec);
-       alc662_eeepc_mic_automute(codec);
+       alc_mic_automute(codec);
 }
 
 static void alc663_g71v_hp_automute(struct hda_codec *codec)
@@ -16560,16 +16603,18 @@ static void alc663_g71v_unsol_event(struct hda_codec *codec,
                alc663_g71v_front_automute(codec);
                break;
        case ALC880_MIC_EVENT:
-               alc662_eeepc_mic_automute(codec);
+               alc_mic_automute(codec);
                break;
        }
 }
 
+#define alc663_g71v_setup      alc663_m51va_setup
+
 static void alc663_g71v_inithook(struct hda_codec *codec)
 {
        alc663_g71v_front_automute(codec);
        alc663_g71v_hp_automute(codec);
-       alc662_eeepc_mic_automute(codec);
+       alc_mic_automute(codec);
 }
 
 static void alc663_g50v_unsol_event(struct hda_codec *codec,
@@ -16580,15 +16625,17 @@ static void alc663_g50v_unsol_event(struct hda_codec *codec,
                alc663_m51va_speaker_automute(codec);
                break;
        case ALC880_MIC_EVENT:
-               alc662_eeepc_mic_automute(codec);
+               alc_mic_automute(codec);
                break;
        }
 }
 
+#define alc663_g50v_setup      alc663_m51va_setup
+
 static void alc663_g50v_inithook(struct hda_codec *codec)
 {
        alc663_m51va_speaker_automute(codec);
-       alc662_eeepc_mic_automute(codec);
+       alc_mic_automute(codec);
 }
 
 static struct snd_kcontrol_new alc662_ecs_mixer[] = {
@@ -16792,8 +16839,8 @@ static struct alc_config_preset alc662_presets[] = {
                .dac_nids = alc662_dac_nids,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .input_mux = &alc662_eeepc_capture_source,
                .unsol_event = alc662_eeepc_unsol_event,
+               .setup = alc662_eeepc_setup,
                .init_hook = alc662_eeepc_inithook,
        },
        [ALC662_ASUS_EEEPC_EP20] = {
@@ -16807,6 +16854,7 @@ static struct alc_config_preset alc662_presets[] = {
                .channel_mode = alc662_3ST_6ch_modes,
                .input_mux = &alc662_lenovo_101e_capture_source,
                .unsol_event = alc662_eeepc_unsol_event,
+               .setup = alc662_eeepc_ep20_setup,
                .init_hook = alc662_eeepc_ep20_inithook,
        },
        [ALC662_ECS] = {
@@ -16817,8 +16865,8 @@ static struct alc_config_preset alc662_presets[] = {
                .dac_nids = alc662_dac_nids,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .input_mux = &alc662_eeepc_capture_source,
                .unsol_event = alc662_eeepc_unsol_event,
+               .setup = alc662_eeepc_setup,
                .init_hook = alc662_eeepc_inithook,
        },
        [ALC663_ASUS_M51VA] = {
@@ -16829,8 +16877,8 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .input_mux = &alc663_m51va_capture_source,
                .unsol_event = alc663_m51va_unsol_event,
+               .setup = alc663_m51va_setup,
                .init_hook = alc663_m51va_inithook,
        },
        [ALC663_ASUS_G71V] = {
@@ -16841,8 +16889,8 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .input_mux = &alc662_eeepc_capture_source,
                .unsol_event = alc663_g71v_unsol_event,
+               .setup = alc663_g71v_setup,
                .init_hook = alc663_g71v_inithook,
        },
        [ALC663_ASUS_H13] = {
@@ -16852,7 +16900,6 @@ static struct alc_config_preset alc662_presets[] = {
                .dac_nids = alc662_dac_nids,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .input_mux = &alc663_m51va_capture_source,
                .unsol_event = alc663_m51va_unsol_event,
                .init_hook = alc663_m51va_inithook,
        },
@@ -16866,6 +16913,7 @@ static struct alc_config_preset alc662_presets[] = {
                .channel_mode = alc662_3ST_6ch_modes,
                .input_mux = &alc663_capture_source,
                .unsol_event = alc663_g50v_unsol_event,
+               .setup = alc663_g50v_setup,
                .init_hook = alc663_g50v_inithook,
        },
        [ALC663_ASUS_MODE1] = {
@@ -16879,8 +16927,8 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .input_mux = &alc662_eeepc_capture_source,
                .unsol_event = alc663_mode1_unsol_event,
+               .setup = alc663_mode1_setup,
                .init_hook = alc663_mode1_inithook,
        },
        [ALC662_ASUS_MODE2] = {
@@ -16893,8 +16941,8 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .input_mux = &alc662_eeepc_capture_source,
                .unsol_event = alc662_mode2_unsol_event,
+               .setup = alc662_mode2_setup,
                .init_hook = alc662_mode2_inithook,
        },
        [ALC663_ASUS_MODE3] = {
@@ -16908,8 +16956,8 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .input_mux = &alc662_eeepc_capture_source,
                .unsol_event = alc663_mode3_unsol_event,
+               .setup = alc663_mode3_setup,
                .init_hook = alc663_mode3_inithook,
        },
        [ALC663_ASUS_MODE4] = {
@@ -16923,8 +16971,8 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .input_mux = &alc662_eeepc_capture_source,
                .unsol_event = alc663_mode4_unsol_event,
+               .setup = alc663_mode4_setup,
                .init_hook = alc663_mode4_inithook,
        },
        [ALC663_ASUS_MODE5] = {
@@ -16938,8 +16986,8 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .input_mux = &alc662_eeepc_capture_source,
                .unsol_event = alc663_mode5_unsol_event,
+               .setup = alc663_mode5_setup,
                .init_hook = alc663_mode5_inithook,
        },
        [ALC663_ASUS_MODE6] = {
@@ -16953,8 +17001,8 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .input_mux = &alc662_eeepc_capture_source,
                .unsol_event = alc663_mode6_unsol_event,
+               .setup = alc663_mode6_setup,
                .init_hook = alc663_mode6_inithook,
        },
        [ALC272_DELL] = {
@@ -16968,8 +17016,8 @@ static struct alc_config_preset alc662_presets[] = {
                .num_adc_nids = ARRAY_SIZE(alc272_adc_nids),
                .capsrc_nids = alc272_capsrc_nids,
                .channel_mode = alc662_3ST_2ch_modes,
-               .input_mux = &alc663_m51va_capture_source,
                .unsol_event = alc663_m51va_unsol_event,
+               .setup = alc663_m51va_setup,
                .init_hook = alc663_m51va_inithook,
        },
        [ALC272_DELL_ZM1] = {
@@ -16980,11 +17028,11 @@ static struct alc_config_preset alc662_presets[] = {
                .dac_nids = alc662_dac_nids,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .adc_nids = alc662_adc_nids,
-               .num_adc_nids = ARRAY_SIZE(alc662_adc_nids),
+               .num_adc_nids = 1,
                .capsrc_nids = alc662_capsrc_nids,
                .channel_mode = alc662_3ST_2ch_modes,
-               .input_mux = &alc663_m51va_capture_source,
                .unsol_event = alc663_m51va_unsol_event,
+               .setup = alc663_m51va_setup,
                .init_hook = alc663_m51va_inithook,
        },
        [ALC272_SAMSUNG_NC10] = {
@@ -16995,8 +17043,9 @@ static struct alc_config_preset alc662_presets[] = {
                .dac_nids = alc272_dac_nids,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .input_mux = &alc272_nc10_capture_source,
+               /*.input_mux = &alc272_nc10_capture_source,*/
                .unsol_event = alc663_mode4_unsol_event,
+               .setup = alc663_mode4_setup,
                .init_hook = alc663_mode4_inithook,
        },
 };
@@ -17116,25 +17165,6 @@ static int alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
        return 0;
 }
 
-/* return the index of the src widget from the connection list of the nid.
- * return -1 if not found
- */
-static int alc662_input_pin_idx(struct hda_codec *codec, hda_nid_t nid,
-                               hda_nid_t src)
-{
-       hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
-       int i, conns;
-
-       conns = snd_hda_get_connections(codec, nid, conn_list,
-                                       ARRAY_SIZE(conn_list));
-       if (conns < 0)
-               return -1;
-       for (i = 0; i < conns; i++)
-               if (conn_list[i] == src)
-                       return i;
-       return -1;
-}
-
 static int alc662_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
 {
        unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
@@ -17151,7 +17181,7 @@ static int alc662_auto_create_analog_input_ctls(struct hda_codec *codec,
 
        for (i = 0; i < AUTO_PIN_LAST; i++) {
                if (alc662_is_input_pin(codec, cfg->input_pins[i])) {
-                       idx = alc662_input_pin_idx(codec, 0x0b,
+                       idx = get_connection_index(codec, 0x0b,
                                                   cfg->input_pins[i]);
                        if (idx >= 0) {
                                err = new_analog_input(spec, cfg->input_pins[i],
@@ -17160,7 +17190,7 @@ static int alc662_auto_create_analog_input_ctls(struct hda_codec *codec,
                                if (err < 0)
                                        return err;
                        }
-                       idx = alc662_input_pin_idx(codec, 0x22,
+                       idx = get_connection_index(codec, 0x22,
                                                   cfg->input_pins[i]);
                        if (idx >= 0) {
                                imux->items[imux->num_items].label =
@@ -17349,7 +17379,7 @@ static int patch_alc662(struct hda_codec *codec)
        }
 
        if (board_config != ALC662_AUTO)
-               setup_preset(spec, &alc662_presets[board_config]);
+               setup_preset(codec, &alc662_presets[board_config]);
 
        spec->stream_analog_playback = &alc662_pcm_analog_playback;
        spec->stream_analog_capture = &alc662_pcm_analog_capture;
@@ -17357,12 +17387,15 @@ static int patch_alc662(struct hda_codec *codec)
        spec->stream_digital_playback = &alc662_pcm_digital_playback;
        spec->stream_digital_capture = &alc662_pcm_digital_capture;
 
-       spec->adc_nids = alc662_adc_nids;
-       spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
-       spec->capsrc_nids = alc662_capsrc_nids;
+       if (!spec->adc_nids) {
+               spec->adc_nids = alc662_adc_nids;
+               spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
+       }
+       if (!spec->capsrc_nids)
+               spec->capsrc_nids = alc662_capsrc_nids;
 
        if (!spec->cap_mixer)
-               set_capture_mixer(spec);
+               set_capture_mixer(codec);
        if (codec->vendor_id == 0x10ec0662)
                set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
        else
index 3457f61..f6d7ef4 100644 (file)
@@ -40,6 +40,7 @@ enum {
        STAC_INSERT_EVENT,
        STAC_PWR_EVENT,
        STAC_HP_EVENT,
+       STAC_LO_EVENT,
        STAC_MIC_EVENT,
 };
 
@@ -1806,6 +1807,8 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
                                "Dell Studio 1537", STAC_DELL_M6_DMIC),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a0,
                                "Dell Studio 17", STAC_DELL_M6_DMIC),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02be,
+                               "Dell Studio 1555", STAC_DELL_M6_DMIC),
        {} /* terminator */
 };
 
@@ -1916,6 +1919,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
                      "HP mini 1000", STAC_HP_M4),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b,
                      "HP HDX", STAC_HP_HDX),  /* HDX16 */
+       SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010,
+                     "HP", STAC_HP_DV5),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
                                "unknown Dell", STAC_DELL_M4_1),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
@@ -2261,7 +2266,7 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0227, "Dell Vostro 1400  ", STAC_DELL_BIOS),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x022e, "Dell     ", STAC_DELL_BIOS),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x022f, "Dell Inspiron 1525", STAC_DELL_3ST),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x022f, "Dell Inspiron 1525", STAC_DELL_BIOS),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0242, "Dell     ", STAC_DELL_BIOS),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0243, "Dell     ", STAC_DELL_BIOS),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x02ff, "Dell     ", STAC_DELL_BIOS),
@@ -3320,11 +3325,11 @@ static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol,
 {
        int err;
        err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx,
-                                      "Captuer Volume", vol);
+                                      "Capture Volume", vol);
        if (err < 0)
                return err;
        err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_MUTE, idx,
-                                      "Captuer Switch", sw);
+                                      "Capture Switch", sw);
        if (err < 0)
                return err;
        return 0;
@@ -3696,7 +3701,7 @@ static int set_mic_route(struct hda_codec *codec,
                if (i < 0)
                        return -1;
                mic->mux_idx = i;
-       }  else {
+       }  else if (spec->dmux_nids) {
                /* digital pin */
                mic->mux_idx = 0;
                i = get_connection_index(codec, spec->dmux_nids[0], pin);
@@ -4376,6 +4381,15 @@ static int stac92xx_init(struct hda_codec *codec)
                        hda_nid_t nid = cfg->hp_pins[i];
                        enable_pin_detect(codec, nid, STAC_HP_EVENT);
                }
+               if (cfg->line_out_type == AUTO_PIN_LINE_OUT &&
+                   cfg->speaker_outs > 0) {
+                       /* enable pin-detect for line-outs as well */
+                       for (i = 0; i < cfg->line_outs; i++) {
+                               hda_nid_t nid = cfg->line_out_pins[i];
+                               enable_pin_detect(codec, nid, STAC_LO_EVENT);
+                       }
+               }
+
                /* force to enable the first line-out; the others are set up
                 * in unsol_event
                 */
@@ -4391,7 +4405,8 @@ static int stac92xx_init(struct hda_codec *codec)
        }
        if (spec->auto_mic) {
                /* initialize connection to analog input */
-               snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
+               if (spec->dmux_nids)
+                       snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
                                          AC_VERB_SET_CONNECT_SEL, 0);
                if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT))
                        stac_issue_unsol_event(codec, spec->ext_mic.pin);
@@ -4574,6 +4589,48 @@ static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
        return 0;
 }
 
+static void stac92xx_line_out_detect(struct hda_codec *codec,
+                                    int presence)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
+
+       for (i = 0; i < cfg->line_outs; i++) {
+               if (presence)
+                       break;
+               presence = get_pin_presence(codec, cfg->line_out_pins[i]);
+               if (presence) {
+                       unsigned int pinctl;
+                       pinctl = snd_hda_codec_read(codec,
+                                                   cfg->line_out_pins[i], 0,
+                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+                       if (pinctl & AC_PINCTL_IN_EN)
+                               presence = 0; /* mic- or line-input */
+               }
+       }
+
+       if (presence) {
+               /* disable speakers */
+               for (i = 0; i < cfg->speaker_outs; i++)
+                       stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
+                                               AC_PINCTL_OUT_EN);
+               if (spec->eapd_mask && spec->eapd_switch)
+                       stac_gpio_set(codec, spec->gpio_mask,
+                               spec->gpio_dir, spec->gpio_data &
+                               ~spec->eapd_mask);
+       } else {
+               /* enable speakers */
+               for (i = 0; i < cfg->speaker_outs; i++)
+                       stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
+                                               AC_PINCTL_OUT_EN);
+               if (spec->eapd_mask && spec->eapd_switch)
+                       stac_gpio_set(codec, spec->gpio_mask,
+                               spec->gpio_dir, spec->gpio_data |
+                               spec->eapd_mask);
+       }
+} 
+
 /* return non-zero if the hp-pin of the given array index isn't
  * a jack-detection target
  */
@@ -4626,13 +4683,6 @@ static void stac92xx_hp_detect(struct hda_codec *codec)
                for (i = 0; i < cfg->line_outs; i++)
                        stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
                                                AC_PINCTL_OUT_EN);
-               for (i = 0; i < cfg->speaker_outs; i++)
-                       stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
-                                               AC_PINCTL_OUT_EN);
-               if (spec->eapd_mask && spec->eapd_switch)
-                       stac_gpio_set(codec, spec->gpio_mask,
-                               spec->gpio_dir, spec->gpio_data &
-                               ~spec->eapd_mask);
        } else {
                /* enable lineouts */
                if (spec->hp_switch)
@@ -4641,14 +4691,8 @@ static void stac92xx_hp_detect(struct hda_codec *codec)
                for (i = 0; i < cfg->line_outs; i++)
                        stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
                                                AC_PINCTL_OUT_EN);
-               for (i = 0; i < cfg->speaker_outs; i++)
-                       stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
-                                               AC_PINCTL_OUT_EN);
-               if (spec->eapd_mask && spec->eapd_switch)
-                       stac_gpio_set(codec, spec->gpio_mask,
-                               spec->gpio_dir, spec->gpio_data |
-                               spec->eapd_mask);
        }
+       stac92xx_line_out_detect(codec, presence);
        /* toggle hp outs */
        for (i = 0; i < cfg->hp_outs; i++) {
                unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN;
@@ -4773,6 +4817,7 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
 
        switch (event->type) {
        case STAC_HP_EVENT:
+       case STAC_LO_EVENT:
                stac92xx_hp_detect(codec);
                break;
        case STAC_MIC_EVENT:
@@ -4782,6 +4827,7 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
 
        switch (event->type) {
        case STAC_HP_EVENT:
+       case STAC_LO_EVENT:
        case STAC_MIC_EVENT:
        case STAC_INSERT_EVENT:
        case STAC_PWR_EVENT:
@@ -5776,6 +5822,13 @@ static int patch_stac927x(struct hda_codec *codec)
                /* GPIO2 High = Enable EAPD */
                spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x04;
                spec->gpio_data = 0x04;
+               switch (codec->subsystem_id) {
+               case 0x1028022f:
+                       /* correct EAPD to be GPIO0 */
+                       spec->eapd_mask = spec->gpio_mask = 0x01;
+                       spec->gpio_dir = spec->gpio_data = 0x01;
+                       break;
+               };
                spec->dmic_nids = stac927x_dmic_nids;
                spec->num_dmics = STAC927X_NUM_DMICS;