Merge branch 'topic/hda' into for-linus
authorTakashi Iwai <tiwai@suse.de>
Mon, 25 Oct 2010 08:40:05 +0000 (10:40 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 25 Oct 2010 08:40:05 +0000 (10:40 +0200)
21 files changed:
Documentation/sound/alsa/HD-Audio.txt
include/sound/tlv.h
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_generic.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_atihdmi.c [deleted file]
sound/pci/hda/patch_ca0110.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_intelhdmi.c [deleted file]
sound/pci/hda/patch_nvhdmi.c [deleted file]
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c

index 278cc2122ea056797aa3c6ae04c77b4052f5f5f9..c82beb0076341856ca13b1e1dd125cc440ed439c 100644 (file)
@@ -57,9 +57,11 @@ dead.  However, this detection isn't perfect on some devices.  In such
 a case, you can change the default method via `position_fix` option.
 
 `position_fix=1` means to use LPIB method explicitly.
-`position_fix=2` means to use the position-buffer.  0 is the default
-value, the automatic check and fallback to LPIB as described in the
-above.  If you get a problem of repeated sounds, this option might
+`position_fix=2` means to use the position-buffer.
+`position_fix=3` means to use a combination of both methods, needed
+for some VIA and ATI controllers.  0 is the default value for all other
+controllers, the automatic check and fallback to LPIB as described in
+the above.  If you get a problem of repeated sounds, this option might
 help.
 
 In addition to that, every controller is known to be broken regarding
index 9fd5b19ccf5c05a7002440588041e49b3e9a8e18..7067e2dfb0b992abc09840c1c58c23a4d4d7e351 100644 (file)
 #define SNDRV_CTL_TLVT_DB_MINMAX 4     /* dB scale with min/max */
 #define SNDRV_CTL_TLVT_DB_MINMAX_MUTE 5        /* dB scale with min/max with mute */
 
+#define TLV_DB_SCALE_MASK      0xffff
+#define TLV_DB_SCALE_MUTE      0x10000
 #define TLV_DB_SCALE_ITEM(min, step, mute)                     \
        SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int),      \
-       (min), ((step) & 0xffff) | ((mute) ? 0x10000 : 0)
+       (min), ((step) & TLV_DB_SCALE_MASK) | ((mute) ? TLV_DB_SCALE_MUTE : 0)
 #define DECLARE_TLV_DB_SCALE(name, min, step, mute) \
        unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) }
 
index 9194c3c1d04adf176a01ff91f3a8990327aab6fc..0ea5cc60ac782f1cd9d05161455b1e1a79d4cd85 100644 (file)
@@ -119,47 +119,20 @@ config SND_HDA_CODEC_VIA
          snd-hda-codec-via.
          This module is automatically loaded at probing.
 
-config SND_HDA_CODEC_ATIHDMI
-       bool "Build ATI HDMI HD-audio codec support"
-       default y
-       help
-         Say Y here to include ATI HDMI HD-audio codec support in
-         snd-hda-intel driver, such as ATI RS600 HDMI.
-
-         When the HD-audio driver is built as a module, the codec
-         support code is also built as another module,
-         snd-hda-codec-atihdmi.
-         This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_NVHDMI
-       bool "Build NVIDIA HDMI HD-audio codec support"
-       default y
-       help
-         Say Y here to include NVIDIA HDMI HD-audio codec support in
-         snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
-
-         When the HD-audio driver is built as a module, the codec
-         support code is also built as another module,
-         snd-hda-codec-nvhdmi.
-         This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_INTELHDMI
-       bool "Build INTEL HDMI HD-audio codec support"
+config SND_HDA_CODEC_HDMI
+       bool "Build HDMI/DisplayPort HD-audio codec support"
        select SND_DYNAMIC_MINORS
        default y
        help
-         Say Y here to include INTEL HDMI HD-audio codec support in
-         snd-hda-intel driver, such as Eaglelake integrated HDMI.
+         Say Y here to include HDMI and DisplayPort HD-audio codec
+         support in snd-hda-intel driver.  This includes all AMD/ATI,
+         Intel and Nvidia HDMI/DisplayPort codecs.
 
          When the HD-audio driver is built as a module, the codec
          support code is also built as another module,
-         snd-hda-codec-intelhdmi.
+         snd-hda-codec-hdmi.
          This module is automatically loaded at probing.
 
-config SND_HDA_ELD
-       def_bool y
-       depends on SND_HDA_CODEC_INTELHDMI || SND_HDA_CODEC_NVHDMI
-
 config SND_HDA_CODEC_CIRRUS
        bool "Build Cirrus Logic codec support"
        depends on SND_HDA_INTEL
index 24bc195b02daef99fa675601712f04cfc6eb73e8..17ef3658f34b72d42d1deede9bc5f4efb1054a27 100644 (file)
@@ -3,7 +3,6 @@ snd-hda-intel-objs := hda_intel.o
 snd-hda-codec-y := hda_codec.o
 snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
 snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
-snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
 snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
 snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
 
@@ -12,13 +11,11 @@ snd-hda-codec-cmedia-objs :=        patch_cmedia.o
 snd-hda-codec-analog-objs :=   patch_analog.o
 snd-hda-codec-idt-objs :=      patch_sigmatel.o
 snd-hda-codec-si3054-objs :=   patch_si3054.o
-snd-hda-codec-atihdmi-objs :=  patch_atihdmi.o
 snd-hda-codec-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
-snd-hda-codec-nvhdmi-objs :=   patch_nvhdmi.o
-snd-hda-codec-intelhdmi-objs :=        patch_intelhdmi.o
+snd-hda-codec-hdmi-objs :=     patch_hdmi.o hda_eld.o
 
 # common driver
 obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
@@ -39,9 +36,6 @@ endif
 ifdef CONFIG_SND_HDA_CODEC_SI3054
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o
 endif
-ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
-endif
 ifdef CONFIG_SND_HDA_CODEC_CIRRUS
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o
 endif
@@ -54,11 +48,8 @@ endif
 ifdef CONFIG_SND_HDA_CODEC_VIA
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o
 endif
-ifdef CONFIG_SND_HDA_CODEC_NVHDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-nvhdmi.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_INTELHDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-intelhdmi.o
+ifdef CONFIG_SND_HDA_CODEC_HDMI
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-hdmi.o
 endif
 
 # this must be the last entry after codec drivers;
index 14829210ef0bf4b1bac018d4f5e31a1d1c4728b9..644e3f14f8ca5aea7af2e465450caab86d2f90cb 100644 (file)
@@ -1216,6 +1216,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
        struct hda_codec *c;
        struct hda_cvt_setup *p;
        unsigned int oldval, newval;
+       int type;
        int i;
 
        if (!nid)
@@ -1254,10 +1255,12 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
        p->dirty = 0;
 
        /* make other inactive cvts with the same stream-tag dirty */
+       type = get_wcaps_type(get_wcaps(codec, nid));
        list_for_each_entry(c, &codec->bus->codec_list, list) {
                for (i = 0; i < c->cvt_setups.used; i++) {
                        p = snd_array_elem(&c->cvt_setups, i);
-                       if (!p->active && p->stream_tag == stream_tag)
+                       if (!p->active && p->stream_tag == stream_tag &&
+                           get_wcaps_type(get_wcaps(codec, p->nid)) == type)
                                p->dirty = 1;
                }
        }
@@ -1281,6 +1284,9 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
        if (!nid)
                return;
 
+       if (codec->no_sticky_stream)
+               do_now = 1;
+
        snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
        p = get_hda_cvt_setup(codec, nid);
        if (p) {
@@ -1831,6 +1837,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
        hda_nid_t nid = get_amp_nid(kcontrol);
        int dir = get_amp_direction(kcontrol);
        unsigned int ofs = get_amp_offset(kcontrol);
+       bool min_mute = get_amp_min_mute(kcontrol);
        u32 caps, val1, val2;
 
        if (size < 4 * sizeof(unsigned int))
@@ -1841,6 +1848,8 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
        val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
        val1 += ofs;
        val1 = ((int)val1) * ((int)val2);
+       if (min_mute)
+               val2 |= TLV_DB_SCALE_MUTE;
        if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
                return -EFAULT;
        if (put_user(2 * sizeof(unsigned int), _tlv + 1))
@@ -2228,10 +2237,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
                change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
                                                   HDA_AMP_MUTE,
                                                   *valp ? 0 : HDA_AMP_MUTE);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       if (codec->patch_ops.check_power_status)
-               codec->patch_ops.check_power_status(codec, nid);
-#endif
+       hda_call_check_power_status(codec, nid);
        snd_hda_power_down(codec);
        return change;
 }
@@ -4372,6 +4378,34 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
 }
 
 
+/* add the found input-pin to the cfg->inputs[] table */
+static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
+                                  int type)
+{
+       if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
+               cfg->inputs[cfg->num_inputs].pin = nid;
+               cfg->inputs[cfg->num_inputs].type = type;
+               cfg->num_inputs++;
+       }
+}
+
+/* sort inputs in the order of AUTO_PIN_* type */
+static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
+{
+       int i, j;
+
+       for (i = 0; i < cfg->num_inputs; i++) {
+               for (j = i + 1; j < cfg->num_inputs; j++) {
+                       if (cfg->inputs[i].type > cfg->inputs[j].type) {
+                               struct auto_pin_cfg_item tmp;
+                               tmp = cfg->inputs[i];
+                               cfg->inputs[i] = cfg->inputs[j];
+                               cfg->inputs[j] = tmp;
+                       }
+               }
+       }
+}
+
 /*
  * Parse all pin widgets and store the useful pin nids to cfg
  *
@@ -4385,7 +4419,7 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
  * output, i.e. to line_out_pins[0].  So, line_outs is always positive
  * if any analog output exists.
  *
- * The analog input pins are assigned to input_pins array.
+ * The analog input pins are assigned to inputs array.
  * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
  * respectively.
  */
@@ -4398,6 +4432,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
        short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
        short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
        short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
+       int i;
 
        memset(cfg, 0, sizeof(*cfg));
 
@@ -4468,33 +4503,17 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                        sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
                        cfg->hp_outs++;
                        break;
-               case AC_JACK_MIC_IN: {
-                       int preferred, alt;
-                       if (loc == AC_JACK_LOC_FRONT ||
-                           (loc & 0x30) == AC_JACK_LOC_INTERNAL) {
-                               preferred = AUTO_PIN_FRONT_MIC;
-                               alt = AUTO_PIN_MIC;
-                       } else {
-                               preferred = AUTO_PIN_MIC;
-                               alt = AUTO_PIN_FRONT_MIC;
-                       }
-                       if (!cfg->input_pins[preferred])
-                               cfg->input_pins[preferred] = nid;
-                       else if (!cfg->input_pins[alt])
-                               cfg->input_pins[alt] = nid;
+               case AC_JACK_MIC_IN:
+                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
                        break;
-               }
                case AC_JACK_LINE_IN:
-                       if (loc == AC_JACK_LOC_FRONT)
-                               cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid;
-                       else
-                               cfg->input_pins[AUTO_PIN_LINE] = nid;
+                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
                        break;
                case AC_JACK_CD:
-                       cfg->input_pins[AUTO_PIN_CD] = nid;
+                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
                        break;
                case AC_JACK_AUX:
-                       cfg->input_pins[AUTO_PIN_AUX] = nid;
+                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
                        break;
                case AC_JACK_SPDIF_OUT:
                case AC_JACK_DIG_OTHER_OUT:
@@ -4539,6 +4558,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                        memmove(sequences_hp + i, sequences_hp + i + 1,
                                sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
                }
+               memset(cfg->hp_pins + cfg->hp_outs, 0,
+                      sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
        }
 
        /* sort by sequence */
@@ -4549,21 +4570,6 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
        sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
                              cfg->hp_outs);
 
-       /* if we have only one mic, make it AUTO_PIN_MIC */
-       if (!cfg->input_pins[AUTO_PIN_MIC] &&
-           cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
-               cfg->input_pins[AUTO_PIN_MIC] =
-                       cfg->input_pins[AUTO_PIN_FRONT_MIC];
-               cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0;
-       }
-       /* ditto for line-in */
-       if (!cfg->input_pins[AUTO_PIN_LINE] &&
-           cfg->input_pins[AUTO_PIN_FRONT_LINE]) {
-               cfg->input_pins[AUTO_PIN_LINE] =
-                       cfg->input_pins[AUTO_PIN_FRONT_LINE];
-               cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0;
-       }
-
        /*
         * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
         * as a primary output
@@ -4602,6 +4608,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                break;
        }
 
+       sort_autocfg_input_pins(cfg);
+
        /*
         * debug prints of the parsed results
         */
@@ -4621,14 +4629,13 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
        if (cfg->dig_outs)
                snd_printd("   dig-out=0x%x/0x%x\n",
                           cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
-       snd_printd("   inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
-                  " cd=0x%x, aux=0x%x\n",
-                  cfg->input_pins[AUTO_PIN_MIC],
-                  cfg->input_pins[AUTO_PIN_FRONT_MIC],
-                  cfg->input_pins[AUTO_PIN_LINE],
-                  cfg->input_pins[AUTO_PIN_FRONT_LINE],
-                  cfg->input_pins[AUTO_PIN_CD],
-                  cfg->input_pins[AUTO_PIN_AUX]);
+       snd_printd("   inputs:");
+       for (i = 0; i < cfg->num_inputs; i++) {
+               snd_printdd(" %s=0x%x",
+                           hda_get_autocfg_input_label(codec, cfg, i),
+                           cfg->inputs[i].pin);
+       }
+       snd_printd("\n");
        if (cfg->dig_in_pin)
                snd_printd("   dig-in=0x%x\n", cfg->dig_in_pin);
 
@@ -4636,11 +4643,165 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config);
 
-/* labels for input pins */
-const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
-       "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
-};
-EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
+int snd_hda_get_input_pin_attr(unsigned int def_conf)
+{
+       unsigned int loc = get_defcfg_location(def_conf);
+       unsigned int conn = get_defcfg_connect(def_conf);
+       if (conn == AC_JACK_PORT_NONE)
+               return INPUT_PIN_ATTR_UNUSED;
+       /* Windows may claim the internal mic to be BOTH, too */
+       if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
+               return INPUT_PIN_ATTR_INT;
+       if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
+               return INPUT_PIN_ATTR_INT;
+       if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
+               return INPUT_PIN_ATTR_DOCK;
+       if (loc == AC_JACK_LOC_REAR)
+               return INPUT_PIN_ATTR_REAR;
+       if (loc == AC_JACK_LOC_FRONT)
+               return INPUT_PIN_ATTR_FRONT;
+       return INPUT_PIN_ATTR_NORMAL;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
+
+/**
+ * hda_get_input_pin_label - Give a label for the given input pin
+ *
+ * When check_location is true, the function checks the pin location
+ * for mic and line-in pins, and set an appropriate prefix like "Front",
+ * "Rear", "Internal".
+ */
+
+const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
+                                       int check_location)
+{
+       unsigned int def_conf;
+       static const char *mic_names[] = {
+               "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
+       };
+       int attr;
+
+       def_conf = snd_hda_codec_get_pincfg(codec, pin);
+
+       switch (get_defcfg_device(def_conf)) {
+       case AC_JACK_MIC_IN:
+               if (!check_location)
+                       return "Mic";
+               attr = snd_hda_get_input_pin_attr(def_conf);
+               if (!attr)
+                       return "None";
+               return mic_names[attr - 1];
+       case AC_JACK_LINE_IN:
+               if (!check_location)
+                       return "Line";
+               attr = snd_hda_get_input_pin_attr(def_conf);
+               if (!attr)
+                       return "None";
+               if (attr == INPUT_PIN_ATTR_DOCK)
+                       return "Dock Line";
+               return "Line";
+       case AC_JACK_AUX:
+               return "Aux";
+       case AC_JACK_CD:
+               return "CD";
+       case AC_JACK_SPDIF_IN:
+               return "SPDIF In";
+       case AC_JACK_DIG_OTHER_IN:
+               return "Digital In";
+       default:
+               return "Misc";
+       }
+}
+EXPORT_SYMBOL_HDA(hda_get_input_pin_label);
+
+/* Check whether the location prefix needs to be added to the label.
+ * If all mic-jacks are in the same location (e.g. rear panel), we don't
+ * have to put "Front" prefix to each label.  In such a case, returns false.
+ */
+static int check_mic_location_need(struct hda_codec *codec,
+                                  const struct auto_pin_cfg *cfg,
+                                  int input)
+{
+       unsigned int defc;
+       int i, attr, attr2;
+
+       defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
+       attr = snd_hda_get_input_pin_attr(defc);
+       /* for internal or docking mics, we need locations */
+       if (attr <= INPUT_PIN_ATTR_NORMAL)
+               return 1;
+
+       attr = 0;
+       for (i = 0; i < cfg->num_inputs; i++) {
+               defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
+               attr2 = snd_hda_get_input_pin_attr(defc);
+               if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
+                       if (attr && attr != attr2)
+                               return 1; /* different locations found */
+                       attr = attr2;
+               }
+       }
+       return 0;
+}
+
+/**
+ * hda_get_autocfg_input_label - Get a label for the given input
+ *
+ * Get a label for the given input pin defined by the autocfg item.
+ * Unlike hda_get_input_pin_label(), this function checks all inputs
+ * defined in autocfg and avoids the redundant mic/line prefix as much as
+ * possible.
+ */
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+                                       const struct auto_pin_cfg *cfg,
+                                       int input)
+{
+       int type = cfg->inputs[input].type;
+       int has_multiple_pins = 0;
+
+       if ((input > 0 && cfg->inputs[input - 1].type == type) ||
+           (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
+               has_multiple_pins = 1;
+       if (has_multiple_pins && type == AUTO_PIN_MIC)
+               has_multiple_pins &= check_mic_location_need(codec, cfg, input);
+       return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
+                                      has_multiple_pins);
+}
+EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
+
+/**
+ * snd_hda_add_imux_item - Add an item to input_mux
+ *
+ * When the same label is used already in the existing items, the number
+ * suffix is appended to the label.  This label index number is stored
+ * to type_idx when non-NULL pointer is given.
+ */
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+                         int index, int *type_idx)
+{
+       int i, label_idx = 0;
+       if (imux->num_items >= HDA_MAX_NUM_INPUTS) {
+               snd_printd(KERN_ERR "hda_codec: Too many imux items!\n");
+               return -EINVAL;
+       }
+       for (i = 0; i < imux->num_items; i++) {
+               if (!strncmp(label, imux->items[i].label, strlen(label)))
+                       label_idx++;
+       }
+       if (type_idx)
+               *type_idx = label_idx;
+       if (label_idx > 0)
+               snprintf(imux->items[imux->num_items].label,
+                        sizeof(imux->items[imux->num_items].label),
+                        "%s %d", label, label_idx);
+       else
+               strlcpy(imux->items[imux->num_items].label, label,
+                       sizeof(imux->items[imux->num_items].label));
+       imux->items[imux->num_items].index = index;
+       imux->num_items++;
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_add_imux_item);
 
 
 #ifdef CONFIG_PM
index 62c70224010808dca1b7a8330e28dfa791bbe61e..fdf8d44f8b6b041fc4a9b1e0e63a31315a9fbe3b 100644 (file)
@@ -850,6 +850,7 @@ struct hda_codec {
        unsigned int pin_amp_workaround:1; /* pin out-amp takes index
                                            * (e.g. Conexant codecs)
                                            */
+       unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */
        unsigned int pins_shutup:1;     /* pins are shut up */
        unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -989,6 +990,18 @@ int snd_hda_suspend(struct hda_bus *bus);
 int snd_hda_resume(struct hda_bus *bus);
 #endif
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static inline
+int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+{
+       if (codec->patch_ops.check_power_status)
+               return codec->patch_ops.check_power_status(codec, nid);
+       return 0;
+}
+#else  
+#define hda_call_check_power_status(codec, nid)                0
+#endif
+
 /*
  * get widget information
  */
index 26c3ade735838624c9f6d2ca889b4ca724df96db..cb0c23a6b473e07bd9d768f9430e7cde2342639c 100644 (file)
@@ -332,7 +332,6 @@ int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
        return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
                                                 AC_DIPSIZE_ELD_BUF);
 }
-EXPORT_SYMBOL_HDA(snd_hdmi_get_eld_size);
 
 int snd_hdmi_get_eld(struct hdmi_eld *eld,
                     struct hda_codec *codec, hda_nid_t nid)
@@ -368,7 +367,6 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld,
        kfree(buf);
        return ret;
 }
-EXPORT_SYMBOL_HDA(snd_hdmi_get_eld);
 
 static void hdmi_show_short_audio_desc(struct cea_sad *a)
 {
@@ -407,7 +405,6 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
        }
        buf[j] = '\0';  /* necessary when j == 0 */
 }
-EXPORT_SYMBOL_HDA(snd_print_channel_allocation);
 
 void snd_hdmi_show_eld(struct hdmi_eld *e)
 {
@@ -426,7 +423,6 @@ void snd_hdmi_show_eld(struct hdmi_eld *e)
        for (i = 0; i < e->sad_count; i++)
                hdmi_show_short_audio_desc(e->sad + i);
 }
-EXPORT_SYMBOL_HDA(snd_hdmi_show_eld);
 
 #ifdef CONFIG_PROC_FS
 
@@ -585,7 +581,6 @@ int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
 
        return 0;
 }
-EXPORT_SYMBOL_HDA(snd_hda_eld_proc_new);
 
 void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
 {
@@ -594,7 +589,6 @@ void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
                eld->proc_entry = NULL;
        }
 }
-EXPORT_SYMBOL_HDA(snd_hda_eld_proc_free);
 
 #endif /* CONFIG_PROC_FS */
 
@@ -645,4 +639,3 @@ void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
        pcm->channels_max = min(pcm->channels_max, codec_pars->channels_max);
        pcm->maxbps = min(pcm->maxbps, codec_pars->maxbps);
 }
-EXPORT_SYMBOL_HDA(hdmi_eld_update_pcm_info);
index 5ea21285ee1f6e700fa320b3710495e47f06aca7..fb0582f8d72503905566f3db047e4f8c65fc7f68 100644 (file)
@@ -61,7 +61,6 @@ struct hda_gspec {
        struct hda_gnode *cap_vol_node; /* Node for capture volume */
        unsigned int cur_cap_src;       /* current capture source */
        struct hda_input_mux input_mux;
-       char cap_labels[HDA_MAX_NUM_INPUTS][16];
 
        unsigned int def_amp_in_caps;
        unsigned int def_amp_out_caps;
@@ -506,11 +505,10 @@ static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
  * returns 0 if not found, 1 if found, or a negative error code.
  */
 static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
-                              struct hda_gnode *node)
+                              struct hda_gnode *node, int idx)
 {
        int i, err;
        unsigned int pinctl;
-       char *label;
        const char *type;
 
        if (node->checked)
@@ -523,7 +521,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
                        child = hda_get_node(spec, node->conn_list[i]);
                        if (! child)
                                continue;
-                       err = parse_adc_sub_nodes(codec, spec, child);
+                       err = parse_adc_sub_nodes(codec, spec, child, idx);
                        if (err < 0)
                                return err;
                        if (err > 0) {
@@ -564,9 +562,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
                        return 0;
                type = "Input";
        }
-       label = spec->cap_labels[spec->input_mux.num_items];
-       strcpy(label, type);
-       spec->input_mux.items[spec->input_mux.num_items].label = label;
+       snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL);
 
        /* unmute the PIN external input */
        unmute_input(codec, node, 0); /* index = 0? */
@@ -577,29 +573,6 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
        return 1; /* found */
 }
 
-/* add a capture source element */
-static void add_cap_src(struct hda_gspec *spec, int idx)
-{
-       struct hda_input_mux_item *csrc;
-       char *buf;
-       int num, ocap;
-
-       num = spec->input_mux.num_items;
-       csrc = &spec->input_mux.items[num];
-       buf = spec->cap_labels[num];
-       for (ocap = 0; ocap < num; ocap++) {
-               if (! strcmp(buf, spec->cap_labels[ocap])) {
-                       /* same label already exists,
-                        * put the index number to be unique
-                        */
-                       sprintf(buf, "%s %d", spec->cap_labels[ocap], num);
-                       break;
-               }
-       }
-       csrc->index = idx;
-       spec->input_mux.num_items++;
-}
-
 /*
  * parse input
  */
@@ -624,22 +597,18 @@ static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
        for (i = 0; i < adc_node->nconns; i++) {
                node = hda_get_node(spec, adc_node->conn_list[i]);
                if (node && node->type == AC_WID_PIN) {
-                       err = parse_adc_sub_nodes(codec, spec, node);
+                       err = parse_adc_sub_nodes(codec, spec, node, i);
                        if (err < 0)
                                return err;
-                       else if (err > 0)
-                               add_cap_src(spec, i);
                }
        }
        /* ... then check the rests, more complicated connections */
        for (i = 0; i < adc_node->nconns; i++) {
                node = hda_get_node(spec, adc_node->conn_list[i]);
                if (node && node->type != AC_WID_PIN) {
-                       err = parse_adc_sub_nodes(codec, spec, node);
+                       err = parse_adc_sub_nodes(codec, spec, node, i);
                        if (err < 0)
                                return err;
-                       else if (err > 0)
-                               add_cap_src(spec, i);
                }
        }
 
index 34940a07905192590a056efe734e155d66f575f7..21aa9b0e28f6cb466a81a4d1e4c48622c71f89c9 100644 (file)
@@ -78,8 +78,8 @@ MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
 module_param_array(model, charp, NULL, 0444);
 MODULE_PARM_DESC(model, "Use the given board model.");
 module_param_array(position_fix, int, NULL, 0444);
-MODULE_PARM_DESC(position_fix, "Fix DMA pointer "
-                "(0 = auto, 1 = none, 2 = POSBUF).");
+MODULE_PARM_DESC(position_fix, "DMA pointer read method."
+                "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO).");
 module_param_array(bdl_pos_adj, int, NULL, 0644);
 MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
 module_param_array(probe_mask, int, NULL, 0444);
@@ -305,6 +305,7 @@ enum {
        POS_FIX_AUTO,
        POS_FIX_LPIB,
        POS_FIX_POSBUF,
+       POS_FIX_VIACOMBO,
 };
 
 /* Defines for ATI HD Audio support in SB450 south bridge */
@@ -433,7 +434,6 @@ struct azx {
        unsigned int polling_mode :1;
        unsigned int msi :1;
        unsigned int irq_pending_warned :1;
-       unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
        unsigned int probing :1; /* codec probing phase */
 
        /* for debugging */
@@ -458,6 +458,7 @@ enum {
        AZX_DRIVER_ULI,
        AZX_DRIVER_NVIDIA,
        AZX_DRIVER_TERA,
+       AZX_DRIVER_CTX,
        AZX_DRIVER_GENERIC,
        AZX_NUM_DRIVERS, /* keep this as last entry */
 };
@@ -473,6 +474,7 @@ static char *driver_short_names[] __devinitdata = {
        [AZX_DRIVER_ULI] = "HDA ULI M5461",
        [AZX_DRIVER_NVIDIA] = "HDA NVidia",
        [AZX_DRIVER_TERA] = "HDA Teradici", 
+       [AZX_DRIVER_CTX] = "HDA Creative", 
        [AZX_DRIVER_GENERIC] = "HD-Audio Generic",
 };
 
@@ -563,7 +565,10 @@ static void azx_init_cmd_io(struct azx *chip)
        /* reset the rirb hw write pointer */
        azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
        /* set N=1, get RIRB response interrupt for new entry */
-       azx_writew(chip, RINTCNT, 1);
+       if (chip->driver_type == AZX_DRIVER_CTX)
+               azx_writew(chip, RINTCNT, 0xc0);
+       else
+               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);
@@ -1136,8 +1141,11 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
        /* clear rirb int */
        status = azx_readb(chip, RIRBSTS);
        if (status & RIRB_INT_MASK) {
-               if (status & RIRB_INT_RESPONSE)
+               if (status & RIRB_INT_RESPONSE) {
+                       if (chip->driver_type == AZX_DRIVER_CTX)
+                               udelay(80);
                        azx_update_rirb(chip);
+               }
                azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
        }
 
@@ -1309,11 +1317,8 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
        azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr));
 
        /* enable the position buffer */
-       if (chip->position_fix[0] == POS_FIX_POSBUF ||
-           chip->position_fix[0] == POS_FIX_AUTO ||
-           chip->position_fix[1] == POS_FIX_POSBUF ||
-           chip->position_fix[1] == POS_FIX_AUTO ||
-           chip->via_dmapos_patch) {
+       if (chip->position_fix[0] != POS_FIX_LPIB ||
+           chip->position_fix[1] != POS_FIX_LPIB) {
                if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
                        azx_writel(chip, DPLBASE,
                                (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
@@ -1647,7 +1652,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
        struct azx_dev *azx_dev = get_azx_dev(substream);
        struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
        struct snd_pcm_runtime *runtime = substream->runtime;
-       unsigned int bufsize, period_bytes, format_val;
+       unsigned int bufsize, period_bytes, format_val, stream_tag;
        int err;
 
        azx_stream_reset(chip, azx_dev);
@@ -1689,7 +1694,12 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
        else
                azx_dev->fifo_size = 0;
 
-       return snd_hda_codec_prepare(apcm->codec, hinfo, azx_dev->stream_tag,
+       stream_tag = azx_dev->stream_tag;
+       /* CA-IBG chips need the playback stream starting from 1 */
+       if (chip->driver_type == AZX_DRIVER_CTX &&
+           stream_tag > chip->capture_streams)
+               stream_tag -= chip->capture_streams;
+       return snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
                                     azx_dev->format_val, substream);
 }
 
@@ -1852,20 +1862,21 @@ static unsigned int azx_get_position(struct azx *chip,
                                     struct azx_dev *azx_dev)
 {
        unsigned int pos;
+       int stream = azx_dev->substream->stream;
 
-       if (chip->via_dmapos_patch)
+       switch (chip->position_fix[stream]) {
+       case POS_FIX_LPIB:
+               /* read LPIB */
+               pos = azx_sd_readl(azx_dev, SD_LPIB);
+               break;
+       case POS_FIX_VIACOMBO:
                pos = azx_via_get_position(chip, azx_dev);
-       else {
-               int stream = azx_dev->substream->stream;
-               if (chip->position_fix[stream] == POS_FIX_POSBUF ||
-                   chip->position_fix[stream] == POS_FIX_AUTO) {
-                       /* use the position buffer */
-                       pos = le32_to_cpu(*azx_dev->posbuf);
-               } else {
-                       /* read LPIB */
-                       pos = azx_sd_readl(azx_dev, SD_LPIB);
-               }
+               break;
+       default:
+               /* use the position buffer */
+               pos = le32_to_cpu(*azx_dev->posbuf);
        }
+
        if (pos >= azx_dev->bufsize)
                pos = 0;
        return pos;
@@ -2313,19 +2324,10 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
        switch (fix) {
        case POS_FIX_LPIB:
        case POS_FIX_POSBUF:
+       case POS_FIX_VIACOMBO:
                return fix;
        }
 
-       /* Check VIA/ATI HD Audio Controller exist */
-       switch (chip->driver_type) {
-       case AZX_DRIVER_VIA:
-       case AZX_DRIVER_ATI:
-               chip->via_dmapos_patch = 1;
-               /* Use link position directly, avoid any transfer problem. */
-               return POS_FIX_LPIB;
-       }
-       chip->via_dmapos_patch = 0;
-
        q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
        if (q) {
                printk(KERN_INFO
@@ -2334,6 +2336,15 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
                       q->value, q->subvendor, q->subdevice);
                return q->value;
        }
+
+       /* Check VIA/ATI HD Audio Controller exist */
+       switch (chip->driver_type) {
+       case AZX_DRIVER_VIA:
+       case AZX_DRIVER_ATI:
+               /* Use link position directly, avoid any transfer problem. */
+               return POS_FIX_VIACOMBO;
+       }
+
        return POS_FIX_AUTO;
 }
 
@@ -2735,25 +2746,17 @@ static void __devexit azx_remove(struct pci_dev *pci)
 
 /* PCI IDs */
 static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
-       /* ICH 6..10 */
-       { PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH },
-       { PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH },
-       { PCI_DEVICE(0x8086, 0x269a), .driver_data = AZX_DRIVER_ICH },
-       { PCI_DEVICE(0x8086, 0x284b), .driver_data = AZX_DRIVER_ICH },
-       { PCI_DEVICE(0x8086, 0x2911), .driver_data = AZX_DRIVER_ICH },
-       { PCI_DEVICE(0x8086, 0x293e), .driver_data = AZX_DRIVER_ICH },
-       { PCI_DEVICE(0x8086, 0x293f), .driver_data = AZX_DRIVER_ICH },
-       { PCI_DEVICE(0x8086, 0x3a3e), .driver_data = AZX_DRIVER_ICH },
-       { PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH },
-       /* PCH */
-       { PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_ICH },
-       { PCI_DEVICE(0x8086, 0x3b57), .driver_data = AZX_DRIVER_ICH },
        /* CPT */
        { PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH },
        /* PBG */
        { PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH },
        /* SCH */
        { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
+       /* Generic Intel */
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID),
+         .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+         .class_mask = 0xffffff,
+         .driver_data = AZX_DRIVER_ICH },
        /* ATI SB 450/600 */
        { PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI },
        { PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI },
@@ -2794,11 +2797,13 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
        { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
          .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
          .class_mask = 0xffffff,
-         .driver_data = AZX_DRIVER_GENERIC },
+         .driver_data = AZX_DRIVER_CTX },
 #else
        /* this entry seems still valid -- i.e. without emu20kx chip */
-       { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC },
+       { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_CTX },
 #endif
+       /* Vortex86MX */
+       { PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
        /* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */
        { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
          .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
index 28ab4aead48fd17db36098f2eb36b306f4a5d534..46bbefe2e4a9465189d2341d42825f1439d17a40 100644 (file)
  */
 #define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs)           \
        ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23))
+#define HDA_AMP_VAL_MIN_MUTE (1<<29)
 #define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \
        HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0)
 /* mono volume with index (index=0,1,...) (channel=1,2) */
-#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, dir, flags) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx,  \
          .subdevice = HDA_SUBDEV_AMP_FLAG, \
          .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
          .get = snd_hda_mixer_amp_volume_get, \
          .put = snd_hda_mixer_amp_volume_put, \
          .tlv = { .c = snd_hda_mixer_amp_tlv },                \
-         .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+         .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, dir) | flags }
 /* stereo volume with index */
 #define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \
-       HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction)
+       HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction, 0)
 /* mono volume */
 #define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \
-       HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+       HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction, 0)
 /* stereo volume */
 #define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \
        HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction)
+/* stereo volume with min=mute */
+#define HDA_CODEC_VOLUME_MIN_MUTE(xname, nid, xindex, direction) \
+       HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, 3, xindex, direction, \
+                                 HDA_AMP_VAL_MIN_MUTE)
 /* mono mute switch with index (index=0,1,...) (channel=1,2) */
 #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
@@ -215,7 +220,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
  */
 #define HDA_MAX_NUM_INPUTS     16
 struct hda_input_mux_item {
-       const char *label;
+       char label[32];
        unsigned int index;
 };
 struct hda_input_mux {
@@ -366,9 +371,7 @@ struct hda_bus_unsolicited {
 
 enum {
        AUTO_PIN_MIC,
-       AUTO_PIN_FRONT_MIC,
-       AUTO_PIN_LINE,
-       AUTO_PIN_FRONT_LINE,
+       AUTO_PIN_LINE_IN,
        AUTO_PIN_CD,
        AUTO_PIN_AUX,
        AUTO_PIN_LAST
@@ -380,9 +383,33 @@ enum {
        AUTO_PIN_HP_OUT
 };
 
-extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST];
-
 #define AUTO_CFG_MAX_OUTS      5
+#define AUTO_CFG_MAX_INS       8
+
+struct auto_pin_cfg_item {
+       hda_nid_t pin;
+       int type;
+};
+
+struct auto_pin_cfg;
+const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
+                                   int check_location);
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+                                       const struct auto_pin_cfg *cfg,
+                                       int input);
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+                         int index, int *type_index_ret);
+
+enum {
+       INPUT_PIN_ATTR_UNUSED,  /* pin not connected */
+       INPUT_PIN_ATTR_INT,     /* internal mic/line-in */
+       INPUT_PIN_ATTR_DOCK,    /* docking mic/line-in */
+       INPUT_PIN_ATTR_NORMAL,  /* mic/line-in jack */
+       INPUT_PIN_ATTR_FRONT,   /* mic/line-in jack in front */
+       INPUT_PIN_ATTR_REAR,    /* mic/line-in jack in rear */
+};
+
+int snd_hda_get_input_pin_attr(unsigned int def_conf);
 
 struct auto_pin_cfg {
        int line_outs;
@@ -393,7 +420,8 @@ struct auto_pin_cfg {
        int hp_outs;
        int line_out_type;      /* AUTO_PIN_XXX_OUT */
        hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
-       hda_nid_t input_pins[AUTO_PIN_LAST];
+       int num_inputs;
+       struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
        int dig_outs;
        hda_nid_t dig_out_pins[2];
        hda_nid_t dig_in_pin;
@@ -558,6 +586,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
 #define get_amp_direction(kc)  (((kc)->private_value >> 18) & 0x1)
 #define get_amp_index(kc)      (((kc)->private_value >> 19) & 0xf)
 #define get_amp_offset(kc)     (((kc)->private_value >> 23) & 0x3f)
+#define get_amp_min_mute(kc)   (((kc)->private_value >> 29) & 0x1)
 
 /*
  * CEA Short Audio Descriptor data
index 10bbbaf6ebc3d2b0531ba2fb1ab7e3f2be1f8994..f7ff3f7ccb8e938e7f074afea766495907bc7e1e 100644 (file)
@@ -1276,6 +1276,7 @@ static int patch_ad1986a(struct hda_codec *codec)
        spec->multiout.no_share_stream = 1;
 
        codec->no_trigger_sense = 1;
+       codec->no_sticky_stream = 1;
 
        return 0;
 }
@@ -1463,6 +1464,7 @@ static int patch_ad1983(struct hda_codec *codec)
        codec->patch_ops = ad198x_patch_ops;
 
        codec->no_trigger_sense = 1;
+       codec->no_sticky_stream = 1;
 
        return 0;
 }
@@ -1917,6 +1919,7 @@ static int patch_ad1981(struct hda_codec *codec)
        }
 
        codec->no_trigger_sense = 1;
+       codec->no_sticky_stream = 1;
 
        return 0;
 }
@@ -2880,7 +2883,7 @@ static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
 
 /* create input playback/capture controls for the given pin */
 static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
-                           const char *ctlname, int boost)
+                           const char *ctlname, int ctlidx, int boost)
 {
        char name[32];
        int err, idx;
@@ -2909,25 +2912,27 @@ static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
 }
 
 /* create playback/capture controls for input pins */
-static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
+static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec,
                                                const struct auto_pin_cfg *cfg)
 {
+       struct ad198x_spec *spec = codec->spec;
        struct hda_input_mux *imux = &spec->private_imux;
-       int i, err;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               err = new_analog_input(spec, cfg->input_pins[i],
-                                      auto_pin_cfg_labels[i],
-                                      i <= AUTO_PIN_FRONT_MIC);
+       int i, err, type, type_idx;
+
+       for (i = 0; i < cfg->num_inputs; i++) {
+               const char *label;
+               type = cfg->inputs[i].type;
+               label = hda_get_autocfg_input_label(codec, cfg, i);
+               snd_hda_add_imux_item(imux, label,
+                                     ad1988_pin_to_adc_idx(cfg->inputs[i].pin),
+                                     &type_idx);
+               err = new_analog_input(spec, cfg->inputs[i].pin,
+                                      label, type_idx,
+                                      type == AUTO_PIN_MIC);
                if (err < 0)
                        return err;
-               imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
-               imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
-               imux->num_items++;
        }
-       imux->items[imux->num_items].label = "Mix";
-       imux->items[imux->num_items].index = 9;
-       imux->num_items++;
+       snd_hda_add_imux_item(imux, "Mix", 9, NULL);
 
        if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
                               "Analog Mix Playback Volume",
@@ -2994,12 +2999,11 @@ static void ad1988_auto_init_extra_out(struct hda_codec *codec)
 static void ad1988_auto_init_analog_input(struct hda_codec *codec)
 {
        struct ad198x_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
        int i, idx;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
-               if (! nid)
-                       continue;
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                switch (nid) {
                case 0x15: /* port-C */
                        snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
@@ -3009,7 +3013,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec)
                        break;
                }
                snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+                                   i == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
                if (nid != AD1988_PIN_CD_NID)
                        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
                                            AMP_OUT_MUTE);
@@ -3040,7 +3044,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
                                                "Speaker")) < 0 ||
            (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
                                                "Headphone")) < 0 ||
-           (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+           (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
                return err;
 
        spec->multiout.max_channels = spec->multiout.num_dacs * 2;
@@ -3235,6 +3239,7 @@ static int patch_ad1988(struct hda_codec *codec)
        spec->vmaster_nid = 0x04;
 
        codec->no_trigger_sense = 1;
+       codec->no_sticky_stream = 1;
 
        return 0;
 }
@@ -3449,6 +3454,7 @@ static int patch_ad1884(struct hda_codec *codec)
        codec->patch_ops = ad198x_patch_ops;
 
        codec->no_trigger_sense = 1;
+       codec->no_sticky_stream = 1;
 
        return 0;
 }
@@ -4422,6 +4428,7 @@ static int patch_ad1884a(struct hda_codec *codec)
        }
 
        codec->no_trigger_sense = 1;
+       codec->no_sticky_stream = 1;
 
        return 0;
 }
@@ -4761,6 +4768,7 @@ static int patch_ad1882(struct hda_codec *codec)
        }
 
        codec->no_trigger_sense = 1;
+       codec->no_sticky_stream = 1;
 
        return 0;
 }
diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
deleted file mode 100644 (file)
index fb684f0..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * HD audio interface patch for ATI HDMI codecs
- *
- * Copyright (c) 2006 ATI Technologies Inc.
- *
- *
- *  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 <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-struct atihdmi_spec {
-       struct hda_multi_out multiout;
-
-       struct hda_pcm pcm_rec;
-};
-
-#define CVT_NID                0x02    /* audio converter */
-#define PIN_NID                0x03    /* HDMI output pin */
-
-static struct hda_verb atihdmi_basic_init[] = {
-       /* enable digital output on pin widget */
-       { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-       {} /* terminator */
-};
-
-/*
- * Controls
- */
-static int atihdmi_build_controls(struct hda_codec *codec)
-{
-       struct atihdmi_spec *spec = codec->spec;
-       int err;
-
-       err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
-       if (err < 0)
-               return err;
-
-       return 0;
-}
-
-static int atihdmi_init(struct hda_codec *codec)
-{
-       snd_hda_sequence_write(codec, atihdmi_basic_init);
-       /* SI codec requires to unmute the pin */
-       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);
-       return 0;
-}
-
-/*
- * Digital out
- */
-static int atihdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
-                                    struct hda_codec *codec,
-                                    struct snd_pcm_substream *substream)
-{
-       struct atihdmi_spec *spec = codec->spec;
-       return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int atihdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
-                                     struct hda_codec *codec,
-                                     struct snd_pcm_substream *substream)
-{
-       struct atihdmi_spec *spec = codec->spec;
-       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int atihdmi_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 atihdmi_spec *spec = codec->spec;
-       int chans = substream->runtime->channels;
-       int i, err;
-
-       err = snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
-                                           format, substream);
-       if (err < 0)
-               return err;
-       snd_hda_codec_write(codec, CVT_NID, 0, AC_VERB_SET_CVT_CHAN_COUNT,
-                           chans - 1);
-       /* FIXME: XXX */
-       for (i = 0; i < chans; i++) {
-               snd_hda_codec_write(codec, CVT_NID, 0,
-                                   AC_VERB_SET_HDMI_CHAN_SLOT,
-                                   (i << 4) | i);
-       }
-       return 0;
-}
-
-static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = CVT_NID, /* NID to query formats and rates and setup streams */
-       .ops = {
-               .open = atihdmi_dig_playback_pcm_open,
-               .close = atihdmi_dig_playback_pcm_close,
-               .prepare = atihdmi_dig_playback_pcm_prepare
-       },
-};
-
-static int atihdmi_build_pcms(struct hda_codec *codec)
-{
-       struct atihdmi_spec *spec = codec->spec;
-       struct hda_pcm *info = &spec->pcm_rec;
-       unsigned int chans;
-
-       codec->num_pcms = 1;
-       codec->pcm_info = info;
-
-       info->name = "ATI HDMI";
-       info->pcm_type = HDA_PCM_TYPE_HDMI;
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback;
-
-       /* FIXME: we must check ELD and change the PCM parameters dynamically
-        */
-       chans = get_wcaps(codec, CVT_NID);
-       chans = get_wcaps_channels(chans);
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
-
-       return 0;
-}
-
-static void atihdmi_free(struct hda_codec *codec)
-{
-       kfree(codec->spec);
-}
-
-static struct hda_codec_ops atihdmi_patch_ops = {
-       .build_controls = atihdmi_build_controls,
-       .build_pcms = atihdmi_build_pcms,
-       .init = atihdmi_init,
-       .free = atihdmi_free,
-};
-
-static int patch_atihdmi(struct hda_codec *codec)
-{
-       struct atihdmi_spec *spec;
-
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
-
-       spec->multiout.num_dacs = 0;      /* no analog */
-       spec->multiout.max_channels = 2;
-       /* NID for copying analog to digital,
-        * seems to be unused in pure-digital
-        * case.
-        */
-       spec->multiout.dig_out_nid = CVT_NID;
-
-       codec->patch_ops = atihdmi_patch_ops;
-
-       return 0;
-}
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_atihdmi[] = {
-       { .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
-       { .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
-       { .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
-       { .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
-       { .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },
-       { .id = 0x17e80047, .name = "Chrontel HDMI",  .patch = patch_atihdmi },
-       {} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:1002793c");
-MODULE_ALIAS("snd-hda-codec-id:10027919");
-MODULE_ALIAS("snd-hda-codec-id:1002791a");
-MODULE_ALIAS("snd-hda-codec-id:1002aa01");
-MODULE_ALIAS("snd-hda-codec-id:10951390");
-MODULE_ALIAS("snd-hda-codec-id:17e80047");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("ATI HDMI HD-audio codec");
-
-static struct hda_codec_preset_list atihdmi_list = {
-       .preset = snd_hda_preset_atihdmi,
-       .owner = THIS_MODULE,
-};
-
-static int __init patch_atihdmi_init(void)
-{
-       return snd_hda_add_codec_preset(&atihdmi_list);
-}
-
-static void __exit patch_atihdmi_exit(void)
-{
-       snd_hda_delete_codec_preset(&atihdmi_list);
-}
-
-module_init(patch_atihdmi_init)
-module_exit(patch_atihdmi_exit)
index af478019088eea787528f115df7a5f7e4270f701..46c8bf48c31fb7c0dfeea8881d1ff4784697a564 100644 (file)
@@ -468,13 +468,13 @@ static void parse_input(struct hda_codec *codec)
                        spec->dig_in = nid;
                        continue;
                }
-               for (j = 0; j < AUTO_PIN_LAST; j++)
-                       if (cfg->input_pins[j] == pin)
+               for (j = 0; j < cfg->num_inputs; j++)
+                       if (cfg->inputs[j].pin == pin)
                                break;
-               if (j >= AUTO_PIN_LAST)
+               if (j >= cfg->num_inputs)
                        continue;
                spec->input_pins[n] = pin;
-               spec->input_labels[n] = auto_pin_cfg_labels[j];
+               spec->input_labels[n] = hda_get_input_pin_label(codec, pin, 1);
                spec->adcs[n] = nid;
                n++;
        }
@@ -489,7 +489,7 @@ static void parse_digital(struct hda_codec *codec)
        if (cfg->dig_outs &&
            snd_hda_get_connections(codec, cfg->dig_out_pins[0],
                                    &spec->dig_out, 1) == 1)
-               spec->multiout.dig_out_nid = cfg->dig_out_pins[0];
+               spec->multiout.dig_out_nid = spec->dig_out;
 }
 
 static int ca0110_parse_auto_config(struct hda_codec *codec)
index 488fd9ade1ba2bf7b306bf6e48ae106ef823294b..460fb2ef7e394614a4045a51d4306c429e818d6a 100644 (file)
@@ -65,6 +65,7 @@ struct cs_spec {
 
 /* available models */
 enum {
+       CS420X_MBP53,
        CS420X_MBP55,
        CS420X_IMAC27,
        CS420X_AUTO,
@@ -329,12 +330,12 @@ 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];
+       hda_nid_t pin = cfg->inputs[idx].pin;
        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);
+       return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT);
 }
 
 static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
@@ -424,10 +425,8 @@ static int parse_input(struct hda_codec *codec)
        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;
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t pin = cfg->inputs[i].pin;
                spec->input_idx[spec->num_inputs] = i;
                spec->capsrc_idx[i] = spec->num_inputs++;
                spec->cur_input = i;
@@ -438,16 +437,17 @@ static int parse_input(struct hda_codec *codec)
 
        /* 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])) {
+           cfg->inputs[0].type == AUTO_PIN_MIC &&
+           cfg->inputs[1].type == AUTO_PIN_MIC) {
+               if (is_ext_mic(codec, cfg->inputs[0].pin)) {
+                       if (!is_ext_mic(codec, cfg->inputs[1].pin)) {
                                spec->mic_detect = 1;
-                               spec->automic_idx = AUTO_PIN_FRONT_MIC;
+                               spec->automic_idx = 0;
                        }
                } else {
-                       if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+                       if (is_ext_mic(codec, cfg->inputs[1].pin)) {
                                spec->mic_detect = 1;
-                               spec->automic_idx = AUTO_PIN_MIC;
+                               spec->automic_idx = 1;
                        }
                }
        }
@@ -674,6 +674,7 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct cs_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        unsigned int idx;
 
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
@@ -682,7 +683,8 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
        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]);
+       strcpy(uinfo->value.enumerated.name,
+              hda_get_input_pin_label(codec, cfg->inputs[idx].pin, 1));
        return 0;
 }
 
@@ -740,6 +742,27 @@ static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
        return bind;
 }
 
+/* add a (input-boost) volume control to the given input pin */
+static int add_input_volume_control(struct hda_codec *codec,
+                                   struct auto_pin_cfg *cfg,
+                                   int item)
+{
+       hda_nid_t pin = cfg->inputs[item].pin;
+       u32 caps;
+       const char *label;
+       struct snd_kcontrol *kctl;
+               
+       if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP))
+               return 0;
+       caps = query_amp_caps(codec, pin, HDA_INPUT);
+       caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+       if (caps <= 1)
+               return 0;
+       label = hda_get_autocfg_input_label(codec, cfg, item);
+       return add_volume(codec, label, 0,
+                         HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl);
+}
+
 static int build_input(struct hda_codec *codec)
 {
        struct cs_spec *spec = codec->spec;
@@ -779,6 +802,12 @@ static int build_input(struct hda_codec *codec)
                        return err;
        }
 
+       for (i = 0; i < spec->num_inputs; i++) {
+               err = add_input_volume_control(codec, &spec->autocfg, i);
+               if (err < 0)
+                       return err;
+       }
+
        return 0;
 }
 
@@ -838,7 +867,8 @@ static void cs_automute(struct hda_codec *codec)
                                    AC_VERB_SET_PIN_WIDGET_CONTROL,
                                    hp_present ? 0 : PIN_OUT);
        }
-       if (spec->board_config == CS420X_MBP55 ||
+       if (spec->board_config == CS420X_MBP53 ||
+           spec->board_config == CS420X_MBP55 ||
            spec->board_config == CS420X_IMAC27) {
                unsigned int gpio = hp_present ? 0x02 : 0x08;
                snd_hda_codec_write(codec, 0x01, 0,
@@ -853,15 +883,12 @@ static void cs_automic(struct hda_codec *codec)
        hda_nid_t nid;
        unsigned int present;
        
-       nid = cfg->input_pins[spec->automic_idx];
+       nid = cfg->inputs[spec->automic_idx].pin;
        present = snd_hda_jack_detect(codec, nid);
        if (present)
                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);
-       }
+       else
+               change_cur_input(codec, !spec->automic_idx, 0);
 }
 
 /*
@@ -918,14 +945,14 @@ static void init_input(struct hda_codec *codec)
        unsigned int coef;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
+       for (i = 0; i < cfg->num_inputs; i++) {
                unsigned int ctl;
-               hda_nid_t pin = cfg->input_pins[i];
-               if (!pin || !spec->adc_nid[i])
+               hda_nid_t pin = cfg->inputs[i].pin;
+               if (!spec->adc_nid[i])
                        continue;
                /* set appropriate pin control and mute first */
                ctl = PIN_IN;
-               if (i <= AUTO_PIN_FRONT_MIC) {
+               if (cfg->inputs[i].type == AUTO_PIN_MIC) {
                        unsigned int caps = snd_hda_query_pin_caps(codec, pin);
                        caps >>= AC_PINCAP_VREF_SHIFT;
                        if (caps & AC_PINCAP_VREF_80)
@@ -1130,6 +1157,7 @@ static int cs_parse_auto_config(struct hda_codec *codec)
 }
 
 static const char *cs420x_models[CS420X_MODELS] = {
+       [CS420X_MBP53] = "mbp53",
        [CS420X_MBP55] = "mbp55",
        [CS420X_IMAC27] = "imac27",
        [CS420X_AUTO] = "auto",
@@ -1137,7 +1165,9 @@ static const char *cs420x_models[CS420X_MODELS] = {
 
 
 static struct snd_pci_quirk cs420x_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
        SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
+       SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
        SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),
        {} /* terminator */
 };
@@ -1147,6 +1177,20 @@ struct cs_pincfg {
        u32 val;
 };
 
+static struct cs_pincfg mbp53_pincfgs[] = {
+       { 0x09, 0x012b4050 },
+       { 0x0a, 0x90100141 },
+       { 0x0b, 0x90100140 },
+       { 0x0c, 0x018b3020 },
+       { 0x0d, 0x90a00110 },
+       { 0x0e, 0x400000f0 },
+       { 0x0f, 0x01cbe030 },
+       { 0x10, 0x014be060 },
+       { 0x12, 0x400000f0 },
+       { 0x15, 0x400000f0 },
+       {} /* terminator */
+};
+
 static struct cs_pincfg mbp55_pincfgs[] = {
        { 0x09, 0x012b4030 },
        { 0x0a, 0x90100121 },
@@ -1176,6 +1220,7 @@ static struct cs_pincfg imac27_pincfgs[] = {
 };
 
 static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
+       [CS420X_MBP53] = mbp53_pincfgs,
        [CS420X_MBP55] = mbp55_pincfgs,
        [CS420X_IMAC27] = imac27_pincfgs,
 };
@@ -1208,6 +1253,7 @@ static int patch_cs420x(struct hda_codec *codec)
 
        switch (spec->board_config) {
        case CS420X_IMAC27:
+       case CS420X_MBP53:
        case CS420X_MBP55:
                /* GPIO1 = headphones */
                /* GPIO3 = speakers */
index 972e7c453b3d6c8320d58b6aebe6ef41fc259920..6361f752b5f35cfddff7f8423f0d5d185a9003d8 100644 (file)
@@ -57,6 +57,12 @@ struct conexant_jack {
 
 };
 
+struct pin_dac_pair {
+       hda_nid_t pin;
+       hda_nid_t dac;
+       int type;
+};
+
 struct conexant_spec {
 
        struct snd_kcontrol_new *mixers[5];
@@ -77,6 +83,7 @@ struct conexant_spec {
        unsigned int cur_eapd;
        unsigned int hp_present;
        unsigned int auto_mic;
+       int auto_mic_ext;               /* autocfg.inputs[] index for ext mic */
        unsigned int need_dac_fix;
 
        /* capture */
@@ -110,9 +117,12 @@ struct conexant_spec {
        struct auto_pin_cfg autocfg;
        struct hda_input_mux private_imux;
        hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+       struct pin_dac_pair dac_info[8];
+       int dac_info_filled;
 
-       unsigned int dell_automute;
        unsigned int port_d_mode;
+       unsigned int auto_mute:1;       /* used in auto-parser */
+       unsigned int dell_automute:1;
        unsigned int dell_vostro:1;
        unsigned int ideapad:1;
        unsigned int thinkpad:1;
@@ -3065,7 +3075,7 @@ enum {
        CXT5066_LAPTOP,         /* Laptops w/ EAPD support */
        CXT5066_DELL_LAPTOP,    /* Dell Laptop */
        CXT5066_OLPC_XO_1_5,    /* OLPC XO 1.5 */
-       CXT5066_DELL_VOSTO,     /* Dell Vostro 1015i */
+       CXT5066_DELL_VOSTRO,    /* Dell Vostro 1015i */
        CXT5066_IDEAPAD,        /* Lenovo IdeaPad U150 */
        CXT5066_THINKPAD,       /* Lenovo ThinkPad T410s, others? */
        CXT5066_HP_LAPTOP,      /* HP Laptop */
@@ -3076,25 +3086,26 @@ static const char *cxt5066_models[CXT5066_MODELS] = {
        [CXT5066_LAPTOP]        = "laptop",
        [CXT5066_DELL_LAPTOP]   = "dell-laptop",
        [CXT5066_OLPC_XO_1_5]   = "olpc-xo-1_5",
-       [CXT5066_DELL_VOSTO]    = "dell-vostro",
+       [CXT5066_DELL_VOSTRO]   = "dell-vostro",
        [CXT5066_IDEAPAD]       = "ideapad",
        [CXT5066_THINKPAD]      = "thinkpad",
        [CXT5066_HP_LAPTOP]     = "hp-laptop",
 };
 
 static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
-       SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
-                     CXT5066_LAPTOP),
+       SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD),
+       SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO),
        SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
                      CXT5066_DELL_LAPTOP),
-       SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
-       SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTO),
-       SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO),
+       SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO),
        SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
        SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP),
        SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD),
        SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5),
        SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5),
+       SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
+                     CXT5066_LAPTOP),
+       SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
        SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
        SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
        SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD),
@@ -3196,7 +3207,7 @@ static int patch_cxt5066(struct hda_codec *codec)
                spec->capture_prepare = cxt5066_olpc_capture_prepare;
                spec->capture_cleanup = cxt5066_olpc_capture_cleanup;
                break;
-       case CXT5066_DELL_VOSTO:
+       case CXT5066_DELL_VOSTRO:
                codec->patch_ops.init = cxt5066_init;
                codec->patch_ops.unsol_event = cxt5066_vostro_event;
                spec->init_verbs[0] = cxt5066_init_verbs_vostro;
@@ -3253,6 +3264,604 @@ static int patch_cxt5066(struct hda_codec *codec)
        return 0;
 }
 
+/*
+ * Automatic parser for CX20641 & co
+ */
+
+static hda_nid_t cx_auto_adc_nids[] = { 0x14 };
+
+/* get the connection index of @nid in the widget @mux */
+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;
+}
+
+/* get an unassigned DAC from the given list.
+ * Return the nid if found and reduce the DAC list, or return zero if
+ * not found
+ */
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin,
+                                   hda_nid_t *dacs, int *num_dacs)
+{
+       int i, nums = *num_dacs;
+       hda_nid_t ret = 0;
+
+       for (i = 0; i < nums; i++) {
+               if (get_connection_index(codec, pin, dacs[i]) >= 0) {
+                       ret = dacs[i];
+                       break;
+               }
+       }
+       if (!ret)
+               return 0;
+       if (--nums > 0)
+               memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t));
+       *num_dacs = nums;
+       return ret;
+}
+
+#define MAX_AUTO_DACS  5
+
+/* fill analog DAC list from the widget tree */
+static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs)
+{
+       hda_nid_t nid, end_nid;
+       int nums = 0;
+
+       end_nid = codec->start_nid + codec->num_nodes;
+       for (nid = codec->start_nid; nid < end_nid; nid++) {
+               unsigned int wcaps = get_wcaps(codec, nid);
+               unsigned int type = get_wcaps_type(wcaps);
+               if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) {
+                       dacs[nums++] = nid;
+                       if (nums >= MAX_AUTO_DACS)
+                               break;
+               }
+       }
+       return nums;
+}
+
+/* fill pin_dac_pair list from the pin and dac list */
+static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins,
+                             int num_pins, hda_nid_t *dacs, int *rest,
+                             struct pin_dac_pair *filled, int type)
+{
+       int i, nums;
+
+       nums = 0;
+       for (i = 0; i < num_pins; i++) {
+               filled[nums].pin = pins[i];
+               filled[nums].type = type;
+               filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest);
+               nums++;
+       }
+       return nums;
+}
+
+/* parse analog output paths */
+static void cx_auto_parse_output(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       hda_nid_t dacs[MAX_AUTO_DACS];
+       int i, j, nums, rest;
+
+       rest = fill_cx_auto_dacs(codec, dacs);
+       /* parse all analog output pins */
+       nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs,
+                                 dacs, &rest, spec->dac_info,
+                                 AUTO_PIN_LINE_OUT);
+       nums += fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs,
+                                 dacs, &rest, spec->dac_info + nums,
+                                 AUTO_PIN_HP_OUT);
+       nums += fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs,
+                                 dacs, &rest, spec->dac_info + nums,
+                                 AUTO_PIN_SPEAKER_OUT);
+       spec->dac_info_filled = nums;
+       /* fill multiout struct */
+       for (i = 0; i < nums; i++) {
+               hda_nid_t dac = spec->dac_info[i].dac;
+               if (!dac)
+                       continue;
+               switch (spec->dac_info[i].type) {
+               case AUTO_PIN_LINE_OUT:
+                       spec->private_dac_nids[spec->multiout.num_dacs] = dac;
+                       spec->multiout.num_dacs++;
+                       break;
+               case AUTO_PIN_HP_OUT:
+               case AUTO_PIN_SPEAKER_OUT:
+                       if (!spec->multiout.hp_nid) {
+                               spec->multiout.hp_nid = dac;
+                               break;
+                       }
+                       for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++)
+                               if (!spec->multiout.extra_out_nid[j]) {
+                                       spec->multiout.extra_out_nid[j] = dac;
+                                       break;
+                               }
+                       break;
+               }
+       }
+       spec->multiout.dac_nids = spec->private_dac_nids;
+       spec->multiout.max_channels = nums * 2;
+
+       if (cfg->hp_outs > 0)
+               spec->auto_mute = 1;
+       spec->vmaster_nid = spec->private_dac_nids[0];
+}
+
+/* auto-mute/unmute speaker and line outs according to headphone jack */
+static void cx_auto_hp_automute(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i, present;
+
+       if (!spec->auto_mute)
+               return;
+       present = 0;
+       for (i = 0; i < cfg->hp_outs; i++) {
+               if (snd_hda_jack_detect(codec, cfg->hp_pins[i])) {
+                       present = 1;
+                       break;
+               }
+       }
+       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,
+                                   present ? 0 : PIN_OUT);
+       }
+       for (i = 0; i < cfg->speaker_outs; i++) {
+               snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   present ? 0 : PIN_OUT);
+       }
+}
+
+/* automatic switch internal and external mic */
+static void cx_auto_automic(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       struct hda_input_mux *imux = &spec->private_imux;
+       int ext_idx = spec->auto_mic_ext;
+
+       if (!spec->auto_mic)
+               return;
+       if (snd_hda_jack_detect(codec, cfg->inputs[ext_idx].pin)) {
+               snd_hda_codec_write(codec, spec->adc_nids[0], 0,
+                                   AC_VERB_SET_CONNECT_SEL,
+                                   imux->items[ext_idx].index);
+       } else {
+               snd_hda_codec_write(codec, spec->adc_nids[0], 0,
+                                   AC_VERB_SET_CONNECT_SEL,
+                                   imux->items[!ext_idx].index);
+       }
+}
+
+static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20;
+       switch (res >> 26) {
+       case CONEXANT_HP_EVENT:
+               cx_auto_hp_automute(codec);
+               conexant_report_jack(codec, nid);
+               break;
+       case CONEXANT_MIC_EVENT:
+               cx_auto_automic(codec);
+               conexant_report_jack(codec, nid);
+               break;
+       }
+}
+
+/* return true if it's an internal-mic pin */
+static int is_int_mic(struct hda_codec *codec, hda_nid_t pin)
+{
+       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
+       return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
+               snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT;
+}
+
+/* return true if it's an external-mic pin */
+static int is_ext_mic(struct hda_codec *codec, hda_nid_t pin)
+{
+       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
+       return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
+               snd_hda_get_input_pin_attr(def_conf) >= INPUT_PIN_ATTR_NORMAL &&
+               (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_PRES_DETECT);
+}
+
+/* check whether the pin config is suitable for auto-mic switching;
+ * auto-mic is enabled only when one int-mic and one-ext mic exist
+ */
+static void cx_auto_check_auto_mic(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+
+       if (is_ext_mic(codec, cfg->inputs[0].pin) &&
+           is_int_mic(codec, cfg->inputs[1].pin)) {
+               spec->auto_mic = 1;
+               spec->auto_mic_ext = 1;
+               return;
+       }
+       if (is_int_mic(codec, cfg->inputs[1].pin) &&
+           is_ext_mic(codec, cfg->inputs[0].pin)) {
+               spec->auto_mic = 1;
+               spec->auto_mic_ext = 0;
+               return;
+       }
+}
+
+static void cx_auto_parse_input(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       struct hda_input_mux *imux;
+       int i;
+
+       imux = &spec->private_imux;
+       for (i = 0; i < cfg->num_inputs; i++) {
+               int idx = get_connection_index(codec, spec->adc_nids[0],
+                                              cfg->inputs[i].pin);
+               if (idx >= 0) {
+                       const char *label;
+                       label = hda_get_autocfg_input_label(codec, cfg, i);
+                       snd_hda_add_imux_item(imux, label, idx, NULL);
+               }
+       }
+       if (imux->num_items == 2 && cfg->num_inputs == 2)
+               cx_auto_check_auto_mic(codec);
+       if (imux->num_items > 1 && !spec->auto_mic)
+               spec->input_mux = imux;
+}
+
+/* get digital-input audio widget corresponding to the given pin */
+static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin)
+{
+       hda_nid_t nid, end_nid;
+
+       end_nid = codec->start_nid + codec->num_nodes;
+       for (nid = codec->start_nid; nid < end_nid; nid++) {
+               unsigned int wcaps = get_wcaps(codec, nid);
+               unsigned int type = get_wcaps_type(wcaps);
+               if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) {
+                       if (get_connection_index(codec, nid, pin) >= 0)
+                               return nid;
+               }
+       }
+       return 0;
+}
+
+static void cx_auto_parse_digital(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       hda_nid_t nid;
+
+       if (cfg->dig_outs &&
+           snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1)
+               spec->multiout.dig_out_nid = nid;
+       if (cfg->dig_in_pin)
+               spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin);
+}
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static void cx_auto_parse_beep(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       hda_nid_t nid, end_nid;
+
+       end_nid = codec->start_nid + codec->num_nodes;
+       for (nid = codec->start_nid; nid < end_nid; nid++)
+               if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) {
+                       set_beep_amp(spec, nid, 0, HDA_OUTPUT);
+                       break;
+               }
+}
+#else
+#define cx_auto_parse_beep(codec)
+#endif
+
+static int cx_auto_parse_auto_config(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       int err;
+
+       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+       if (err < 0)
+               return err;
+
+       cx_auto_parse_output(codec);
+       cx_auto_parse_input(codec);
+       cx_auto_parse_digital(codec);
+       cx_auto_parse_beep(codec);
+       return 0;
+}
+
+static void cx_auto_turn_on_eapd(struct hda_codec *codec, int num_pins,
+                                hda_nid_t *pins)
+{
+       int i;
+       for (i = 0; i < num_pins; i++) {
+               if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
+                       snd_hda_codec_write(codec, pins[i], 0,
+                                           AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+       }
+}
+
+static void select_connection(struct hda_codec *codec, hda_nid_t pin,
+                             hda_nid_t src)
+{
+       int idx = get_connection_index(codec, pin, src);
+       if (idx >= 0)
+               snd_hda_codec_write(codec, pin, 0,
+                                   AC_VERB_SET_CONNECT_SEL, idx);
+}
+
+static void cx_auto_init_output(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       hda_nid_t nid;
+       int i;
+
+       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);
+
+       for (i = 0; i < cfg->hp_outs; i++)
+               snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+       if (spec->auto_mute) {
+               for (i = 0; i < cfg->hp_outs; i++) {
+                       snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
+                                   AC_VERB_SET_UNSOLICITED_ENABLE,
+                                   AC_USRSP_EN | CONEXANT_HP_EVENT);
+               }
+               cx_auto_hp_automute(codec);
+       } else {
+               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->speaker_outs; i++)
+                       snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+       }
+
+       for (i = 0; i < spec->dac_info_filled; i++) {
+               nid = spec->dac_info[i].dac;
+               if (!nid)
+                       nid = spec->multiout.dac_nids[0];
+               select_connection(codec, spec->dac_info[i].pin, nid);
+       }
+
+       /* turn on EAPD */
+       cx_auto_turn_on_eapd(codec, cfg->line_outs, cfg->line_out_pins);
+       cx_auto_turn_on_eapd(codec, cfg->hp_outs, cfg->hp_pins);
+       cx_auto_turn_on_eapd(codec, cfg->speaker_outs, cfg->speaker_pins);
+}
+
+static void cx_auto_init_input(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
+
+       for (i = 0; i < spec->num_adc_nids; i++)
+               snd_hda_codec_write(codec, spec->adc_nids[i], 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0));
+
+       for (i = 0; i < cfg->num_inputs; i++) {
+               unsigned int type;
+               if (cfg->inputs[i].type == AUTO_PIN_MIC)
+                       type = PIN_VREF80;
+               else
+                       type = PIN_IN;
+               snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, type);
+       }
+
+       if (spec->auto_mic) {
+               int ext_idx = spec->auto_mic_ext;
+               snd_hda_codec_write(codec, cfg->inputs[ext_idx].pin, 0,
+                                   AC_VERB_SET_UNSOLICITED_ENABLE,
+                                   AC_USRSP_EN | CONEXANT_MIC_EVENT);
+               cx_auto_automic(codec);
+       } else {
+               for (i = 0; i < spec->num_adc_nids; i++) {
+                       snd_hda_codec_write(codec, spec->adc_nids[i], 0,
+                                           AC_VERB_SET_CONNECT_SEL,
+                                           spec->private_imux.items[0].index);
+               }
+       }
+}
+
+static void cx_auto_init_digital(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+
+       if (spec->multiout.dig_out_nid)
+               snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+       if (spec->dig_in_nid)
+               snd_hda_codec_write(codec, cfg->dig_in_pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+}
+
+static int cx_auto_init(struct hda_codec *codec)
+{
+       /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/
+       cx_auto_init_output(codec);
+       cx_auto_init_input(codec);
+       cx_auto_init_digital(codec);
+       return 0;
+}
+
+static int cx_auto_add_volume(struct hda_codec *codec, const char *basename,
+                             const char *dir, int cidx,
+                             hda_nid_t nid, int hda_dir)
+{
+       static char name[32];
+       static struct snd_kcontrol_new knew[] = {
+               HDA_CODEC_VOLUME(name, 0, 0, 0),
+               HDA_CODEC_MUTE(name, 0, 0, 0),
+       };
+       static char *sfx[2] = { "Volume", "Switch" };
+       int i, err;
+
+       for (i = 0; i < 2; i++) {
+               struct snd_kcontrol *kctl;
+               knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, hda_dir);
+               knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
+               knew[i].index = cidx;
+               snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]);
+               kctl = snd_ctl_new1(&knew[i], codec);
+               if (!kctl)
+                       return -ENOMEM;
+               err = snd_hda_ctl_add(codec, nid, kctl);
+               if (err < 0)
+                       return err;
+               if (!(query_amp_caps(codec, nid, hda_dir) & AC_AMPCAP_MUTE))
+                       break;
+       }
+       return 0;
+}
+
+#define cx_auto_add_pb_volume(codec, nid, str, idx)                    \
+       cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
+
+static int cx_auto_build_output_controls(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       int i, err;
+       int num_line = 0, num_hp = 0, num_spk = 0;
+       static const char *texts[3] = { "Front", "Surround", "CLFE" };
+
+       if (spec->dac_info_filled == 1)
+               return cx_auto_add_pb_volume(codec, spec->dac_info[0].dac,
+                                            "Master", 0);
+       for (i = 0; i < spec->dac_info_filled; i++) {
+               const char *label;
+               int idx, type;
+               if (!spec->dac_info[i].dac)
+                       continue;
+               type = spec->dac_info[i].type;
+               if (type == AUTO_PIN_LINE_OUT)
+                       type = spec->autocfg.line_out_type;
+               switch (type) {
+               case AUTO_PIN_LINE_OUT:
+               default:
+                       label = texts[num_line++];
+                       idx = 0;
+                       break;
+               case AUTO_PIN_HP_OUT:
+                       label = "Headphone";
+                       idx = num_hp++;
+                       break;
+               case AUTO_PIN_SPEAKER_OUT:
+                       label = "Speaker";
+                       idx = num_spk++;
+                       break;
+               }
+               err = cx_auto_add_pb_volume(codec, spec->dac_info[i].dac,
+                                           label, idx);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+static int cx_auto_build_input_controls(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       static const char *prev_label;
+       int i, err, cidx;
+
+       err = cx_auto_add_volume(codec, "Capture", "", 0, spec->adc_nids[0],
+                                HDA_INPUT);
+       if (err < 0)
+               return err;
+       prev_label = NULL;
+       cidx = 0;
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
+               const char *label;
+               if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
+                       continue;
+               label = hda_get_autocfg_input_label(codec, cfg, i);
+               if (label == prev_label)
+                       cidx++;
+               else
+                       cidx = 0;
+               prev_label = label;
+               err = cx_auto_add_volume(codec, label, " Capture", cidx,
+                                        nid, HDA_INPUT);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+static int cx_auto_build_controls(struct hda_codec *codec)
+{
+       int err;
+
+       err = cx_auto_build_output_controls(codec);
+       if (err < 0)
+               return err;
+       err = cx_auto_build_input_controls(codec);
+       if (err < 0)
+               return err;
+       return conexant_build_controls(codec);
+}
+
+static struct hda_codec_ops cx_auto_patch_ops = {
+       .build_controls = cx_auto_build_controls,
+       .build_pcms = conexant_build_pcms,
+       .init = cx_auto_init,
+       .free = conexant_free,
+       .unsol_event = cx_auto_unsol_event,
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       .suspend = conexant_suspend,
+#endif
+       .reboot_notify = snd_hda_shutup_pins,
+};
+
+static int patch_conexant_auto(struct hda_codec *codec)
+{
+       struct conexant_spec *spec;
+       int err;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       codec->spec = spec;
+       spec->adc_nids = cx_auto_adc_nids;
+       spec->num_adc_nids = ARRAY_SIZE(cx_auto_adc_nids);
+       spec->capsrc_nids = spec->adc_nids;
+       err = cx_auto_parse_auto_config(codec);
+       if (err < 0) {
+               kfree(codec->spec);
+               codec->spec = NULL;
+               return err;
+       }
+       codec->patch_ops = cx_auto_patch_ops;
+       if (spec->beep_amp)
+               snd_hda_attach_beep_device(codec, spec->beep_amp);
+       return 0;
+}
+
 /*
  */
 
@@ -3271,6 +3880,22 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
          .patch = patch_cxt5066 },
        { .id = 0x14f15069, .name = "CX20585",
          .patch = patch_cxt5066 },
+       { .id = 0x14f15097, .name = "CX20631",
+         .patch = patch_conexant_auto },
+       { .id = 0x14f15098, .name = "CX20632",
+         .patch = patch_conexant_auto },
+       { .id = 0x14f150a1, .name = "CX20641",
+         .patch = patch_conexant_auto },
+       { .id = 0x14f150a2, .name = "CX20642",
+         .patch = patch_conexant_auto },
+       { .id = 0x14f150ab, .name = "CX20651",
+         .patch = patch_conexant_auto },
+       { .id = 0x14f150ac, .name = "CX20652",
+         .patch = patch_conexant_auto },
+       { .id = 0x14f150b8, .name = "CX20664",
+         .patch = patch_conexant_auto },
+       { .id = 0x14f150b9, .name = "CX20665",
+         .patch = patch_conexant_auto },
        {} /* terminator */
 };
 
@@ -3281,6 +3906,14 @@ MODULE_ALIAS("snd-hda-codec-id:14f15066");
 MODULE_ALIAS("snd-hda-codec-id:14f15067");
 MODULE_ALIAS("snd-hda-codec-id:14f15068");
 MODULE_ALIAS("snd-hda-codec-id:14f15069");
+MODULE_ALIAS("snd-hda-codec-id:14f15097");
+MODULE_ALIAS("snd-hda-codec-id:14f15098");
+MODULE_ALIAS("snd-hda-codec-id:14f150a1");
+MODULE_ALIAS("snd-hda-codec-id:14f150a2");
+MODULE_ALIAS("snd-hda-codec-id:14f150ab");
+MODULE_ALIAS("snd-hda-codec-id:14f150ac");
+MODULE_ALIAS("snd-hda-codec-id:14f150b8");
+MODULE_ALIAS("snd-hda-codec-id:14f150b9");
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Conexant HD-audio codec");
index afd6022a96a75bda3b24b652973dccbe591c625f..d3e49aa5b9ecd42f6db6f5892319e6602ad9b7ab 100644 (file)
@@ -3,6 +3,9 @@
  *  patch_hdmi.c - routines for HDMI/DisplayPort codecs
  *
  *  Copyright(c) 2008-2010 Intel Corporation. All rights reserved.
+ *  Copyright (c) 2006 ATI Technologies Inc.
+ *  Copyright (c) 2008 NVIDIA Corp.  All rights reserved.
+ *  Copyright (c) 2008 Wei Ni <wni@nvidia.com>
  *
  *  Authors:
  *                     Wu Fengguang <wfg@linux.intel.com>
  *  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+/*
+ * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
+ * could support two independent pipes, each of them can be connected to one or
+ * more ports (DVI, HDMI or DisplayPort).
+ *
+ * The HDA correspondence of pipes/ports are converter/pin nodes.
+ */
+#define MAX_HDMI_CVTS  3
+#define MAX_HDMI_PINS  3
 
 struct hdmi_spec {
        int num_cvts;
@@ -49,10 +68,10 @@ struct hdmi_spec {
        struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS];
 
        /*
-        * nvhdmi specific
+        * ati/nvhdmi specific
         */
        struct hda_multi_out multiout;
-       unsigned int codec_type;
+       struct hda_pcm_stream *pcm_playback;
 
        /* misc flags */
        /* PD bit indicates only the update, not the current state */
@@ -65,13 +84,25 @@ struct hdmi_audio_infoframe {
        u8 ver;  /* 0x01 */
        u8 len;  /* 0x0a */
 
-       u8 checksum;    /* PB0 */
+       u8 checksum;
+
        u8 CC02_CT47;   /* CC in bits 0:2, CT in 4:7 */
        u8 SS01_SF24;
        u8 CXT04;
        u8 CA;
        u8 LFEPBL01_LSV36_DM_INH7;
-       u8 reserved[5]; /* PB6 - PB10 */
+};
+
+struct dp_audio_infoframe {
+       u8 type; /* 0x84 */
+       u8 len;  /* 0x1b */
+       u8 ver;  /* 0x11 << 2 */
+
+       u8 CC02_CT47;   /* match with HDMI infoframe from this on */
+       u8 SS01_SF24;
+       u8 CXT04;
+       u8 CA;
+       u8 LFEPBL01_LSV36_DM_INH7;
 };
 
 /*
@@ -162,7 +193,7 @@ static int hdmi_channel_mapping[0x32][8] = {
        /* 4ch */
        [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
        /* surround41 */
-       [0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 },
+       [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
        /* surround50 */
        [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
        /* surround51 */
@@ -175,7 +206,7 @@ static int hdmi_channel_mapping[0x32][8] = {
  * This is an ordered list!
  *
  * The preceding ones have better chances to be selected by
- * hdmi_setup_channel_allocation().
+ * hdmi_channel_allocation().
  */
 static struct cea_channel_speaker_allocation channel_allocations[] = {
 /*                       channel:   7     6    5    4    3     2    1    0  */
@@ -352,14 +383,14 @@ static void init_channel_allocations(void)
  *
  * TODO: it could select the wrong CA from multiple candidates.
 */
-static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
-                                        struct hdmi_audio_infoframe *ai)
+static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
+                                  int channels)
 {
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_eld *eld;
        int i;
+       int ca = 0;
        int spk_mask = 0;
-       int channels = 1 + (ai->CC02_CT47 & 0x7);
        char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
 
        /*
@@ -397,16 +428,16 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
                if (channels == channel_allocations[i].channels &&
                    (spk_mask & channel_allocations[i].spk_mask) ==
                                channel_allocations[i].spk_mask) {
-                       ai->CA = channel_allocations[i].ca_index;
+                       ca = channel_allocations[i].ca_index;
                        break;
                }
        }
 
        snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
        snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n",
-                   ai->CA, channels, buf);
+                   ca, channels, buf);
 
-       return ai->CA;
+       return ca;
 }
 
 static void hdmi_debug_channel_mapping(struct hda_codec *codec,
@@ -428,10 +459,9 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec,
 
 static void hdmi_setup_channel_mapping(struct hda_codec *codec,
                                       hda_nid_t pin_nid,
-                                      struct hdmi_audio_infoframe *ai)
+                                      int ca)
 {
        int i;
-       int ca = ai->CA;
        int err;
 
        if (hdmi_channel_mapping[ca][1] == 0) {
@@ -528,41 +558,37 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
 #endif
 }
 
-static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
+static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai)
 {
-       u8 *bytes = (u8 *)ai;
+       u8 *bytes = (u8 *)hdmi_ai;
        u8 sum = 0;
        int i;
 
-       ai->checksum = 0;
+       hdmi_ai->checksum = 0;
 
-       for (i = 0; i < sizeof(*ai); i++)
+       for (i = 0; i < sizeof(*hdmi_ai); i++)
                sum += bytes[i];
 
-       ai->checksum = -sum;
+       hdmi_ai->checksum = -sum;
 }
 
 static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
                                      hda_nid_t pin_nid,
-                                     struct hdmi_audio_infoframe *ai)
+                                     u8 *dip, int size)
 {
-       u8 *bytes = (u8 *)ai;
        int i;
 
        hdmi_debug_dip_size(codec, pin_nid);
        hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
 
-       hdmi_checksum_audio_infoframe(ai);
-
        hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
-       for (i = 0; i < sizeof(*ai); i++)
-               hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
+       for (i = 0; i < size; i++)
+               hdmi_write_dip_byte(codec, pin_nid, dip[i]);
 }
 
 static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
-                                   struct hdmi_audio_infoframe *ai)
+                                   u8 *dip, int size)
 {
-       u8 *bytes = (u8 *)ai;
        u8 val;
        int i;
 
@@ -571,10 +597,10 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
                return false;
 
        hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
-       for (i = 0; i < sizeof(*ai); i++) {
+       for (i = 0; i < size; i++) {
                val = snd_hda_codec_read(codec, pin_nid, 0,
                                         AC_VERB_GET_HDMI_DIP_DATA, 0);
-               if (val != bytes[i])
+               if (val != dip[i])
                        return false;
        }
 
@@ -586,15 +612,13 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
 {
        struct hdmi_spec *spec = codec->spec;
        hda_nid_t pin_nid;
+       int channels = substream->runtime->channels;
+       int ca;
        int i;
-       struct hdmi_audio_infoframe ai = {
-               .type           = 0x84,
-               .ver            = 0x01,
-               .len            = 0x0a,
-               .CC02_CT47      = substream->runtime->channels - 1,
-       };
+       u8 ai[max(sizeof(struct hdmi_audio_infoframe),
+                 sizeof(struct dp_audio_infoframe))];
 
-       hdmi_setup_channel_allocation(codec, nid, &ai);
+       ca = hdmi_channel_allocation(codec, nid, channels);
 
        for (i = 0; i < spec->num_pins; i++) {
                if (spec->pin_cvt[i] != nid)
@@ -603,14 +627,45 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
                        continue;
 
                pin_nid = spec->pin[i];
-               if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
+
+               memset(ai, 0, sizeof(ai));
+               if (spec->sink_eld[i].conn_type == 0) { /* HDMI */
+                       struct hdmi_audio_infoframe *hdmi_ai;
+
+                       hdmi_ai = (struct hdmi_audio_infoframe *)ai;
+                       hdmi_ai->type           = 0x84;
+                       hdmi_ai->ver            = 0x01;
+                       hdmi_ai->len            = 0x0a;
+                       hdmi_ai->CC02_CT47      = channels - 1;
+                       hdmi_checksum_audio_infoframe(hdmi_ai);
+               } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */
+                       struct dp_audio_infoframe *dp_ai;
+
+                       dp_ai = (struct dp_audio_infoframe *)ai;
+                       dp_ai->type             = 0x84;
+                       dp_ai->len              = 0x1b;
+                       dp_ai->ver              = 0x11 << 2;
+                       dp_ai->CC02_CT47        = channels - 1;
+               } else {
+                       snd_printd("HDMI: unknown connection type at pin %d\n",
+                                  pin_nid);
+                       continue;
+               }
+
+               /*
+                * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
+                * sizeof(*dp_ai) to avoid partial match/update problems when
+                * the user switches between HDMI/DP monitors.
+                */
+               if (!hdmi_infoframe_uptodate(codec, pin_nid, ai, sizeof(ai))) {
                        snd_printdd("hdmi_setup_audio_infoframe: "
                                    "cvt=%d pin=%d channels=%d\n",
                                    nid, pin_nid,
-                                   substream->runtime->channels);
-                       hdmi_setup_channel_mapping(codec, pin_nid, &ai);
+                                   channels);
+                       hdmi_setup_channel_mapping(codec, pin_nid, ca);
                        hdmi_stop_infoframe_trans(codec, pin_nid);
-                       hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
+                       hdmi_fill_audio_infoframe(codec, pin_nid,
+                                                 ai, sizeof(ai));
                        hdmi_start_infoframe_trans(codec, pin_nid);
                }
        }
@@ -791,7 +846,6 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 /*
  * HDA/HDMI auto parsing
  */
-
 static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
 {
        struct hdmi_spec *spec = codec->spec;
@@ -922,3 +976,664 @@ static int hdmi_parse_codec(struct hda_codec *codec)
        return 0;
 }
 
+/*
+ */
+static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = {
+       "HDMI 0",
+       "HDMI 1",
+       "HDMI 2",
+};
+
+/*
+ * HDMI callbacks
+ */
+
+static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                          struct hda_codec *codec,
+                                          unsigned int stream_tag,
+                                          unsigned int format,
+                                          struct snd_pcm_substream *substream)
+{
+       hdmi_set_channel_count(codec, hinfo->nid,
+                              substream->runtime->channels);
+
+       hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
+
+       return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
+}
+
+static struct hda_pcm_stream generic_hdmi_pcm_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .ops = {
+               .open = hdmi_pcm_open,
+               .prepare = generic_hdmi_playback_pcm_prepare,
+       },
+};
+
+static int generic_hdmi_build_pcms(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+       struct hda_pcm *info = spec->pcm_rec;
+       int i;
+
+       codec->num_pcms = spec->num_cvts;
+       codec->pcm_info = info;
+
+       for (i = 0; i < codec->num_pcms; i++, info++) {
+               unsigned int chans;
+               struct hda_pcm_stream *pstr;
+
+               chans = get_wcaps(codec, spec->cvt[i]);
+               chans = get_wcaps_channels(chans);
+
+               info->name = generic_hdmi_pcm_names[i];
+               info->pcm_type = HDA_PCM_TYPE_HDMI;
+               pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
+               if (spec->pcm_playback)
+                       *pstr = *spec->pcm_playback;
+               else
+                       *pstr = generic_hdmi_pcm_playback;
+               pstr->nid = spec->cvt[i];
+               if (pstr->channels_max <= 2 && chans && chans <= 16)
+                       pstr->channels_max = chans;
+       }
+
+       return 0;
+}
+
+static int generic_hdmi_build_controls(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+       int err;
+       int i;
+
+       for (i = 0; i < codec->num_pcms; i++) {
+               err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int generic_hdmi_init(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; spec->pin[i]; i++) {
+               hdmi_enable_output(codec, spec->pin[i]);
+               snd_hda_codec_write(codec, spec->pin[i], 0,
+                                   AC_VERB_SET_UNSOLICITED_ENABLE,
+                                   AC_USRSP_EN | spec->pin[i]);
+       }
+       return 0;
+}
+
+static void generic_hdmi_free(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < spec->num_pins; i++)
+               snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
+
+       kfree(spec);
+}
+
+static struct hda_codec_ops generic_hdmi_patch_ops = {
+       .init                   = generic_hdmi_init,
+       .free                   = generic_hdmi_free,
+       .build_pcms             = generic_hdmi_build_pcms,
+       .build_controls         = generic_hdmi_build_controls,
+       .unsol_event            = hdmi_unsol_event,
+};
+
+static int patch_generic_hdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+       int i;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+       if (hdmi_parse_codec(codec) < 0) {
+               codec->spec = NULL;
+               kfree(spec);
+               return -EINVAL;
+       }
+       codec->patch_ops = generic_hdmi_patch_ops;
+
+       for (i = 0; i < spec->num_pins; i++)
+               snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
+
+       init_channel_allocations();
+
+       return 0;
+}
+
+/*
+ * Nvidia specific implementations
+ */
+
+#define Nv_VERB_SET_Channel_Allocation          0xF79
+#define Nv_VERB_SET_Info_Frame_Checksum         0xF7A
+#define Nv_VERB_SET_Audio_Protection_On         0xF98
+#define Nv_VERB_SET_Audio_Protection_Off        0xF99
+
+#define nvhdmi_master_con_nid_7x       0x04
+#define nvhdmi_master_pin_nid_7x       0x05
+
+static hda_nid_t nvhdmi_con_nids_7x[4] = {
+       /*front, rear, clfe, rear_surr */
+       0x6, 0x8, 0xa, 0xc,
+};
+
+static struct hda_verb nvhdmi_basic_init_7x[] = {
+       /* set audio protect on */
+       { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
+       /* enable digital output on pin widget */
+       { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+       { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+       { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+       { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+       { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+       {} /* terminator */
+};
+
+#ifdef LIMITED_RATE_FMT_SUPPORT
+/* support only the safe format and rate */
+#define SUPPORTED_RATES                SNDRV_PCM_RATE_48000
+#define SUPPORTED_MAXBPS       16
+#define SUPPORTED_FORMATS      SNDRV_PCM_FMTBIT_S16_LE
+#else
+/* support all rates and formats */
+#define SUPPORTED_RATES \
+       (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+       SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
+        SNDRV_PCM_RATE_192000)
+#define SUPPORTED_MAXBPS       24
+#define SUPPORTED_FORMATS \
+       (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+#endif
+
+static int nvhdmi_7x_init(struct hda_codec *codec)
+{
+       snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
+       return 0;
+}
+
+static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                   struct hda_codec *codec,
+                                   struct snd_pcm_substream *substream)
+{
+       struct hdmi_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo,
+                                    struct hda_codec *codec,
+                                    struct snd_pcm_substream *substream)
+{
+       struct hdmi_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int simple_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 hdmi_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+                                            stream_tag, format, substream);
+}
+
+static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo,
+                                  struct hda_codec *codec,
+                                  struct snd_pcm_substream *substream)
+{
+       struct hdmi_spec *spec = codec->spec;
+       int i;
+
+       snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
+                       0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+       for (i = 0; i < 4; i++) {
+               /* set the stream id */
+               snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
+                               AC_VERB_SET_CHANNEL_STREAMID, 0);
+               /* set the stream format */
+               snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
+                               AC_VERB_SET_STREAM_FORMAT, 0);
+       }
+
+       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                    struct hda_codec *codec,
+                                    unsigned int stream_tag,
+                                    unsigned int format,
+                                    struct snd_pcm_substream *substream)
+{
+       int chs;
+       unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
+       int i;
+
+       mutex_lock(&codec->spdif_mutex);
+
+       chs = substream->runtime->channels;
+       chan = chs ? (chs - 1) : 1;
+
+       switch (chs) {
+       default:
+       case 0:
+       case 2:
+               chanmask = 0x00;
+               break;
+       case 4:
+               chanmask = 0x08;
+               break;
+       case 6:
+               chanmask = 0x0b;
+               break;
+       case 8:
+               chanmask = 0x13;
+               break;
+       }
+       dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
+       dataDCC2 = 0x2;
+
+       /* set the Audio InforFrame Channel Allocation */
+       snd_hda_codec_write(codec, 0x1, 0,
+                       Nv_VERB_SET_Channel_Allocation, chanmask);
+
+       /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
+       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+               snd_hda_codec_write(codec,
+                               nvhdmi_master_con_nid_7x,
+                               0,
+                               AC_VERB_SET_DIGI_CONVERT_1,
+                               codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+
+       /* set the stream id */
+       snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
+                       AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
+
+       /* set the stream format */
+       snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
+                       AC_VERB_SET_STREAM_FORMAT, format);
+
+       /* turn on again (if needed) */
+       /* enable and set the channel status audio/data flag */
+       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+               snd_hda_codec_write(codec,
+                               nvhdmi_master_con_nid_7x,
+                               0,
+                               AC_VERB_SET_DIGI_CONVERT_1,
+                               codec->spdif_ctls & 0xff);
+               snd_hda_codec_write(codec,
+                               nvhdmi_master_con_nid_7x,
+                               0,
+                               AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+       }
+
+       for (i = 0; i < 4; i++) {
+               if (chs == 2)
+                       channel_id = 0;
+               else
+                       channel_id = i * 2;
+
+               /* turn off SPDIF once;
+                *otherwise the IEC958 bits won't be updated
+                */
+               if (codec->spdif_status_reset &&
+               (codec->spdif_ctls & AC_DIG1_ENABLE))
+                       snd_hda_codec_write(codec,
+                               nvhdmi_con_nids_7x[i],
+                               0,
+                               AC_VERB_SET_DIGI_CONVERT_1,
+                               codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+               /* set the stream id */
+               snd_hda_codec_write(codec,
+                               nvhdmi_con_nids_7x[i],
+                               0,
+                               AC_VERB_SET_CHANNEL_STREAMID,
+                               (stream_tag << 4) | channel_id);
+               /* set the stream format */
+               snd_hda_codec_write(codec,
+                               nvhdmi_con_nids_7x[i],
+                               0,
+                               AC_VERB_SET_STREAM_FORMAT,
+                               format);
+               /* turn on again (if needed) */
+               /* enable and set the channel status audio/data flag */
+               if (codec->spdif_status_reset &&
+               (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+                       snd_hda_codec_write(codec,
+                                       nvhdmi_con_nids_7x[i],
+                                       0,
+                                       AC_VERB_SET_DIGI_CONVERT_1,
+                                       codec->spdif_ctls & 0xff);
+                       snd_hda_codec_write(codec,
+                                       nvhdmi_con_nids_7x[i],
+                                       0,
+                                       AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+               }
+       }
+
+       /* set the Audio Info Frame Checksum */
+       snd_hda_codec_write(codec, 0x1, 0,
+                       Nv_VERB_SET_Info_Frame_Checksum,
+                       (0x71 - chan - chanmask));
+
+       mutex_unlock(&codec->spdif_mutex);
+       return 0;
+}
+
+static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 8,
+       .nid = nvhdmi_master_con_nid_7x,
+       .rates = SUPPORTED_RATES,
+       .maxbps = SUPPORTED_MAXBPS,
+       .formats = SUPPORTED_FORMATS,
+       .ops = {
+               .open = simple_playback_pcm_open,
+               .close = nvhdmi_8ch_7x_pcm_close,
+               .prepare = nvhdmi_8ch_7x_pcm_prepare
+       },
+};
+
+static struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = nvhdmi_master_con_nid_7x,
+       .rates = SUPPORTED_RATES,
+       .maxbps = SUPPORTED_MAXBPS,
+       .formats = SUPPORTED_FORMATS,
+       .ops = {
+               .open = simple_playback_pcm_open,
+               .close = simple_playback_pcm_close,
+               .prepare = simple_playback_pcm_prepare
+       },
+};
+
+static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
+       .build_controls = generic_hdmi_build_controls,
+       .build_pcms = generic_hdmi_build_pcms,
+       .init = nvhdmi_7x_init,
+       .free = generic_hdmi_free,
+};
+
+static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
+       .build_controls = generic_hdmi_build_controls,
+       .build_pcms = generic_hdmi_build_pcms,
+       .init = nvhdmi_7x_init,
+       .free = generic_hdmi_free,
+};
+
+static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+       int err = patch_generic_hdmi(codec);
+
+       if (err < 0)
+               return err;
+       spec = codec->spec;
+       spec->old_pin_detect = 1;
+       return 0;
+}
+
+static int patch_nvhdmi_2ch(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       spec->multiout.num_dacs = 0;  /* no analog */
+       spec->multiout.max_channels = 2;
+       spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
+       spec->old_pin_detect = 1;
+       spec->num_cvts = 1;
+       spec->cvt[0] = nvhdmi_master_con_nid_7x;
+       spec->pcm_playback = &nvhdmi_pcm_playback_2ch;
+
+       codec->patch_ops = nvhdmi_patch_ops_2ch;
+
+       return 0;
+}
+
+static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+       int err = patch_nvhdmi_2ch(codec);
+
+       if (err < 0)
+               return err;
+       spec = codec->spec;
+       spec->multiout.max_channels = 8;
+       spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x;
+       codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
+       return 0;
+}
+
+/*
+ * ATI-specific implementations
+ *
+ * FIXME: we may omit the whole this and use the generic code once after
+ * it's confirmed to work.
+ */
+
+#define ATIHDMI_CVT_NID                0x02    /* audio converter */
+#define ATIHDMI_PIN_NID                0x03    /* HDMI output pin */
+
+static int atihdmi_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 hdmi_spec *spec = codec->spec;
+       int chans = substream->runtime->channels;
+       int i, err;
+
+       err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format,
+                                         substream);
+       if (err < 0)
+               return err;
+       snd_hda_codec_write(codec, spec->cvt[0], 0, AC_VERB_SET_CVT_CHAN_COUNT,
+                           chans - 1);
+       /* FIXME: XXX */
+       for (i = 0; i < chans; i++) {
+               snd_hda_codec_write(codec, spec->cvt[0], 0,
+                                   AC_VERB_SET_HDMI_CHAN_SLOT,
+                                   (i << 4) | i);
+       }
+       return 0;
+}
+
+static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = ATIHDMI_CVT_NID,
+       .ops = {
+               .open = simple_playback_pcm_open,
+               .close = simple_playback_pcm_close,
+               .prepare = atihdmi_playback_pcm_prepare
+       },
+};
+
+static struct hda_verb atihdmi_basic_init[] = {
+       /* enable digital output on pin widget */
+       { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       {} /* terminator */
+};
+
+static int atihdmi_init(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+
+       snd_hda_sequence_write(codec, atihdmi_basic_init);
+       /* SI codec requires to unmute the pin */
+       if (get_wcaps(codec, spec->pin[0]) & AC_WCAP_OUT_AMP)
+               snd_hda_codec_write(codec, spec->pin[0], 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_OUT_UNMUTE);
+       return 0;
+}
+
+static struct hda_codec_ops atihdmi_patch_ops = {
+       .build_controls = generic_hdmi_build_controls,
+       .build_pcms = generic_hdmi_build_pcms,
+       .init = atihdmi_init,
+       .free = generic_hdmi_free,
+};
+
+
+static int patch_atihdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       spec->multiout.num_dacs = 0;      /* no analog */
+       spec->multiout.max_channels = 2;
+       spec->multiout.dig_out_nid = ATIHDMI_CVT_NID;
+       spec->num_cvts = 1;
+       spec->cvt[0] = ATIHDMI_CVT_NID;
+       spec->pin[0] = ATIHDMI_PIN_NID;
+       spec->pcm_playback = &atihdmi_pcm_digital_playback;
+
+       codec->patch_ops = atihdmi_patch_ops;
+
+       return 0;
+}
+
+
+/*
+ * patch entries
+ */
+static struct hda_codec_preset snd_hda_preset_hdmi[] = {
+{ .id = 0x1002793c, .name = "RS600 HDMI",      .patch = patch_atihdmi },
+{ .id = 0x10027919, .name = "RS600 HDMI",      .patch = patch_atihdmi },
+{ .id = 0x1002791a, .name = "RS690/780 HDMI",  .patch = patch_atihdmi },
+{ .id = 0x1002aa01, .name = "R6xx HDMI",       .patch = patch_atihdmi },
+{ .id = 0x10951390, .name = "SiI1390 HDMI",    .patch = patch_generic_hdmi },
+{ .id = 0x10951392, .name = "SiI1392 HDMI",    .patch = patch_generic_hdmi },
+{ .id = 0x17e80047, .name = "Chrontel HDMI",   .patch = patch_generic_hdmi },
+{ .id = 0x10de0002, .name = "MCP77/78 HDMI",   .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0003, .name = "MCP77/78 HDMI",   .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0005, .name = "MCP77/78 HDMI",   .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0006, .name = "MCP77/78 HDMI",   .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0007, .name = "MCP79/7A HDMI",   .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000c, .name = "MCP89 HDMI",      .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP",  .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0067, .name = "MCP67 HDMI",      .patch = patch_nvhdmi_2ch },
+{ .id = 0x10de8001, .name = "MCP73 HDMI",      .patch = patch_nvhdmi_2ch },
+{ .id = 0x80860054, .name = "IbexPeak HDMI",   .patch = patch_generic_hdmi },
+{ .id = 0x80862801, .name = "Bearlake HDMI",   .patch = patch_generic_hdmi },
+{ .id = 0x80862802, .name = "Cantiga HDMI",    .patch = patch_generic_hdmi },
+{ .id = 0x80862803, .name = "Eaglelake HDMI",  .patch = patch_generic_hdmi },
+{ .id = 0x80862804, .name = "IbexPeak HDMI",   .patch = patch_generic_hdmi },
+{ .id = 0x80862805, .name = "CougarPoint HDMI",        .patch = patch_generic_hdmi },
+{ .id = 0x808629fb, .name = "Crestline HDMI",  .patch = patch_generic_hdmi },
+{} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:1002793c");
+MODULE_ALIAS("snd-hda-codec-id:10027919");
+MODULE_ALIAS("snd-hda-codec-id:1002791a");
+MODULE_ALIAS("snd-hda-codec-id:1002aa01");
+MODULE_ALIAS("snd-hda-codec-id:10951390");
+MODULE_ALIAS("snd-hda-codec-id:10951392");
+MODULE_ALIAS("snd-hda-codec-id:10de0002");
+MODULE_ALIAS("snd-hda-codec-id:10de0003");
+MODULE_ALIAS("snd-hda-codec-id:10de0005");
+MODULE_ALIAS("snd-hda-codec-id:10de0006");
+MODULE_ALIAS("snd-hda-codec-id:10de0007");
+MODULE_ALIAS("snd-hda-codec-id:10de000a");
+MODULE_ALIAS("snd-hda-codec-id:10de000b");
+MODULE_ALIAS("snd-hda-codec-id:10de000c");
+MODULE_ALIAS("snd-hda-codec-id:10de000d");
+MODULE_ALIAS("snd-hda-codec-id:10de0010");
+MODULE_ALIAS("snd-hda-codec-id:10de0011");
+MODULE_ALIAS("snd-hda-codec-id:10de0012");
+MODULE_ALIAS("snd-hda-codec-id:10de0013");
+MODULE_ALIAS("snd-hda-codec-id:10de0014");
+MODULE_ALIAS("snd-hda-codec-id:10de0018");
+MODULE_ALIAS("snd-hda-codec-id:10de0019");
+MODULE_ALIAS("snd-hda-codec-id:10de001a");
+MODULE_ALIAS("snd-hda-codec-id:10de001b");
+MODULE_ALIAS("snd-hda-codec-id:10de001c");
+MODULE_ALIAS("snd-hda-codec-id:10de0040");
+MODULE_ALIAS("snd-hda-codec-id:10de0041");
+MODULE_ALIAS("snd-hda-codec-id:10de0042");
+MODULE_ALIAS("snd-hda-codec-id:10de0043");
+MODULE_ALIAS("snd-hda-codec-id:10de0044");
+MODULE_ALIAS("snd-hda-codec-id:10de0067");
+MODULE_ALIAS("snd-hda-codec-id:10de8001");
+MODULE_ALIAS("snd-hda-codec-id:17e80047");
+MODULE_ALIAS("snd-hda-codec-id:80860054");
+MODULE_ALIAS("snd-hda-codec-id:80862801");
+MODULE_ALIAS("snd-hda-codec-id:80862802");
+MODULE_ALIAS("snd-hda-codec-id:80862803");
+MODULE_ALIAS("snd-hda-codec-id:80862804");
+MODULE_ALIAS("snd-hda-codec-id:80862805");
+MODULE_ALIAS("snd-hda-codec-id:808629fb");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("HDMI HD-audio codec");
+MODULE_ALIAS("snd-hda-codec-intelhdmi");
+MODULE_ALIAS("snd-hda-codec-nvhdmi");
+MODULE_ALIAS("snd-hda-codec-atihdmi");
+
+static struct hda_codec_preset_list intel_list = {
+       .preset = snd_hda_preset_hdmi,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_hdmi_init(void)
+{
+       return snd_hda_add_codec_preset(&intel_list);
+}
+
+static void __exit patch_hdmi_exit(void)
+{
+       snd_hda_delete_codec_preset(&intel_list);
+}
+
+module_init(patch_hdmi_init)
+module_exit(patch_hdmi_exit)
diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c
deleted file mode 100644 (file)
index 36a9b83..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- *
- *  patch_intelhdmi.c - Patch for Intel HDMI codecs
- *
- *  Copyright(c) 2008 Intel Corporation. All rights reserved.
- *
- *  Authors:
- *                     Jiang Zhe <zhe.jiang@intel.com>
- *                     Wu Fengguang <wfg@linux.intel.com>
- *
- *  Maintained by:
- *                     Wu Fengguang <wfg@linux.intel.com>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License as published by the Free
- *  Software Foundation; either version 2 of the License, or (at your option)
- *  any later version.
- *
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- *  for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software Foundation,
- *  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-/*
- * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
- * could support two independent pipes, each of them can be connected to one or
- * more ports (DVI, HDMI or DisplayPort).
- *
- * The HDA correspondence of pipes/ports are converter/pin nodes.
- */
-#define MAX_HDMI_CVTS  3
-#define MAX_HDMI_PINS  3
-
-#include "patch_hdmi.c"
-
-static char *intel_hdmi_pcm_names[MAX_HDMI_CVTS] = {
-       "INTEL HDMI 0",
-       "INTEL HDMI 1",
-       "INTEL HDMI 2",
-};
-
-/*
- * HDMI callbacks
- */
-
-static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
-                                          struct hda_codec *codec,
-                                          unsigned int stream_tag,
-                                          unsigned int format,
-                                          struct snd_pcm_substream *substream)
-{
-       hdmi_set_channel_count(codec, hinfo->nid,
-                              substream->runtime->channels);
-
-       hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
-
-       return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
-}
-
-static struct hda_pcm_stream intel_hdmi_pcm_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .ops = {
-               .open = hdmi_pcm_open,
-               .prepare = intel_hdmi_playback_pcm_prepare,
-       },
-};
-
-static int intel_hdmi_build_pcms(struct hda_codec *codec)
-{
-       struct hdmi_spec *spec = codec->spec;
-       struct hda_pcm *info = spec->pcm_rec;
-       int i;
-
-       codec->num_pcms = spec->num_cvts;
-       codec->pcm_info = info;
-
-       for (i = 0; i < codec->num_pcms; i++, info++) {
-               unsigned int chans;
-
-               chans = get_wcaps(codec, spec->cvt[i]);
-               chans = get_wcaps_channels(chans);
-
-               info->name = intel_hdmi_pcm_names[i];
-               info->pcm_type = HDA_PCM_TYPE_HDMI;
-               info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
-                                                       intel_hdmi_pcm_playback;
-               info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
-               info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
-       }
-
-       return 0;
-}
-
-static int intel_hdmi_build_controls(struct hda_codec *codec)
-{
-       struct hdmi_spec *spec = codec->spec;
-       int err;
-       int i;
-
-       for (i = 0; i < codec->num_pcms; i++) {
-               err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
-static int intel_hdmi_init(struct hda_codec *codec)
-{
-       struct hdmi_spec *spec = codec->spec;
-       int i;
-
-       for (i = 0; spec->pin[i]; i++) {
-               hdmi_enable_output(codec, spec->pin[i]);
-               snd_hda_codec_write(codec, spec->pin[i], 0,
-                                   AC_VERB_SET_UNSOLICITED_ENABLE,
-                                   AC_USRSP_EN | spec->pin[i]);
-       }
-       return 0;
-}
-
-static void intel_hdmi_free(struct hda_codec *codec)
-{
-       struct hdmi_spec *spec = codec->spec;
-       int i;
-
-       for (i = 0; i < spec->num_pins; i++)
-               snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
-
-       kfree(spec);
-}
-
-static struct hda_codec_ops intel_hdmi_patch_ops = {
-       .init                   = intel_hdmi_init,
-       .free                   = intel_hdmi_free,
-       .build_pcms             = intel_hdmi_build_pcms,
-       .build_controls         = intel_hdmi_build_controls,
-       .unsol_event            = hdmi_unsol_event,
-};
-
-static int patch_intel_hdmi(struct hda_codec *codec)
-{
-       struct hdmi_spec *spec;
-       int i;
-
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
-       if (hdmi_parse_codec(codec) < 0) {
-               codec->spec = NULL;
-               kfree(spec);
-               return -EINVAL;
-       }
-       codec->patch_ops = intel_hdmi_patch_ops;
-
-       for (i = 0; i < spec->num_pins; i++)
-               snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
-
-       init_channel_allocations();
-
-       return 0;
-}
-
-static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
-{ .id = 0x808629fb, .name = "Crestline HDMI",  .patch = patch_intel_hdmi },
-{ .id = 0x80862801, .name = "Bearlake HDMI",   .patch = patch_intel_hdmi },
-{ .id = 0x80862802, .name = "Cantiga HDMI",    .patch = patch_intel_hdmi },
-{ .id = 0x80862803, .name = "Eaglelake HDMI",  .patch = patch_intel_hdmi },
-{ .id = 0x80862804, .name = "IbexPeak HDMI",   .patch = patch_intel_hdmi },
-{ .id = 0x80860054, .name = "IbexPeak HDMI",   .patch = patch_intel_hdmi },
-{ .id = 0x80862805, .name = "CougarPoint HDMI",        .patch = patch_intel_hdmi },
-{ .id = 0x10951392, .name = "SiI1392 HDMI",    .patch = patch_intel_hdmi },
-{} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:808629fb");
-MODULE_ALIAS("snd-hda-codec-id:80862801");
-MODULE_ALIAS("snd-hda-codec-id:80862802");
-MODULE_ALIAS("snd-hda-codec-id:80862803");
-MODULE_ALIAS("snd-hda-codec-id:80862804");
-MODULE_ALIAS("snd-hda-codec-id:80862805");
-MODULE_ALIAS("snd-hda-codec-id:80860054");
-MODULE_ALIAS("snd-hda-codec-id:10951392");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Intel HDMI HD-audio codec");
-
-static struct hda_codec_preset_list intel_list = {
-       .preset = snd_hda_preset_intelhdmi,
-       .owner = THIS_MODULE,
-};
-
-static int __init patch_intelhdmi_init(void)
-{
-       return snd_hda_add_codec_preset(&intel_list);
-}
-
-static void __exit patch_intelhdmi_exit(void)
-{
-       snd_hda_delete_codec_preset(&intel_list);
-}
-
-module_init(patch_intelhdmi_init)
-module_exit(patch_intelhdmi_exit)
diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c
deleted file mode 100644 (file)
index baa108b..0000000
+++ /dev/null
@@ -1,608 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * HD audio interface patch for NVIDIA HDMI codecs
- *
- * Copyright (c) 2008 NVIDIA Corp.  All rights reserved.
- * Copyright (c) 2008 Wei Ni <wni@nvidia.com>
- *
- *
- *  This driver is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This driver is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-#define MAX_HDMI_CVTS  1
-#define MAX_HDMI_PINS  1
-
-#include "patch_hdmi.c"
-
-static char *nvhdmi_pcm_names[MAX_HDMI_CVTS] = {
-       "NVIDIA HDMI",
-};
-
-/* define below to restrict the supported rates and formats */
-/* #define LIMITED_RATE_FMT_SUPPORT */
-
-enum HDACodec {
-       HDA_CODEC_NVIDIA_MCP7X,
-       HDA_CODEC_NVIDIA_MCP89,
-       HDA_CODEC_NVIDIA_GT21X,
-       HDA_CODEC_INVALID
-};
-
-#define Nv_VERB_SET_Channel_Allocation          0xF79
-#define Nv_VERB_SET_Info_Frame_Checksum         0xF7A
-#define Nv_VERB_SET_Audio_Protection_On         0xF98
-#define Nv_VERB_SET_Audio_Protection_Off        0xF99
-
-#define nvhdmi_master_con_nid_7x       0x04
-#define nvhdmi_master_pin_nid_7x       0x05
-
-#define nvhdmi_master_con_nid_89       0x04
-#define nvhdmi_master_pin_nid_89       0x05
-
-static hda_nid_t nvhdmi_con_nids_7x[4] = {
-       /*front, rear, clfe, rear_surr */
-       0x6, 0x8, 0xa, 0xc,
-};
-
-static struct hda_verb nvhdmi_basic_init_7x[] = {
-       /* set audio protect on */
-       { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
-       /* enable digital output on pin widget */
-       { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
-       { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
-       { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
-       { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
-       { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
-       {} /* terminator */
-};
-
-#ifdef LIMITED_RATE_FMT_SUPPORT
-/* support only the safe format and rate */
-#define SUPPORTED_RATES                SNDRV_PCM_RATE_48000
-#define SUPPORTED_MAXBPS       16
-#define SUPPORTED_FORMATS      SNDRV_PCM_FMTBIT_S16_LE
-#else
-/* support all rates and formats */
-#define SUPPORTED_RATES \
-       (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
-       SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
-        SNDRV_PCM_RATE_192000)
-#define SUPPORTED_MAXBPS       24
-#define SUPPORTED_FORMATS \
-       (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
-#endif
-
-/*
- * Controls
- */
-static int nvhdmi_build_controls(struct hda_codec *codec)
-{
-       struct hdmi_spec *spec = codec->spec;
-       int err;
-       int i;
-
-       if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
-       || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
-               for (i = 0; i < codec->num_pcms; i++) {
-                       err = snd_hda_create_spdif_out_ctls(codec,
-                                                           spec->cvt[i]);
-                       if (err < 0)
-                               return err;
-               }
-       } else {
-               err = snd_hda_create_spdif_out_ctls(codec,
-                                                   spec->multiout.dig_out_nid);
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
-static int nvhdmi_init(struct hda_codec *codec)
-{
-       struct hdmi_spec *spec = codec->spec;
-       int i;
-       if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
-       || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
-               for (i = 0; spec->pin[i]; i++) {
-                       hdmi_enable_output(codec, spec->pin[i]);
-                       snd_hda_codec_write(codec, spec->pin[i], 0,
-                                           AC_VERB_SET_UNSOLICITED_ENABLE,
-                                           AC_USRSP_EN | spec->pin[i]);
-               }
-       } else {
-               snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
-       }
-       return 0;
-}
-
-static void nvhdmi_free(struct hda_codec *codec)
-{
-       struct hdmi_spec *spec = codec->spec;
-       int i;
-
-       if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
-       || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
-               for (i = 0; i < spec->num_pins; i++)
-                       snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
-       }
-
-       kfree(spec);
-}
-
-/*
- * Digital out
- */
-static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
-                                       struct hda_codec *codec,
-                                       struct snd_pcm_substream *substream)
-{
-       struct hdmi_spec *spec = codec->spec;
-       return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int nvhdmi_dig_playback_pcm_close_8ch_7x(struct hda_pcm_stream *hinfo,
-                                       struct hda_codec *codec,
-                                       struct snd_pcm_substream *substream)
-{
-       struct hdmi_spec *spec = codec->spec;
-       int i;
-
-       snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
-                       0, AC_VERB_SET_CHANNEL_STREAMID, 0);
-       for (i = 0; i < 4; i++) {
-               /* set the stream id */
-               snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
-                               AC_VERB_SET_CHANNEL_STREAMID, 0);
-               /* set the stream format */
-               snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
-                               AC_VERB_SET_STREAM_FORMAT, 0);
-       }
-
-       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo,
-                                       struct hda_codec *codec,
-                                       struct snd_pcm_substream *substream)
-{
-       struct hdmi_spec *spec = codec->spec;
-       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int nvhdmi_dig_playback_pcm_prepare_8ch_89(struct hda_pcm_stream *hinfo,
-                                       struct hda_codec *codec,
-                                       unsigned int stream_tag,
-                                       unsigned int format,
-                                       struct snd_pcm_substream *substream)
-{
-       hdmi_set_channel_count(codec, hinfo->nid,
-                              substream->runtime->channels);
-
-       hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
-
-       return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
-}
-
-static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
-                                       struct hda_codec *codec,
-                                       unsigned int stream_tag,
-                                       unsigned int format,
-                                       struct snd_pcm_substream *substream)
-{
-       int chs;
-       unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
-       int i;
-
-       mutex_lock(&codec->spdif_mutex);
-
-       chs = substream->runtime->channels;
-       chan = chs ? (chs - 1) : 1;
-
-       switch (chs) {
-       default:
-       case 0:
-       case 2:
-               chanmask = 0x00;
-               break;
-       case 4:
-               chanmask = 0x08;
-               break;
-       case 6:
-               chanmask = 0x0b;
-               break;
-       case 8:
-               chanmask = 0x13;
-               break;
-       }
-       dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
-       dataDCC2 = 0x2;
-
-       /* set the Audio InforFrame Channel Allocation */
-       snd_hda_codec_write(codec, 0x1, 0,
-                       Nv_VERB_SET_Channel_Allocation, chanmask);
-
-       /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
-       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
-               snd_hda_codec_write(codec,
-                               nvhdmi_master_con_nid_7x,
-                               0,
-                               AC_VERB_SET_DIGI_CONVERT_1,
-                               codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
-
-       /* set the stream id */
-       snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
-                       AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
-
-       /* set the stream format */
-       snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
-                       AC_VERB_SET_STREAM_FORMAT, format);
-
-       /* turn on again (if needed) */
-       /* enable and set the channel status audio/data flag */
-       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
-               snd_hda_codec_write(codec,
-                               nvhdmi_master_con_nid_7x,
-                               0,
-                               AC_VERB_SET_DIGI_CONVERT_1,
-                               codec->spdif_ctls & 0xff);
-               snd_hda_codec_write(codec,
-                               nvhdmi_master_con_nid_7x,
-                               0,
-                               AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
-       }
-
-       for (i = 0; i < 4; i++) {
-               if (chs == 2)
-                       channel_id = 0;
-               else
-                       channel_id = i * 2;
-
-               /* turn off SPDIF once;
-                *otherwise the IEC958 bits won't be updated
-                */
-               if (codec->spdif_status_reset &&
-               (codec->spdif_ctls & AC_DIG1_ENABLE))
-                       snd_hda_codec_write(codec,
-                               nvhdmi_con_nids_7x[i],
-                               0,
-                               AC_VERB_SET_DIGI_CONVERT_1,
-                               codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
-               /* set the stream id */
-               snd_hda_codec_write(codec,
-                               nvhdmi_con_nids_7x[i],
-                               0,
-                               AC_VERB_SET_CHANNEL_STREAMID,
-                               (stream_tag << 4) | channel_id);
-               /* set the stream format */
-               snd_hda_codec_write(codec,
-                               nvhdmi_con_nids_7x[i],
-                               0,
-                               AC_VERB_SET_STREAM_FORMAT,
-                               format);
-               /* turn on again (if needed) */
-               /* enable and set the channel status audio/data flag */
-               if (codec->spdif_status_reset &&
-               (codec->spdif_ctls & AC_DIG1_ENABLE)) {
-                       snd_hda_codec_write(codec,
-                                       nvhdmi_con_nids_7x[i],
-                                       0,
-                                       AC_VERB_SET_DIGI_CONVERT_1,
-                                       codec->spdif_ctls & 0xff);
-                       snd_hda_codec_write(codec,
-                                       nvhdmi_con_nids_7x[i],
-                                       0,
-                                       AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
-               }
-       }
-
-       /* set the Audio Info Frame Checksum */
-       snd_hda_codec_write(codec, 0x1, 0,
-                       Nv_VERB_SET_Info_Frame_Checksum,
-                       (0x71 - chan - chanmask));
-
-       mutex_unlock(&codec->spdif_mutex);
-       return 0;
-}
-
-static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
-                                       struct hda_codec *codec,
-                                       unsigned int stream_tag,
-                                       unsigned int format,
-                                       struct snd_pcm_substream *substream)
-{
-       struct hdmi_spec *spec = codec->spec;
-       return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
-                                       format, substream);
-}
-
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_89 = {
-       .substreams = 1,
-       .channels_min = 2,
-       .ops = {
-               .open = hdmi_pcm_open,
-               .prepare = nvhdmi_dig_playback_pcm_prepare_8ch_89,
-       },
-};
-
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_7x = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 8,
-       .nid = nvhdmi_master_con_nid_7x,
-       .rates = SUPPORTED_RATES,
-       .maxbps = SUPPORTED_MAXBPS,
-       .formats = SUPPORTED_FORMATS,
-       .ops = {
-               .open = nvhdmi_dig_playback_pcm_open,
-               .close = nvhdmi_dig_playback_pcm_close_8ch_7x,
-               .prepare = nvhdmi_dig_playback_pcm_prepare_8ch
-       },
-};
-
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = nvhdmi_master_con_nid_7x,
-       .rates = SUPPORTED_RATES,
-       .maxbps = SUPPORTED_MAXBPS,
-       .formats = SUPPORTED_FORMATS,
-       .ops = {
-               .open = nvhdmi_dig_playback_pcm_open,
-               .close = nvhdmi_dig_playback_pcm_close_2ch,
-               .prepare = nvhdmi_dig_playback_pcm_prepare_2ch
-       },
-};
-
-static int nvhdmi_build_pcms_8ch_89(struct hda_codec *codec)
-{
-       struct hdmi_spec *spec = codec->spec;
-       struct hda_pcm *info = spec->pcm_rec;
-       int i;
-
-       codec->num_pcms = spec->num_cvts;
-       codec->pcm_info = info;
-
-       for (i = 0; i < codec->num_pcms; i++, info++) {
-               unsigned int chans;
-
-               chans = get_wcaps(codec, spec->cvt[i]);
-               chans = get_wcaps_channels(chans);
-
-               info->name = nvhdmi_pcm_names[i];
-               info->pcm_type = HDA_PCM_TYPE_HDMI;
-               info->stream[SNDRV_PCM_STREAM_PLAYBACK]
-                                       = nvhdmi_pcm_digital_playback_8ch_89;
-               info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
-               info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
-       }
-
-       return 0;
-}
-
-static int nvhdmi_build_pcms_8ch_7x(struct hda_codec *codec)
-{
-       struct hdmi_spec *spec = codec->spec;
-       struct hda_pcm *info = spec->pcm_rec;
-
-       codec->num_pcms = 1;
-       codec->pcm_info = info;
-
-       info->name = "NVIDIA HDMI";
-       info->pcm_type = HDA_PCM_TYPE_HDMI;
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK]
-                                       = nvhdmi_pcm_digital_playback_8ch_7x;
-
-       return 0;
-}
-
-static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
-{
-       struct hdmi_spec *spec = codec->spec;
-       struct hda_pcm *info = spec->pcm_rec;
-
-       codec->num_pcms = 1;
-       codec->pcm_info = info;
-
-       info->name = "NVIDIA HDMI";
-       info->pcm_type = HDA_PCM_TYPE_HDMI;
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK]
-                                       = nvhdmi_pcm_digital_playback_2ch;
-
-       return 0;
-}
-
-static struct hda_codec_ops nvhdmi_patch_ops_8ch_89 = {
-       .build_controls = nvhdmi_build_controls,
-       .build_pcms = nvhdmi_build_pcms_8ch_89,
-       .init = nvhdmi_init,
-       .free = nvhdmi_free,
-       .unsol_event = hdmi_unsol_event,
-};
-
-static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
-       .build_controls = nvhdmi_build_controls,
-       .build_pcms = nvhdmi_build_pcms_8ch_7x,
-       .init = nvhdmi_init,
-       .free = nvhdmi_free,
-};
-
-static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
-       .build_controls = nvhdmi_build_controls,
-       .build_pcms = nvhdmi_build_pcms_2ch,
-       .init = nvhdmi_init,
-       .free = nvhdmi_free,
-};
-
-static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
-{
-       struct hdmi_spec *spec;
-       int i;
-
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
-       spec->codec_type = HDA_CODEC_NVIDIA_MCP89;
-       spec->old_pin_detect = 1;
-
-       if (hdmi_parse_codec(codec) < 0) {
-               codec->spec = NULL;
-               kfree(spec);
-               return -EINVAL;
-       }
-       codec->patch_ops = nvhdmi_patch_ops_8ch_89;
-
-       for (i = 0; i < spec->num_pins; i++)
-               snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
-
-       init_channel_allocations();
-
-       return 0;
-}
-
-static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
-{
-       struct hdmi_spec *spec;
-
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
-
-       spec->multiout.num_dacs = 0;  /* no analog */
-       spec->multiout.max_channels = 8;
-       spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
-       spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
-       spec->old_pin_detect = 1;
-
-       codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
-
-       return 0;
-}
-
-static int patch_nvhdmi_2ch(struct hda_codec *codec)
-{
-       struct hdmi_spec *spec;
-
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
-
-       spec->multiout.num_dacs = 0;  /* no analog */
-       spec->multiout.max_channels = 2;
-       spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
-       spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
-       spec->old_pin_detect = 1;
-
-       codec->patch_ops = nvhdmi_patch_ops_2ch;
-
-       return 0;
-}
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
-       { .id = 0x10de0002, .name = "MCP77/78 HDMI",  .patch = patch_nvhdmi_8ch_7x },
-       { .id = 0x10de0003, .name = "MCP77/78 HDMI",  .patch = patch_nvhdmi_8ch_7x },
-       { .id = 0x10de0005, .name = "MCP77/78 HDMI",  .patch = patch_nvhdmi_8ch_7x },
-       { .id = 0x10de0006, .name = "MCP77/78 HDMI",  .patch = patch_nvhdmi_8ch_7x },
-       { .id = 0x10de0007, .name = "MCP79/7A HDMI",  .patch = patch_nvhdmi_8ch_7x },
-       { .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de000c, .name = "MCP89 HDMI",     .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-       { .id = 0x10de0067, .name = "MCP67 HDMI",     .patch = patch_nvhdmi_2ch },
-       { .id = 0x10de8001, .name = "MCP73 HDMI",     .patch = patch_nvhdmi_2ch },
-       {} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:10de0002");
-MODULE_ALIAS("snd-hda-codec-id:10de0003");
-MODULE_ALIAS("snd-hda-codec-id:10de0005");
-MODULE_ALIAS("snd-hda-codec-id:10de0006");
-MODULE_ALIAS("snd-hda-codec-id:10de0007");
-MODULE_ALIAS("snd-hda-codec-id:10de000a");
-MODULE_ALIAS("snd-hda-codec-id:10de000b");
-MODULE_ALIAS("snd-hda-codec-id:10de000c");
-MODULE_ALIAS("snd-hda-codec-id:10de000d");
-MODULE_ALIAS("snd-hda-codec-id:10de0010");
-MODULE_ALIAS("snd-hda-codec-id:10de0011");
-MODULE_ALIAS("snd-hda-codec-id:10de0012");
-MODULE_ALIAS("snd-hda-codec-id:10de0013");
-MODULE_ALIAS("snd-hda-codec-id:10de0014");
-MODULE_ALIAS("snd-hda-codec-id:10de0018");
-MODULE_ALIAS("snd-hda-codec-id:10de0019");
-MODULE_ALIAS("snd-hda-codec-id:10de001a");
-MODULE_ALIAS("snd-hda-codec-id:10de001b");
-MODULE_ALIAS("snd-hda-codec-id:10de001c");
-MODULE_ALIAS("snd-hda-codec-id:10de0040");
-MODULE_ALIAS("snd-hda-codec-id:10de0041");
-MODULE_ALIAS("snd-hda-codec-id:10de0042");
-MODULE_ALIAS("snd-hda-codec-id:10de0043");
-MODULE_ALIAS("snd-hda-codec-id:10de0044");
-MODULE_ALIAS("snd-hda-codec-id:10de0067");
-MODULE_ALIAS("snd-hda-codec-id:10de8001");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("NVIDIA HDMI HD-audio codec");
-
-static struct hda_codec_preset_list nvhdmi_list = {
-       .preset = snd_hda_preset_nvhdmi,
-       .owner = THIS_MODULE,
-};
-
-static int __init patch_nvhdmi_init(void)
-{
-       return snd_hda_add_codec_preset(&nvhdmi_list);
-}
-
-static void __exit patch_nvhdmi_exit(void)
-{
-       snd_hda_delete_codec_preset(&nvhdmi_list);
-}
-
-module_init(patch_nvhdmi_init)
-module_exit(patch_nvhdmi_exit)
index a432e6efd19bbe7bbce3ad9b6607c44661f912cf..5f00589cb791aebed2d7aa1f0bd908819b9123b8 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/pci.h>
 #include <sound/core.h>
+#include <sound/jack.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_beep.h"
@@ -282,6 +283,12 @@ struct alc_mic_route {
        unsigned char amix_idx;
 };
 
+struct alc_jack {
+       hda_nid_t nid;
+       int type;
+       struct snd_jack *jack;
+};
+
 #define MUX_IDX_UNDEF  ((unsigned char)-1)
 
 struct alc_customize_define {
@@ -294,6 +301,7 @@ struct alc_customize_define {
        unsigned int  platform_type:1;
        unsigned int  swap:1;
        unsigned int  override:1;
+       unsigned int  fixup:1; /* Means that this sku is set by driver, not read from hw */
 };
 
 struct alc_spec {
@@ -357,6 +365,9 @@ struct alc_spec {
        /* PCM information */
        struct hda_pcm pcm_rec[3];      /* used in alc_build_pcms() */
 
+       /* jack detection */
+       struct snd_array jacks;
+
        /* dynamic controls, init_verbs and input_mux */
        struct auto_pin_cfg autocfg;
        struct alc_customize_define cdefine;
@@ -383,6 +394,7 @@ struct alc_spec {
        unsigned int no_analog :1; /* digital I/O only */
        unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */
        int init_amp;
+       int codec_variant;      /* flag for other variants */
 
        /* for virtual master */
        hda_nid_t vmaster_nid;
@@ -846,7 +858,7 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
 {
        unsigned int val = PIN_IN;
 
-       if (auto_pin_type <= AUTO_PIN_FRONT_MIC) {
+       if (auto_pin_type == AUTO_PIN_MIC) {
                unsigned int pincap;
                unsigned int oldval;
                oldval = snd_hda_codec_read(codec, nid, 0,
@@ -866,6 +878,28 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val);
 }
 
+static void alc_fixup_autocfg_pin_nums(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+
+       if (!cfg->line_outs) {
+               while (cfg->line_outs < AUTO_CFG_MAX_OUTS &&
+                      cfg->line_out_pins[cfg->line_outs])
+                       cfg->line_outs++;
+       }
+       if (!cfg->speaker_outs) {
+               while (cfg->speaker_outs < AUTO_CFG_MAX_OUTS &&
+                      cfg->speaker_pins[cfg->speaker_outs])
+                       cfg->speaker_outs++;
+       }
+       if (!cfg->hp_outs) {
+               while (cfg->hp_outs < AUTO_CFG_MAX_OUTS &&
+                      cfg->hp_pins[cfg->hp_outs])
+                       cfg->hp_outs++;
+       }
+}
+
 /*
  */
 static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix)
@@ -934,6 +968,8 @@ static void setup_preset(struct hda_codec *codec,
 
        if (preset->setup)
                preset->setup(codec);
+
+       alc_fixup_autocfg_pin_nums(codec);
 }
 
 /* Enable GPIO mask and set output */
@@ -990,25 +1026,136 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
        alc_fix_pll(codec);
 }
 
-static void alc_automute_pin(struct hda_codec *codec)
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+static void alc_free_jack_priv(struct snd_jack *jack)
+{
+       struct alc_jack *jacks = jack->private_data;
+       jacks->nid = 0;
+       jacks->jack = NULL;
+}
+
+static int alc_add_jack(struct hda_codec *codec,
+               hda_nid_t nid, int type)
+{
+       struct alc_spec *spec;
+       struct alc_jack *jack;
+       const char *name;
+       int err;
+
+       spec = codec->spec;
+       snd_array_init(&spec->jacks, sizeof(*jack), 32);
+       jack = snd_array_new(&spec->jacks);
+       if (!jack)
+               return -ENOMEM;
+
+       jack->nid = nid;
+       jack->type = type;
+       name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ;
+
+       err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
+       if (err < 0)
+               return err;
+       jack->jack->private_data = jack;
+       jack->jack->private_free = alc_free_jack_priv;
+       return 0;
+}
+
+static void alc_report_jack(struct hda_codec *codec, hda_nid_t nid)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int nid = spec->autocfg.hp_pins[0];
+       struct alc_jack *jacks = spec->jacks.list;
+
+       if (jacks) {
+               int i;
+               for (i = 0; i < spec->jacks.used; i++) {
+                       if (jacks->nid == nid) {
+                               unsigned int present;
+                               present = snd_hda_jack_detect(codec, nid);
+
+                               present = (present) ? jacks->type : 0;
+
+                               snd_jack_report(jacks->jack, present);
+                       }
+                       jacks++;
+               }
+       }
+}
+
+static int alc_init_jacks(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int err;
+       unsigned int hp_nid = spec->autocfg.hp_pins[0];
+       unsigned int mic_nid = spec->ext_mic.pin;
+
+       if (hp_nid) {
+               err = alc_add_jack(codec, hp_nid, SND_JACK_HEADPHONE);
+               if (err < 0)
+                       return err;
+               alc_report_jack(codec, hp_nid);
+       }
+
+       if (mic_nid) {
+               err = alc_add_jack(codec, mic_nid, SND_JACK_MICROPHONE);
+               if (err < 0)
+                       return err;
+               alc_report_jack(codec, mic_nid);
+       }
+
+       return 0;
+}
+#else
+static inline void alc_report_jack(struct hda_codec *codec, hda_nid_t nid)
+{
+}
+
+static inline int alc_init_jacks(struct hda_codec *codec)
+{
+       return 0;
+}
+#endif
+
+static void alc_automute_speaker(struct hda_codec *codec, int pinctl)
+{
+       struct alc_spec *spec = codec->spec;
+       unsigned int mute;
+       hda_nid_t nid;
        int i;
 
-       if (!nid)
-               return;
-       spec->jack_present = snd_hda_jack_detect(codec, nid);
+       spec->jack_present = 0;
+       for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
+               nid = spec->autocfg.hp_pins[i];
+               if (!nid)
+                       break;
+               if (snd_hda_jack_detect(codec, nid)) {
+                       spec->jack_present = 1;
+                       break;
+               }
+               alc_report_jack(codec, spec->autocfg.hp_pins[i]);
+       }
+
+       mute = spec->jack_present ? HDA_AMP_MUTE : 0;
+       /* Toggle internal speakers muting */
        for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
                nid = spec->autocfg.speaker_pins[i];
                if (!nid)
                        break;
-               snd_hda_codec_write(codec, nid, 0,
+               if (pinctl) {
+                       snd_hda_codec_write(codec, nid, 0,
                                    AC_VERB_SET_PIN_WIDGET_CONTROL,
                                    spec->jack_present ? 0 : PIN_OUT);
+               } else {
+                       snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+                                        HDA_AMP_MUTE, mute);
+               }
        }
 }
 
+static void alc_automute_pin(struct hda_codec *codec)
+{
+       alc_automute_speaker(codec, 1);
+}
+
 static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
                                hda_nid_t nid)
 {
@@ -1090,6 +1237,7 @@ static void alc_mic_automute(struct hda_codec *codec)
                                          AC_VERB_SET_CONNECT_SEL,
                                          alive->mux_idx);
        }
+       alc_report_jack(codec, spec->ext_mic.pin);
 
        /* FIXME: analog mixer */
 }
@@ -1236,24 +1384,35 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
 static void alc_init_auto_hp(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
 
-       if (!spec->autocfg.hp_pins[0])
-               return;
+       if (!cfg->hp_pins[0]) {
+               if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+                       return;
+       }
 
-       if (!spec->autocfg.speaker_pins[0]) {
-               if (spec->autocfg.line_out_pins[0] &&
-                   spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
-                       spec->autocfg.speaker_pins[0] =
-                               spec->autocfg.line_out_pins[0];
-               else
+       if (!cfg->speaker_pins[0]) {
+               if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
                        return;
+               memcpy(cfg->speaker_pins, cfg->line_out_pins,
+                      sizeof(cfg->speaker_pins));
+               cfg->speaker_outs = cfg->line_outs;
        }
 
-       snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
-                   spec->autocfg.hp_pins[0]);
-       snd_hda_codec_write_cache(codec, spec->autocfg.hp_pins[0], 0,
+       if (!cfg->hp_pins[0]) {
+               memcpy(cfg->hp_pins, cfg->line_out_pins,
+                      sizeof(cfg->hp_pins));
+               cfg->hp_outs = cfg->line_outs;
+       }
+
+       for (i = 0; i < cfg->hp_outs; i++) {
+               snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
+                           cfg->hp_pins[i]);
+               snd_hda_codec_write_cache(codec, cfg->hp_pins[i], 0,
                                  AC_VERB_SET_UNSOLICITED_ENABLE,
                                  AC_USRSP_EN | ALC880_HP_EVENT);
+       }
        spec->unsol_event = alc_sku_unsol_event;
 }
 
@@ -1265,30 +1424,28 @@ static void alc_init_auto_mic(struct hda_codec *codec)
        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])
+       for (i = 0; i < cfg->num_inputs; i++)
+               if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
                        return;
 
        fixed = ext = 0;
-       for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) {
-               hda_nid_t nid = cfg->input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                unsigned int defcfg;
-               if (!nid)
-                       return;
                defcfg = snd_hda_codec_get_pincfg(codec, nid);
-               switch (get_defcfg_connect(defcfg)) {
-               case AC_JACK_PORT_FIXED:
+               switch (snd_hda_get_input_pin_attr(defcfg)) {
+               case INPUT_PIN_ATTR_INT:
                        if (fixed)
                                return; /* already occupied */
                        fixed = nid;
                        break;
-               case AC_JACK_PORT_COMPLEX:
+               case INPUT_PIN_ATTR_UNUSED:
+                       return; /* invalid entry */
+               default:
                        if (ext)
                                return; /* already occupied */
                        ext = nid;
                        break;
-               default:
-                       return; /* invalid entry */
                }
        }
        if (!ext || !fixed)
@@ -1308,6 +1465,11 @@ static void alc_init_auto_mic(struct hda_codec *codec)
        spec->unsol_event = alc_sku_unsol_event;
 }
 
+/* Could be any non-zero and even value. When used as fixup, tells
+ * the driver to ignore any present sku defines.
+ */
+#define ALC_FIXUP_SKU_IGNORE (2)
+
 static int alc_auto_parse_customize_define(struct hda_codec *codec)
 {
        unsigned int ass, tmp, i;
@@ -1316,6 +1478,13 @@ static int alc_auto_parse_customize_define(struct hda_codec *codec)
 
        spec->cdefine.enable_pcbeep = 1; /* assume always enabled */
 
+       if (spec->cdefine.fixup) {
+               ass = spec->cdefine.sku_cfg;
+               if (ass == ALC_FIXUP_SKU_IGNORE)
+                       return -1;
+               goto do_sku;
+       }
+
        ass = codec->subsystem_id & 0xffff;
        if (ass != codec->bus->pci->subsystem_device && (ass & 1))
                goto do_sku;
@@ -1383,6 +1552,13 @@ static int alc_subsystem_id(struct hda_codec *codec,
        unsigned nid;
        struct alc_spec *spec = codec->spec;
 
+       if (spec->cdefine.fixup) {
+               ass = spec->cdefine.sku_cfg;
+               if (ass == ALC_FIXUP_SKU_IGNORE)
+                       return 0;
+               goto do_sku;
+       }
+
        ass = codec->subsystem_id & 0xffff;
        if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
                goto do_sku;
@@ -1502,6 +1678,7 @@ struct alc_pincfg {
 };
 
 struct alc_fixup {
+       unsigned int sku;
        const struct alc_pincfg *pins;
        const struct hda_verb *verbs;
 };
@@ -1512,12 +1689,22 @@ static void alc_pick_fixup(struct hda_codec *codec,
                           int pre_init)
 {
        const struct alc_pincfg *cfg;
+       struct alc_spec *spec;
 
        quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
        if (!quirk)
                return;
        fix += quirk->value;
        cfg = fix->pins;
+       if (pre_init && fix->sku) {
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               snd_printdd(KERN_INFO "hda_codec: %s: Apply sku override for %s\n",
+                           codec->chip_name, quirk->name);
+#endif
+               spec = codec->spec;
+               spec->cdefine.sku_cfg = fix->sku;
+               spec->cdefine.fixup = 1;
+       }
        if (pre_init && cfg) {
 #ifdef CONFIG_SND_DEBUG_VERBOSE
                snd_printdd(KERN_INFO "hda_codec: %s: Apply pincfg for %s\n",
@@ -1546,6 +1733,15 @@ static int alc_read_coef_idx(struct hda_codec *codec,
        return val;
 }
 
+static void alc_write_coef_idx(struct hda_codec *codec, unsigned int coef_idx,
+                                                       unsigned int coef_val)
+{
+       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
+                           coef_idx);
+       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF,
+                           coef_val);
+}
+
 /* set right pin controls for digital I/O */
 static void alc_auto_init_digital(struct hda_codec *codec)
 {
@@ -1723,31 +1919,7 @@ static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
 
 static void alc_automute_amp(struct hda_codec *codec)
 {
-       struct alc_spec *spec = codec->spec;
-       unsigned int mute;
-       hda_nid_t nid;
-       int i;
-
-       spec->jack_present = 0;
-       for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
-               nid = spec->autocfg.hp_pins[i];
-               if (!nid)
-                       break;
-               if (snd_hda_jack_detect(codec, nid)) {
-                       spec->jack_present = 1;
-                       break;
-               }
-       }
-
-       mute = spec->jack_present ? HDA_AMP_MUTE : 0;
-       /* Toggle internal speakers muting */
-       for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
-               nid = spec->autocfg.speaker_pins[i];
-               if (!nid)
-                       break;
-               snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-       }
+       alc_automute_speaker(codec, 0);
 }
 
 static void alc_automute_amp_unsol_event(struct hda_codec *codec,
@@ -3602,10 +3774,7 @@ static int alc_init(struct hda_codec *codec)
        if (spec->init_hook)
                spec->init_hook(codec);
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       if (codec->patch_ops.check_power_status)
-               codec->patch_ops.check_power_status(codec, 0x01);
-#endif
+       hda_call_check_power_status(codec, 0x01);
        return 0;
 }
 
@@ -4001,10 +4170,7 @@ static int alc_resume(struct hda_codec *codec)
        codec->patch_ops.init(codec);
        snd_hda_codec_resume_amp(codec);
        snd_hda_codec_resume_cache(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       if (codec->patch_ops.check_power_status)
-               codec->patch_ops.check_power_status(codec, 0x01);
-#endif
+       hda_call_check_power_status(codec, 0x01);
        return 0;
 }
 #endif
@@ -4729,7 +4895,7 @@ static struct snd_kcontrol_new alc880_control_templates[] = {
 
 /* add dynamic controls */
 static int add_control(struct alc_spec *spec, int type, const char *name,
-                      unsigned long val)
+                      int cidx, unsigned long val)
 {
        struct snd_kcontrol_new *knew;
 
@@ -4741,6 +4907,7 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
        knew->name = kstrdup(name, GFP_KERNEL);
        if (!knew->name)
                return -ENOMEM;
+       knew->index = cidx;
        if (get_amp_nid_(val))
                knew->subdevice = HDA_SUBDEV_AMP_FLAG;
        knew->private_value = val;
@@ -4749,17 +4916,21 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
 
 static int add_control_with_pfx(struct alc_spec *spec, int type,
                                const char *pfx, const char *dir,
-                               const char *sfx, unsigned long val)
+                               const char *sfx, int cidx, unsigned long val)
 {
        char name[32];
        snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
-       return add_control(spec, type, name, val);
+       return add_control(spec, type, name, cidx, val);
 }
 
-#define add_pb_vol_ctrl(spec, type, pfx, val) \
-       add_control_with_pfx(spec, type, pfx, "Playback", "Volume", val)
-#define add_pb_sw_ctrl(spec, type, pfx, val) \
-       add_control_with_pfx(spec, type, pfx, "Playback", "Switch", val)
+#define add_pb_vol_ctrl(spec, type, pfx, val)                  \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
+#define add_pb_sw_ctrl(spec, type, pfx, val)                   \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
+#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val)                  \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
+#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val)                   \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
 
 #define alc880_is_fixed_pin(nid)       ((nid) >= 0x14 && (nid) <= 0x17)
 #define alc880_fixed_pin_idx(nid)      ((nid) - 0x14)
@@ -4912,16 +5083,16 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
 
 /* create input playback/capture controls for the given pin */
 static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
-                           const char *ctlname,
+                           const char *ctlname, int ctlidx,
                            int idx, hda_nid_t mix_nid)
 {
        int err;
 
-       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
+       err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx,
                          HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
-       err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
+       err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx,
                          HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
@@ -4942,20 +5113,27 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec,
 {
        struct alc_spec *spec = codec->spec;
        struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx;
+       int i, err, idx, type, type_idx = 0;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
+       for (i = 0; i < cfg->num_inputs; i++) {
                hda_nid_t pin;
+               const char *label;
 
-               pin = cfg->input_pins[i];
+               pin = cfg->inputs[i].pin;
                if (!alc_is_input_pin(codec, pin))
                        continue;
 
+               type = cfg->inputs[i].type;
+               if (i > 0 && type == cfg->inputs[i - 1].type)
+                       type_idx++;
+               else
+                       type_idx = 0;
+               label = hda_get_autocfg_input_label(codec, cfg, i);
                if (mixer) {
                        idx = get_connection_index(codec, mixer, pin);
                        if (idx >= 0) {
                                err = new_analog_input(spec, pin,
-                                                      auto_pin_cfg_labels[i],
+                                                      label, type_idx,
                                                       idx, mixer);
                                if (err < 0)
                                        return err;
@@ -4967,12 +5145,8 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec,
                idx = get_connection_index(codec, cap1, pin);
                if (idx < 0 && cap2)
                        idx = get_connection_index(codec, cap2, pin);
-               if (idx >= 0) {
-                       imux->items[imux->num_items].label =
-                               auto_pin_cfg_labels[i];
-                       imux->items[imux->num_items].index = idx;
-                       imux->num_items++;
-               }
+               if (idx >= 0)
+                       snd_hda_add_imux_item(imux, label, idx, NULL);
        }
        return 0;
 }
@@ -5044,12 +5218,13 @@ static void alc880_auto_init_extra_out(struct hda_codec *codec)
 static void alc880_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (alc_is_input_pin(codec, nid)) {
-                       alc_set_input_pin(codec, nid, i);
+                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
                        if (nid != ALC880_PIN_CD_NID &&
                            (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
                                snd_hda_codec_write(codec, nid, 0,
@@ -5214,19 +5389,13 @@ static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
 static void fixup_single_adc(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t pin = 0;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
        /* search for the input pin; there must be only one */
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (spec->autocfg.input_pins[i]) {
-                       pin = spec->autocfg.input_pins[i];
-                       break;
-               }
-       }
-       if (!pin)
+       if (cfg->num_inputs != 1)
                return;
-       i = init_capsrc_for_pin(codec, pin);
+       i = init_capsrc_for_pin(codec, cfg->inputs[0].pin);
        if (i >= 0) {
                /* use only this ADC */
                if (spec->capsrc_nids)
@@ -5279,6 +5448,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
                                 int num_nids)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int n;
        hda_nid_t fallback_adc = 0, fallback_cap = 0;
 
@@ -5304,10 +5474,8 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
                        fallback_adc = adc;
                        fallback_cap = cap;
                }
-               for (i = 0; i < AUTO_PIN_LAST; i++) {
-                       hda_nid_t nid = spec->autocfg.input_pins[i];
-                       if (!nid)
-                               continue;
+               for (i = 0; i < cfg->num_inputs; i++) {
+                       hda_nid_t nid = cfg->inputs[i].pin;
                        for (j = 0; j < nconns; j++) {
                                if (conn[j] == nid)
                                        break;
@@ -5315,7 +5483,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
                        if (j >= nconns)
                                break;
                }
-               if (i >= AUTO_PIN_LAST) {
+               if (i >= cfg->num_inputs) {
                        int num_adcs = spec->num_adc_nids;
                        spec->private_adc_nids[num_adcs] = adc;
                        spec->private_capsrc_nids[num_adcs] = cap;
@@ -6683,12 +6851,13 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)
 static void alc260_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (nid >= 0x12) {
-                       alc_set_input_pin(codec, nid, i);
+                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
                        if (nid != ALC260_PIN_CD_NID &&
                            (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
                                snd_hda_codec_write(codec, nid, 0,
@@ -6810,14 +6979,12 @@ enum {
        PINFIX_HP_DC5750,
 };
 
-static struct alc_pincfg alc260_hp_dc5750_pinfix[] = {
-       { 0x11, 0x90130110 }, /* speaker */
-       { }
-};
-
 static const struct alc_fixup alc260_fixups[] = {
        [PINFIX_HP_DC5750] = {
-               .pins = alc260_hp_dc5750_pinfix
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x11, 0x90130110 }, /* speaker */
+                       { }
+               }
        },
 };
 
@@ -10461,32 +10628,33 @@ static struct alc_config_preset alc882_presets[] = {
 enum {
        PINFIX_ABIT_AW9D_MAX,
        PINFIX_PB_M5210,
-};
-
-static struct alc_pincfg alc882_abit_aw9d_pinfix[] = {
-       { 0x15, 0x01080104 }, /* side */
-       { 0x16, 0x01011012 }, /* rear */
-       { 0x17, 0x01016011 }, /* clfe */
-       { }
-};
-
-static const struct hda_verb pb_m5210_verbs[] = {
-       { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
-       {}
+       PINFIX_ACER_ASPIRE_7736,
 };
 
 static const struct alc_fixup alc882_fixups[] = {
        [PINFIX_ABIT_AW9D_MAX] = {
-               .pins = alc882_abit_aw9d_pinfix
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x15, 0x01080104 }, /* side */
+                       { 0x16, 0x01011012 }, /* rear */
+                       { 0x17, 0x01016011 }, /* clfe */
+                       { }
+               }
        },
        [PINFIX_PB_M5210] = {
-               .verbs = pb_m5210_verbs
+               .verbs = (const struct hda_verb[]) {
+                       { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+                       {}
+               }
+       },
+       [PINFIX_ACER_ASPIRE_7736] = {
+               .sku = ALC_FIXUP_SKU_IGNORE,
        },
 };
 
 static struct snd_pci_quirk alc882_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210),
        SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
+       SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736),
        {}
 };
 
@@ -10535,16 +10703,21 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        hda_nid_t pin, dac;
+       int i;
 
-       pin = spec->autocfg.hp_pins[0];
-       if (pin) {
+       for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
+               pin = spec->autocfg.hp_pins[i];
+               if (!pin)
+                       break;
                dac = spec->multiout.hp_nid;
                if (!dac)
                        dac = spec->multiout.dac_nids[0]; /* to front */
                alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
        }
-       pin = spec->autocfg.speaker_pins[0];
-       if (pin) {
+       for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
+               pin = spec->autocfg.speaker_pins[i];
+               if (!pin)
+                       break;
                dac = spec->multiout.extra_out_nid[0];
                if (!dac)
                        dac = spec->multiout.dac_nids[0]; /* to front */
@@ -10555,13 +10728,12 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
 static void alc882_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
-               if (!nid)
-                       continue;
-               alc_set_input_pin(codec, nid, i);
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
+               alc_set_input_pin(codec, nid, cfg->inputs[i].type);
                if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
                        snd_hda_codec_write(codec, nid, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
@@ -10623,24 +10795,23 @@ static void alc882_auto_init_input_src(struct hda_codec *codec)
 static int alc_auto_add_mic_boost(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       int err;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i, err;
        hda_nid_t nid;
 
-       nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
-       if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
-               err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                 "Mic Boost",
-                                 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
-               if (err < 0)
-                       return err;
-       }
-       nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
-       if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
-               err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                 "Front Mic Boost",
+       for (i = 0; i < cfg->num_inputs; i++) {
+               if (cfg->inputs[i].type > AUTO_PIN_MIC)
+                       break;
+               nid = cfg->inputs[i].pin;
+               if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
+                       char label[32];
+                       snprintf(label, sizeof(label), "%s Boost",
+                                hda_get_autocfg_input_label(codec, cfg, i));
+                       err = add_control(spec, ALC_CTL_WIDGET_VOL, label, 0,
                                  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
-               if (err < 0)
-                       return err;
+                       if (err < 0)
+                               return err;
+               }
        }
        return 0;
 }
@@ -10726,8 +10897,6 @@ static int patch_alc882(struct hda_codec *codec)
 
        codec->spec = spec;
 
-       alc_auto_parse_customize_define(codec);
-
        switch (codec->vendor_id) {
        case 0x10ec0882:
        case 0x10ec0885:
@@ -10755,6 +10924,8 @@ static int patch_alc882(struct hda_codec *codec)
        if (board_config == ALC882_AUTO)
                alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups, 1);
 
+       alc_auto_parse_customize_define(codec);
+
        if (board_config == ALC882_AUTO) {
                /* automatic parse from the BIOS config */
                err = alc882_parse_auto_config(codec);
@@ -10835,6 +11006,8 @@ static int patch_alc882(struct hda_codec *codec)
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC882_AUTO)
                spec->init_hook = alc882_auto_init;
+
+       alc_init_jacks(codec);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc882_loopbacks;
@@ -11831,7 +12004,7 @@ static int alc262_check_volbit(hda_nid_t nid)
 }
 
 static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
-                                 const char *pfx, int *vbits)
+                                 const char *pfx, int *vbits, int idx)
 {
        unsigned long val;
        int vbit;
@@ -11846,11 +12019,11 @@ static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
                val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT);
        else
                val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT);
-       return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, val);
+       return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, val);
 }
 
 static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
-                                const char *pfx)
+                                const char *pfx, int idx)
 {
        unsigned long val;
 
@@ -11860,7 +12033,7 @@ static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
                val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
        else
                val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
-       return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, val);
+       return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, val);
 }
 
 /* add playback controls from the parsed DAC table */
@@ -11869,7 +12042,7 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
 {
        const char *pfx;
        int vbits;
-       int err;
+       int i, err;
 
        spec->multiout.num_dacs = 1;    /* only use one dac */
        spec->multiout.dac_nids = spec->private_dac_nids;
@@ -11879,39 +12052,52 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
                pfx = "Master";
        else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
                pfx = "Speaker";
+       else if (cfg->line_out_type == AUTO_PIN_HP_OUT)
+               pfx = "Headphone";
        else
                pfx = "Front";
-       err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[0], pfx);
-       if (err < 0)
-               return err;
-       err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[0], "Speaker");
-       if (err < 0)
-               return err;
-       err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[0], "Headphone");
-       if (err < 0)
-               return err;
+       for (i = 0; i < 2; i++) {
+               err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, i);
+               if (err < 0)
+                       return err;
+               if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+                       err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[i],
+                                                   "Speaker", i);
+                       if (err < 0)
+                               return err;
+               }
+               if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+                       err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[i],
+                                                   "Headphone", i);
+                       if (err < 0)
+                               return err;
+               }
+       }
 
        vbits = alc262_check_volbit(cfg->line_out_pins[0]) |
                alc262_check_volbit(cfg->speaker_pins[0]) |
                alc262_check_volbit(cfg->hp_pins[0]);
        if (vbits == 1 || vbits == 2)
                pfx = "Master"; /* only one mixer is used */
-       else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
-               pfx = "Speaker";
-       else
-               pfx = "Front";
        vbits = 0;
-       err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[0], pfx, &vbits);
-       if (err < 0)
-               return err;
-       err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[0], "Speaker",
-                                    &vbits);
-       if (err < 0)
-               return err;
-       err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[0], "Headphone",
-                                    &vbits);
-       if (err < 0)
-               return err;
+       for (i = 0; i < 2; i++) {
+               err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx,
+                                            &vbits, i);
+               if (err < 0)
+                       return err;
+               if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+                       err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[i],
+                                                    "Speaker", &vbits, i);
+                       if (err < 0)
+                               return err;
+               }
+               if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+                       err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[i],
+                                                    "Headphone", &vbits, i);
+                       if (err < 0)
+                               return err;
+               }
+       }
        return 0;
 }
 
@@ -12199,6 +12385,35 @@ static struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = {
        {}
 };
 
+/*
+ * Pin config fixes
+ */
+enum {
+       PINFIX_FSC_H270,
+};
+
+static const struct alc_fixup alc262_fixups[] = {
+       [PINFIX_FSC_H270] = {
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x14, 0x99130110 }, /* speaker */
+                       { 0x15, 0x0221142f }, /* front HP */
+                       { 0x1b, 0x0121141f }, /* rear HP */
+                       { }
+               }
+       },
+       [PINFIX_PB_M5210] = {
+               .verbs = (const struct hda_verb[]) {
+                       { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+                       {}
+               }
+       },
+};
+
+static struct snd_pci_quirk alc262_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", PINFIX_FSC_H270),
+       {}
+};
+
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc262_loopbacks       alc880_loopbacks
@@ -12622,6 +12837,9 @@ static int patch_alc262(struct hda_codec *codec)
                board_config = ALC262_AUTO;
        }
 
+       if (board_config == ALC262_AUTO)
+               alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 1);
+
        if (board_config == ALC262_AUTO) {
                /* automatic parse from the BIOS config */
                err = alc262_parse_auto_config(codec);
@@ -12690,11 +12908,16 @@ static int patch_alc262(struct hda_codec *codec)
        if (!spec->no_analog && has_cdefine_beep(codec))
                set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 
+       if (board_config == ALC262_AUTO)
+               alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 0);
+
        spec->vmaster_nid = 0x0c;
 
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC262_AUTO)
                spec->init_hook = alc262_auto_init;
+
+       alc_init_jacks(codec);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc262_loopbacks;
@@ -13310,8 +13533,10 @@ static void alc268_auto_set_output_and_unmute(struct hda_codec *codec,
 static void alc268_auto_init_multi_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t nid = spec->autocfg.line_out_pins[0];
-       if (nid) {
+       int i;
+
+       for (i = 0; i < spec->autocfg.line_outs; i++) {
+               hda_nid_t nid = spec->autocfg.line_out_pins[i];
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
                alc268_auto_set_output_and_unmute(codec, nid, pin_type);
        }
@@ -13321,13 +13546,19 @@ static void alc268_auto_init_hp_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        hda_nid_t pin;
+       int i;
 
-       pin = spec->autocfg.hp_pins[0];
-       if (pin)
+       for (i = 0; i < spec->autocfg.hp_outs; i++) {
+               pin = spec->autocfg.hp_pins[i];
                alc268_auto_set_output_and_unmute(codec, pin, PIN_HP);
-       pin = spec->autocfg.speaker_pins[0];
-       if (pin)
+       }
+       for (i = 0; i < spec->autocfg.speaker_outs; i++) {
+               pin = spec->autocfg.speaker_pins[i];
                alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT);
+       }
+       if (spec->autocfg.mono_out_pin)
+               snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
 }
 
 static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
@@ -13766,6 +13997,8 @@ static int patch_alc268(struct hda_codec *codec)
        if (board_config == ALC268_AUTO)
                spec->init_hook = alc268_auto_init;
 
+       alc_init_jacks(codec);
+
        return 0;
 }
 
@@ -14132,6 +14365,7 @@ static void alc269_speaker_automute(struct hda_codec *codec)
                                 HDA_AMP_MUTE, bits);
        snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
                                 HDA_AMP_MUTE, bits);
+       alc_report_jack(codec, nid);
 }
 
 /* unsolicited event for HP jack sensing */
@@ -14386,6 +14620,13 @@ static int alc275_setup_dual_adc(struct hda_codec *codec)
        return 0;
 }
 
+/* different alc269-variants */
+enum {
+       ALC269_TYPE_NORMAL,
+       ALC269_TYPE_ALC259,
+       ALC269_TYPE_ALC271X,
+};
+
 /*
  * BIOS auto configuration
  */
@@ -14403,7 +14644,11 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
        err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
        if (err < 0)
                return err;
-       err = alc269_auto_create_input_ctls(codec, &spec->autocfg);
+       if (spec->codec_variant == ALC269_TYPE_NORMAL)
+               err = alc269_auto_create_input_ctls(codec, &spec->autocfg);
+       else
+               err = alc_auto_create_input_ctls(codec, &spec->autocfg, 0,
+                                                0x22, 0);
        if (err < 0)
                return err;
 
@@ -14414,7 +14659,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010) {
+       if (spec->codec_variant != ALC269_TYPE_NORMAL) {
                add_verb(spec, alc269vb_init_verbs);
                alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21);
        } else {
@@ -14461,19 +14706,71 @@ static void alc269_auto_init(struct hda_codec *codec)
                alc_inithook(codec);
 }
 
+#ifdef SND_HDA_NEEDS_RESUME
+static void alc269_toggle_power_output(struct hda_codec *codec, int power_up)
+{
+       int val = alc_read_coef_idx(codec, 0x04);
+       if (power_up)
+               val |= 1 << 11;
+       else
+               val &= ~(1 << 11);
+       alc_write_coef_idx(codec, 0x04, val);
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static int alc269_suspend(struct hda_codec *codec, pm_message_t state)
+{
+       struct alc_spec *spec = codec->spec;
+
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017)
+               alc269_toggle_power_output(codec, 0);
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
+               alc269_toggle_power_output(codec, 0);
+               msleep(150);
+       }
+
+       alc_shutup(codec);
+       if (spec && spec->power_hook)
+               spec->power_hook(codec);
+       return 0;
+}
+#endif /* CONFIG_SND_HDA_POWER_SAVE */
+
+static int alc269_resume(struct hda_codec *codec)
+{
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
+               alc269_toggle_power_output(codec, 0);
+               msleep(150);
+       }
+
+       codec->patch_ops.init(codec);
+
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) {
+               alc269_toggle_power_output(codec, 1);
+               msleep(200);
+       }
+
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018)
+               alc269_toggle_power_output(codec, 1);
+
+       snd_hda_codec_resume_amp(codec);
+       snd_hda_codec_resume_cache(codec);
+       hda_call_check_power_status(codec, 0x01);
+       return 0;
+}
+#endif /* SND_HDA_NEEDS_RESUME */
+
 enum {
        ALC269_FIXUP_SONY_VAIO,
        ALC269_FIXUP_DELL_M101Z,
 };
 
-static const struct hda_verb alc269_sony_vaio_fixup_verbs[] = {
-       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
-       {}
-};
-
 static const struct alc_fixup alc269_fixups[] = {
        [ALC269_FIXUP_SONY_VAIO] = {
-               .verbs = alc269_sony_vaio_fixup_verbs
+               .verbs = (const struct hda_verb[]) {
+                       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
+                       {}
+               }
        },
        [ALC269_FIXUP_DELL_M101Z] = {
                .verbs = (const struct hda_verb[]) {
@@ -14486,8 +14783,7 @@ static const struct alc_fixup alc269_fixups[] = {
 };
 
 static struct snd_pci_quirk alc269_fixup_tbl[] = {
-       SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
-       SND_PCI_QUIRK(0x104d, 0x9077, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
+       SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
        SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
        {}
 };
@@ -14689,12 +14985,46 @@ static struct alc_config_preset alc269_presets[] = {
        },
 };
 
+static int alc269_fill_coef(struct hda_codec *codec)
+{
+       int val;
+
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) < 0x015) {
+               alc_write_coef_idx(codec, 0xf, 0x960b);
+               alc_write_coef_idx(codec, 0xe, 0x8817);
+       }
+
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x016) {
+               alc_write_coef_idx(codec, 0xf, 0x960b);
+               alc_write_coef_idx(codec, 0xe, 0x8814);
+       }
+
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) {
+               val = alc_read_coef_idx(codec, 0x04);
+               /* Power up output pin */
+               alc_write_coef_idx(codec, 0x04, val | (1<<11));
+       }
+
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
+               val = alc_read_coef_idx(codec, 0xd);
+               if ((val & 0x0c00) >> 10 != 0x1) {
+                       /* Capless ramp up clock control */
+                       alc_write_coef_idx(codec, 0xd, val | 1<<10);
+               }
+               val = alc_read_coef_idx(codec, 0x17);
+               if ((val & 0x01c0) >> 6 != 0x4) {
+                       /* Class D power on reset */
+                       alc_write_coef_idx(codec, 0x17, val | 1<<7);
+               }
+       }
+       return 0;
+}
+
 static int patch_alc269(struct hda_codec *codec)
 {
        struct alc_spec *spec;
        int board_config;
        int err;
-       int is_alc269vb = 0;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -14706,14 +15036,18 @@ static int patch_alc269(struct hda_codec *codec)
 
        if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){
                if (codec->bus->pci->subsystem_vendor == 0x1025 &&
-                   spec->cdefine.platform_type == 1)
+                   spec->cdefine.platform_type == 1) {
                        alc_codec_rename(codec, "ALC271X");
-               else
+                       spec->codec_variant = ALC269_TYPE_ALC271X;
+               } else {
                        alc_codec_rename(codec, "ALC259");
-               is_alc269vb = 1;
+                       spec->codec_variant = ALC269_TYPE_ALC259;
+               }
        } else
                alc_fix_pll_init(codec, 0x20, 0x04, 15);
 
+       alc269_fill_coef(codec);
+
        board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
                                                  alc269_models,
                                                  alc269_cfg_tbl);
@@ -14770,7 +15104,7 @@ static int patch_alc269(struct hda_codec *codec)
        spec->stream_digital_capture = &alc269_pcm_digital_capture;
 
        if (!spec->adc_nids) { /* wasn't filled automatically? use default */
-               if (!is_alc269vb) {
+               if (spec->codec_variant != ALC269_TYPE_NORMAL) {
                        spec->adc_nids = alc269_adc_nids;
                        spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
                        spec->capsrc_nids = alc269_capsrc_nids;
@@ -14792,8 +15126,16 @@ static int patch_alc269(struct hda_codec *codec)
        spec->vmaster_nid = 0x02;
 
        codec->patch_ops = alc_patch_ops;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       codec->patch_ops.suspend = alc269_suspend;
+#endif
+#ifdef SND_HDA_NEEDS_RESUME
+       codec->patch_ops.resume = alc269_resume;
+#endif
        if (board_config == ALC269_AUTO)
                spec->init_hook = alc269_auto_init;
+
+       alc_init_jacks(codec);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc269_loopbacks;
@@ -15606,12 +15948,13 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec)
 static void alc861_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (nid >= 0x0c && nid <= 0x11)
-                       alc_set_input_pin(codec, nid, i);
+                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
        }
 }
 
@@ -15840,15 +16183,13 @@ enum {
        PINFIX_FSC_AMILO_PI1505,
 };
 
-static struct alc_pincfg alc861_fsc_amilo_pi1505_pinfix[] = {
-       { 0x0b, 0x0221101f }, /* HP */
-       { 0x0f, 0x90170310 }, /* speaker */
-       { }
-};
-
 static const struct alc_fixup alc861_fixups[] = {
        [PINFIX_FSC_AMILO_PI1505] = {
-               .pins = alc861_fsc_amilo_pi1505_pinfix
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x0b, 0x0221101f }, /* HP */
+                       { 0x0f, 0x90170310 }, /* speaker */
+                       { }
+               }
        },
 };
 
@@ -16600,12 +16941,13 @@ static void alc861vd_auto_init_hp_out(struct hda_codec *codec)
 static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (alc_is_input_pin(codec, nid)) {
-                       alc_set_input_pin(codec, nid, i);
+                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
                        if (nid != ALC861VD_PIN_CD_NID &&
                            (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
                                snd_hda_codec_write(codec, nid, 0,
@@ -16815,16 +17157,14 @@ enum {
 };
 
 /* reset GPIO1 */
-static const struct hda_verb alc660vd_fix_asus_gpio1_verbs[] = {
-       {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
-       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
-       {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
-       { }
-};
-
 static const struct alc_fixup alc861vd_fixups[] = {
        [ALC660VD_FIX_ASUS_GPIO1] = {
-               .verbs = alc660vd_fix_asus_gpio1_verbs,
+               .verbs = (const struct hda_verb[]) {
+                       {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
+                       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
+                       {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
+                       { }
+               }
        },
 };
 
@@ -18838,12 +19178,13 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec)
 static void alc662_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (alc_is_input_pin(codec, nid)) {
-                       alc_set_input_pin(codec, nid, i);
+                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
                        if (nid != ALC662_PIN_CD_NID &&
                            (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
                                snd_hda_codec_write(codec, nid, 0,
@@ -18935,10 +19276,40 @@ static void alc662_auto_init(struct hda_codec *codec)
                alc_inithook(codec);
 }
 
+enum {
+       ALC662_FIXUP_ASPIRE,
+       ALC662_FIXUP_IDEAPAD,
+};
+
+static const struct alc_fixup alc662_fixups[] = {
+       [ALC662_FIXUP_ASPIRE] = {
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x15, 0x99130112 }, /* subwoofer */
+                       { }
+               }
+       },
+       [ALC662_FIXUP_IDEAPAD] = {
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x17, 0x99130112 }, /* subwoofer */
+                       { }
+               }
+       },
+};
+
+static struct snd_pci_quirk alc662_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
+       SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
+       SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
+       {}
+};
+
+
+
 static int patch_alc662(struct hda_codec *codec)
 {
        struct alc_spec *spec;
        int err, board_config;
+       int coef;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (!spec)
@@ -18950,12 +19321,15 @@ static int patch_alc662(struct hda_codec *codec)
 
        alc_fix_pll_init(codec, 0x20, 0x04, 15);
 
-       if (alc_read_coef_idx(codec, 0) == 0x8020)
+       coef = alc_read_coef_idx(codec, 0);
+       if (coef == 0x8020 || coef == 0x8011)
                alc_codec_rename(codec, "ALC661");
-       else if ((alc_read_coef_idx(codec, 0) & (1 << 14)) &&
-                codec->bus->pci->subsystem_vendor == 0x1025 &&
-                spec->cdefine.platform_type == 1)
+       else if (coef & (1 << 14) &&
+               codec->bus->pci->subsystem_vendor == 0x1025 &&
+               spec->cdefine.platform_type == 1)
                alc_codec_rename(codec, "ALC272X");
+       else if (coef == 0x4011)
+               alc_codec_rename(codec, "ALC656");
 
        board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST,
                                                  alc662_models,
@@ -18967,6 +19341,7 @@ static int patch_alc662(struct hda_codec *codec)
        }
 
        if (board_config == ALC662_AUTO) {
+               alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 1);
                /* automatic parse from the BIOS config */
                err = alc662_parse_auto_config(codec);
                if (err < 0) {
@@ -19025,8 +19400,13 @@ static int patch_alc662(struct hda_codec *codec)
        spec->vmaster_nid = 0x02;
 
        codec->patch_ops = alc_patch_ops;
-       if (board_config == ALC662_AUTO)
+       if (board_config == ALC662_AUTO) {
                spec->init_hook = alc662_auto_init;
+               alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 0);
+       }
+
+       alc_init_jacks(codec);
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc662_loopbacks;
@@ -19070,6 +19450,39 @@ static hda_nid_t alc680_adc_nids[3] = {
 /*
  * Analog capture ADC cgange
  */
+static void alc680_rec_autoswitch(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int pin_found = 0;
+       int type_found = AUTO_PIN_LAST;
+       hda_nid_t nid;
+       int i;
+
+       for (i = 0; i < cfg->num_inputs; i++) {
+               nid = cfg->inputs[i].pin;
+               if (!(snd_hda_query_pin_caps(codec, nid) &
+                     AC_PINCAP_PRES_DETECT))
+                       continue;
+               if (snd_hda_jack_detect(codec, nid)) {
+                       if (cfg->inputs[i].type < type_found) {
+                               type_found = cfg->inputs[i].type;
+                               pin_found = nid;
+                       }
+               }
+       }
+
+       nid = 0x07;
+       if (pin_found)
+               snd_hda_get_connections(codec, pin_found, &nid, 1);
+
+       if (nid != spec->cur_adc)
+               __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+       spec->cur_adc = nid;
+       snd_hda_codec_setup_stream(codec, nid, spec->cur_adc_stream_tag, 0,
+                                  spec->cur_adc_format);
+}
+
 static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
                                      struct hda_codec *codec,
                                      unsigned int stream_tag,
@@ -19077,24 +19490,12 @@ static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
                                      struct snd_pcm_substream *substream)
 {
        struct alc_spec *spec = codec->spec;
-       struct auto_pin_cfg *cfg = &spec->autocfg;
-       unsigned int pre_mic, pre_line;
-
-       pre_mic  = snd_hda_jack_detect(codec, cfg->input_pins[AUTO_PIN_MIC]);
-       pre_line = snd_hda_jack_detect(codec, cfg->input_pins[AUTO_PIN_LINE]);
 
+       spec->cur_adc = 0x07;
        spec->cur_adc_stream_tag = stream_tag;
        spec->cur_adc_format = format;
 
-       if (pre_mic || pre_line) {
-               if (pre_mic)
-                       snd_hda_codec_setup_stream(codec, 0x08, stream_tag, 0,
-                                                                       format);
-               else
-                       snd_hda_codec_setup_stream(codec, 0x09, stream_tag, 0,
-                                                                       format);
-       } else
-               snd_hda_codec_setup_stream(codec, 0x07, stream_tag, 0, format);
+       alc680_rec_autoswitch(codec);
        return 0;
 }
 
@@ -19180,6 +19581,7 @@ static struct hda_verb alc680_init_verbs[] = {
 
        {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT   | AC_USRSP_EN},
        {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT  | AC_USRSP_EN},
+       {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT  | AC_USRSP_EN},
 
        { }
 };
@@ -19192,25 +19594,11 @@ static void alc680_base_setup(struct hda_codec *codec)
        spec->autocfg.hp_pins[0] = 0x16;
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x15;
-       spec->autocfg.input_pins[AUTO_PIN_MIC] = 0x18;
-       spec->autocfg.input_pins[AUTO_PIN_LINE] = 0x19;
-}
-
-static void alc680_rec_autoswitch(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       struct auto_pin_cfg *cfg = &spec->autocfg;
-       unsigned int present;
-       hda_nid_t new_adc;
-
-       present = snd_hda_jack_detect(codec, cfg->input_pins[AUTO_PIN_MIC]);
-
-       new_adc = present ? 0x8 : 0x7;
-       __snd_hda_codec_cleanup_stream(codec, !present ? 0x8 : 0x7, 1);
-       snd_hda_codec_setup_stream(codec, new_adc,
-                                  spec->cur_adc_stream_tag, 0,
-                                  spec->cur_adc_format);
-
+       spec->autocfg.num_inputs = 2;
+       spec->autocfg.inputs[0].pin = 0x18;
+       spec->autocfg.inputs[0].type = AUTO_PIN_MIC;
+       spec->autocfg.inputs[1].pin = 0x19;
+       spec->autocfg.inputs[1].type = AUTO_PIN_LINE_IN;
 }
 
 static void alc680_unsol_event(struct hda_codec *codec,
index c16c5ba0fda0fe61387d6924a6b84307b0dedcbb..82ebeb9544fe54296ab3043d9a25e34cee04f415 100644 (file)
@@ -32,6 +32,7 @@
 #include <sound/core.h>
 #include <sound/asoundef.h>
 #include <sound/jack.h>
+#include <sound/tlv.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_beep.h"
@@ -263,6 +264,7 @@ struct sigmatel_spec {
 
        struct sigmatel_mic_route ext_mic;
        struct sigmatel_mic_route int_mic;
+       struct sigmatel_mic_route dock_mic;
 
        const char **spdif_labels;
 
@@ -382,6 +384,11 @@ static unsigned int stac92hd83xxx_pwr_mapping[4] = {
        0x03, 0x0c, 0x20, 0x40,
 };
 
+#define STAC92HD83XXX_NUM_DMICS         2
+static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
+       0x11, 0x20, 0
+};
+
 #define STAC92HD83XXX_NUM_CAPS 2
 static unsigned long stac92hd83xxx_capvols[] = {
        HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
@@ -986,7 +993,7 @@ static struct hda_verb stac9205_core_init[] = {
        }
 
 static struct snd_kcontrol_new stac9200_mixer[] = {
-       HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
@@ -1014,7 +1021,7 @@ static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
 };
 
 static struct snd_kcontrol_new stac925x_mixer[] = {
-       HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
        { } /* end */
 };
@@ -1105,9 +1112,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
                struct hda_input_mux *smux = &spec->private_smux;
                /* check for mute support on SPDIF out */
                if (wcaps & AC_WCAP_OUT_AMP) {
-                       smux->items[smux->num_items].label = "Off";
-                       smux->items[smux->num_items].index = 0;
-                       smux->num_items++;
+                       snd_hda_add_imux_item(smux, "Off", 0, NULL);
                        spec->spdif_mute = 1;
                }
                stac_smux_mixer.count = spec->num_smuxes;
@@ -1140,6 +1145,8 @@ static int stac92xx_build_controls(struct hda_codec *codec)
                                        HDA_OUTPUT, vmaster_tlv);
                /* correct volume offset */
                vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset;
+               /* minimum value is actually mute */
+               vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
                err = snd_hda_add_vmaster(codec, "Master Playback Volume",
                                          vmaster_tlv, slave_vols);
                if (err < 0)
@@ -1180,14 +1187,11 @@ static int stac92xx_build_controls(struct hda_codec *codec)
                if (err < 0)
                        return err;
        }
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               nid = cfg->input_pins[i];
-               if (nid) {
-                       err = stac92xx_add_jack(codec, nid,
-                                               SND_JACK_MICROPHONE);
-                       if (err < 0)
-                               return err;
-               }
+       for (i = 0; i < cfg->num_inputs; i++) {
+               nid = cfg->inputs[i].pin;
+               err = stac92xx_add_jack(codec, nid, SND_JACK_MICROPHONE);
+               if (err < 0)
+                       return err;
        }
 
        return 0;       
@@ -2779,7 +2783,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
        struct sigmatel_spec *spec = codec->spec;
        char name[22];
 
-       if (!((get_defcfg_connect(def_conf)) & AC_JACK_PORT_FIXED)) {
+       if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
                if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
                        && nid == spec->line_switch)
                        control = STAC_CTL_WIDGET_IO_SWITCH;
@@ -2791,7 +2795,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
        }
 
        if (control) {
-               strcpy(name, auto_pin_cfg_labels[idx]);
+               strcpy(name, hda_get_input_pin_label(codec, nid, 1));
                return stac92xx_add_control(codec->spec, control,
                                        strcat(name, " Jack Mode"), nid);
        }
@@ -2823,41 +2827,49 @@ static hda_nid_t check_line_out_switch(struct hda_codec *codec)
        struct auto_pin_cfg *cfg = &spec->autocfg;
        hda_nid_t nid;
        unsigned int pincap;
+       int i;
 
        if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
                return 0;
-       nid = cfg->input_pins[AUTO_PIN_LINE];
-       pincap = snd_hda_query_pin_caps(codec, nid);
-       if (pincap & AC_PINCAP_OUT)
-               return nid;
+       for (i = 0; i < cfg->num_inputs; i++) {
+               if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) {
+                       nid = cfg->inputs[i].pin;
+                       pincap = snd_hda_query_pin_caps(codec, nid);
+                       if (pincap & AC_PINCAP_OUT)
+                               return nid;
+               }
+       }
        return 0;
 }
 
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid);
+
 /* check whether the mic-input can be used as line-out */
-static hda_nid_t check_mic_out_switch(struct hda_codec *codec)
+static hda_nid_t check_mic_out_switch(struct hda_codec *codec, hda_nid_t *dac)
 {
        struct sigmatel_spec *spec = codec->spec;
        struct auto_pin_cfg *cfg = &spec->autocfg;
        unsigned int def_conf, pincap;
-       unsigned int mic_pin;
+       int i;
 
+       *dac = 0;
        if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
                return 0;
-       mic_pin = AUTO_PIN_MIC;
-       for (;;) {
-               hda_nid_t nid = cfg->input_pins[mic_pin];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
+               if (cfg->inputs[i].type != AUTO_PIN_MIC)
+                       continue;
                def_conf = snd_hda_codec_get_pincfg(codec, nid);
                /* some laptops have an internal analog microphone
                 * which can't be used as a output */
-               if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
+               if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
                        pincap = snd_hda_query_pin_caps(codec, nid);
-                       if (pincap & AC_PINCAP_OUT)
-                               return nid;
+                       if (pincap & AC_PINCAP_OUT) {
+                               *dac = get_unassigned_dac(codec, nid);
+                               if (*dac)
+                                       return nid;
+                       }
                }
-               if (mic_pin == AUTO_PIN_MIC)
-                       mic_pin = AUTO_PIN_FRONT_MIC;
-               else
-                       break;
        }
        return 0;
 }
@@ -3004,17 +3016,14 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
                }
        }
        /* add mic as output */
-       nid = check_mic_out_switch(codec);
-       if (nid) {
-               dac = get_unassigned_dac(codec, nid);
-               if (dac) {
-                       snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
-                                   nid, cfg->line_outs);
-                       cfg->line_out_pins[cfg->line_outs] = nid;
-                       cfg->line_outs++;
-                       spec->mic_switch = nid;
-                       add_spec_dacs(spec, dac);
-               }
+       nid = check_mic_out_switch(codec, &dac);
+       if (nid && dac) {
+               snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
+                           nid, cfg->line_outs);
+               cfg->line_out_pins[cfg->line_outs] = nid;
+               cfg->line_outs++;
+               spec->mic_switch = nid;
+               add_spec_dacs(spec, dac);
        }
 
        snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
@@ -3204,13 +3213,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
                        return err;
        }
 
-       for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) {
-               nid = cfg->input_pins[idx];
-               if (nid) {
-                       err = stac92xx_add_jack_mode_control(codec, nid, idx);
-                       if (err < 0)
-                               return err;
-               }
+       for (idx = 0; idx < cfg->num_inputs; idx++) {
+               if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
+                       break;
+               nid = cfg->inputs[idx].pin;
+               err = stac92xx_add_jack_mode_control(codec, nid, idx);
+               if (err < 0)
+                       return err;
        }
 
        return 0;
@@ -3256,12 +3265,9 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
        if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
                return -EINVAL;
 
-       for (i = 0; i < num_cons; i++) {
-               mono_mux->items[mono_mux->num_items].label =
-                                       stac92xx_mono_labels[i];
-               mono_mux->items[mono_mux->num_items].index = i;
-               mono_mux->num_items++;
-       }
+       for (i = 0; i < num_cons; i++)
+               snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i,
+                                     NULL);
 
        return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
                                "Mono Mux", spec->mono_nid);
@@ -3386,11 +3392,8 @@ static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
        if (!labels)
                labels = stac92xx_spdif_labels;
 
-       for (i = 0; i < num_cons; i++) {
-               spdif_mux->items[spdif_mux->num_items].label = labels[i];
-               spdif_mux->items[spdif_mux->num_items].index = i;
-               spdif_mux->num_items++;
-       }
+       for (i = 0; i < num_cons; i++)
+               snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL);
 
        return 0;
 }
@@ -3417,7 +3420,7 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
 /* create a volume assigned to the given pin (only if supported) */
 /* return 1 if the volume control is created */
 static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
-                                  const char *label, int direction)
+                                  const char *label, int idx, int direction)
 {
        unsigned int caps, nums;
        char name[32];
@@ -3434,8 +3437,8 @@ static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
        if (!nums)
                return 0;
        snprintf(name, sizeof(name), "%s Capture Volume", label);
-       err = stac92xx_add_control(codec->spec, STAC_CTL_WIDGET_VOL, name,
-                                   HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
+       err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name,
+                                      HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
        if (err < 0)
                return err;
        return 1;
@@ -3448,27 +3451,14 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
        struct sigmatel_spec *spec = codec->spec;
        struct hda_input_mux *imux = &spec->private_imux;
        struct hda_input_mux *dimux = &spec->private_dimux;
-       int err, i, active_mics;
+       int err, i;
        unsigned int def_conf;
 
-       dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
-       dimux->items[dimux->num_items].index = 0;
-       dimux->num_items++;
-
-       active_mics = 0;
-       for (i = 0; i < spec->num_dmics; i++) {
-               /* check the validity: sometimes it's a dead vendor-spec node */
-               if (get_wcaps_type(get_wcaps(codec, spec->dmic_nids[i]))
-                   != AC_WID_PIN)
-                       continue;
-               def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]);
-               if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
-                       active_mics++;
-       }
+       snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL);
 
        for (i = 0; i < spec->num_dmics; i++) {
                hda_nid_t nid;
-               int index;
+               int index, type_idx;
                const char *label;
 
                nid = spec->dmic_nids[i];
@@ -3482,28 +3472,23 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
                if (index < 0)
                        continue;
 
-               if (active_mics == 1)
-                       label = "Digital Mic";
-               else
-                       label = stac92xx_dmic_labels[dimux->num_items];
+               label = hda_get_input_pin_label(codec, nid, 1);
+               snd_hda_add_imux_item(dimux, label, index, &type_idx);
 
-               err = create_elem_capture_vol(codec, nid, label, HDA_INPUT);
+               err = create_elem_capture_vol(codec, nid, label, type_idx,
+                                             HDA_INPUT);
                if (err < 0)
                        return err;
                if (!err) {
                        err = create_elem_capture_vol(codec, nid, label,
-                                                     HDA_OUTPUT);
+                                                     type_idx, HDA_OUTPUT);
                        if (err < 0)
                                return err;
                }
 
-               dimux->items[dimux->num_items].label = label;
-               dimux->items[dimux->num_items].index = index;
-               dimux->num_items++;
                if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) {
-                       imux->items[imux->num_items].label = label;
-                       imux->items[imux->num_items].index = index;
-                       imux->num_items++;
+                       snd_hda_add_imux_item(imux, label, index, NULL);
+                       spec->num_analog_muxes++;
                }
        }
 
@@ -3511,20 +3496,27 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
 }
 
 static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid,
-                        hda_nid_t *fixed, hda_nid_t *ext)
+                        hda_nid_t *fixed, hda_nid_t *ext, hda_nid_t *dock)
 {
        unsigned int cfg;
 
        if (!nid)
                return 0;
        cfg = snd_hda_codec_get_pincfg(codec, nid);
-       switch (get_defcfg_connect(cfg)) {
-       case AC_JACK_PORT_FIXED:
+       switch (snd_hda_get_input_pin_attr(cfg)) {
+       case INPUT_PIN_ATTR_INT:
                if (*fixed)
                        return 1; /* already occupied */
                *fixed = nid;
                break;
-       case AC_JACK_PORT_COMPLEX:
+       case INPUT_PIN_ATTR_UNUSED:
+               break;
+       case INPUT_PIN_ATTR_DOCK:
+               if (*dock)
+                       return 1; /* already occupied */
+               *dock = nid;
+               break;
+       default:
                if (*ext)
                        return 1; /* already occupied */
                *ext = nid;
@@ -3542,10 +3534,13 @@ static int set_mic_route(struct hda_codec *codec,
        int i;
 
        mic->pin = pin;
-       for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
-               if (pin == cfg->input_pins[i])
+       if (pin == 0)
+               return 0;
+       for (i = 0; i < cfg->num_inputs; i++) {
+               if (pin == cfg->inputs[i].pin)
                        break;
-       if (i <= AUTO_PIN_FRONT_MIC) {
+       }
+       if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) {
                /* analog pin */
                i = get_connection_index(codec, spec->mux_nids[0], pin);
                if (i < 0)
@@ -3576,26 +3571,29 @@ static int stac_check_auto_mic(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
        struct auto_pin_cfg *cfg = &spec->autocfg;
-       hda_nid_t fixed, ext;
+       hda_nid_t fixed, ext, dock;
        int i;
 
-       for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) {
-               if (cfg->input_pins[i])
+       for (i = 0; i < cfg->num_inputs; i++) {
+               if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
                        return 0; /* must be exclusively mics */
        }
-       fixed = ext = 0;
-       for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
-               if (check_mic_pin(codec, cfg->input_pins[i], &fixed, &ext))
+       fixed = ext = dock = 0;
+       for (i = 0; i < cfg->num_inputs; i++)
+               if (check_mic_pin(codec, cfg->inputs[i].pin,
+                   &fixed, &ext, &dock))
                        return 0;
        for (i = 0; i < spec->num_dmics; i++)
-               if (check_mic_pin(codec, spec->dmic_nids[i], &fixed, &ext))
+               if (check_mic_pin(codec, spec->dmic_nids[i],
+                   &fixed, &ext, &dock))
                        return 0;
-       if (!fixed || !ext)
-               return 0;
+       if (!fixed && !ext && !dock)
+               return 0; /* no input to switch */
        if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
                return 0; /* no unsol support */
        if (set_mic_route(codec, &spec->ext_mic, ext) ||
-           set_mic_route(codec, &spec->int_mic, fixed))
+           set_mic_route(codec, &spec->int_mic, fixed) ||
+           set_mic_route(codec, &spec->dock_mic, dock))
                return 0; /* something is wrong */
        return 1;
 }
@@ -3606,13 +3604,12 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
        struct sigmatel_spec *spec = codec->spec;
        struct hda_input_mux *imux = &spec->private_imux;
        int i, j;
+       const char *label;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = cfg->input_pins[i];
-               int index, err;
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
+               int index, err, type_idx;
 
-               if (!nid)
-                       continue;
                index = -1;
                for (j = 0; j < spec->num_muxes; j++) {
                        index = get_connection_index(codec, spec->mux_nids[j],
@@ -3623,15 +3620,14 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
                if (index < 0)
                        continue;
 
+               label = hda_get_autocfg_input_label(codec, cfg, i);
+               snd_hda_add_imux_item(imux, label, index, &type_idx);
+
                err = create_elem_capture_vol(codec, nid,
-                                             auto_pin_cfg_labels[i],
+                                             label, type_idx,
                                              HDA_INPUT);
                if (err < 0)
                        return err;
-
-               imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
-               imux->items[imux->num_items].index = index;
-               imux->num_items++;
        }
        spec->num_analog_muxes = imux->num_items;
 
@@ -4305,38 +4301,38 @@ static int stac92xx_init(struct hda_codec *codec)
                                          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);
-       }
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = cfg->input_pins[i];
-               if (nid) {
-                       unsigned int pinctl, conf;
-                       if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
-                               /* for mic pins, force to initialize */
-                               pinctl = stac92xx_get_default_vref(codec, nid);
+               if (enable_pin_detect(codec, spec->dock_mic.pin,
+                   STAC_MIC_EVENT))
+                       stac_issue_unsol_event(codec, spec->dock_mic.pin);
+       }
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
+               int type = cfg->inputs[i].type;
+               unsigned int pinctl, conf;
+               if (type == AUTO_PIN_MIC) {
+                       /* for mic pins, force to initialize */
+                       pinctl = stac92xx_get_default_vref(codec, nid);
+                       pinctl |= AC_PINCTL_IN_EN;
+                       stac92xx_auto_set_pinctl(codec, nid, pinctl);
+               } else {
+                       pinctl = snd_hda_codec_read(codec, nid, 0,
+                                       AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+                       /* if PINCTL already set then skip */
+                       /* Also, if both INPUT and OUTPUT are set,
+                        * it must be a BIOS bug; need to override, too
+                        */
+                       if (!(pinctl & AC_PINCTL_IN_EN) ||
+                           (pinctl & AC_PINCTL_OUT_EN)) {
+                               pinctl &= ~AC_PINCTL_OUT_EN;
                                pinctl |= AC_PINCTL_IN_EN;
                                stac92xx_auto_set_pinctl(codec, nid, pinctl);
-                       } else {
-                               pinctl = snd_hda_codec_read(codec, nid, 0,
-                                       AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-                               /* if PINCTL already set then skip */
-                               /* Also, if both INPUT and OUTPUT are set,
-                                * it must be a BIOS bug; need to override, too
-                                */
-                               if (!(pinctl & AC_PINCTL_IN_EN) ||
-                                   (pinctl & AC_PINCTL_OUT_EN)) {
-                                       pinctl &= ~AC_PINCTL_OUT_EN;
-                                       pinctl |= AC_PINCTL_IN_EN;
-                                       stac92xx_auto_set_pinctl(codec, nid,
-                                                                pinctl);
-                               }
-                       }
-                       conf = snd_hda_codec_get_pincfg(codec, nid);
-                       if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
-                               if (enable_pin_detect(codec, nid,
-                                                     STAC_INSERT_EVENT))
-                                       stac_issue_unsol_event(codec, nid);
                        }
                }
+               conf = snd_hda_codec_get_pincfg(codec, nid);
+               if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
+                       if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT))
+                               stac_issue_unsol_event(codec, nid);
+               }
        }
        for (i = 0; i < spec->num_dmics; i++)
                stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
@@ -4383,11 +4379,9 @@ static int stac92xx_init(struct hda_codec *codec)
                        stac_issue_unsol_event(codec, nid);
        }
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
        /* sync mute LED */
-       if (spec->gpio_led && codec->patch_ops.check_power_status)
-               codec->patch_ops.check_power_status(codec, 0x01);
-#endif 
+       if (spec->gpio_led)
+               hda_call_check_power_status(codec, 0x01);
        if (spec->dac_list)
                stac92xx_power_down(codec);
        return 0;
@@ -4688,6 +4682,36 @@ static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
        }
 }
 
+/* get the pin connection (fixed, none, etc) */
+static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       unsigned int cfg;
+
+       cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
+       return get_defcfg_connect(cfg);
+}
+
+static int stac92xx_connected_ports(struct hda_codec *codec,
+                                        hda_nid_t *nids, int num_nids)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int idx, num;
+       unsigned int def_conf;
+
+       for (num = 0; num < num_nids; num++) {
+               for (idx = 0; idx < spec->num_pins; idx++)
+                       if (spec->pin_nids[idx] == nids[num])
+                               break;
+               if (idx >= spec->num_pins)
+                       break;
+               def_conf = stac_get_defcfg_connect(codec, idx);
+               if (def_conf == AC_JACK_PORT_NONE)
+                       break;
+       }
+       return num;
+}
+
 static void stac92xx_mic_detect(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -4695,6 +4719,8 @@ static void stac92xx_mic_detect(struct hda_codec *codec)
 
        if (get_pin_presence(codec, spec->ext_mic.pin))
                mic = &spec->ext_mic;
+       else if (get_pin_presence(codec, spec->dock_mic.pin))
+               mic = &spec->dock_mic;
        else
                mic = &spec->int_mic;
        if (mic->dmux_idx >= 0)
@@ -4937,11 +4963,9 @@ static int stac92xx_resume(struct hda_codec *codec)
                        stac_issue_unsol_event(codec,
                                               spec->autocfg.line_out_pins[0]);
        }
-#ifdef CONFIG_SND_HDA_POWER_SAVE
        /* sync mute LED */
-       if (spec->gpio_led && codec->patch_ops.check_power_status)
-               codec->patch_ops.check_power_status(codec, 0x01);
-#endif 
+       if (spec->gpio_led)
+               hda_call_check_power_status(codec, 0x01);
        return 0;
 }
 
@@ -5313,11 +5337,16 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
        if (spec == NULL)
                return -ENOMEM;
 
+       /* reset pin power-down; Windows may leave these bits after reboot */
+       snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7EC, 0);
+       snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7ED, 0);
        codec->no_trigger_sense = 1;
        codec->spec = spec;
        spec->linear_tone_beep = 1;
        codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
        spec->digbeep_nid = 0x21;
+       spec->dmic_nids = stac92hd83xxx_dmic_nids;
+       spec->dmux_nids = stac92hd83xxx_mux_nids;
        spec->mux_nids = stac92hd83xxx_mux_nids;
        spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids);
        spec->adc_nids = stac92hd83xxx_adc_nids;
@@ -5363,9 +5392,13 @@ again:
        case 0x111d76d4:
        case 0x111d7605:
        case 0x111d76d5:
+       case 0x111d76e7:
                if (spec->board_config == STAC_92HD83XXX_PWR_REF)
                        break;
                spec->num_pwrs = 0;
+               spec->num_dmics = stac92xx_connected_ports(codec,
+                               stac92hd83xxx_dmic_nids,
+                               STAC92HD83XXX_NUM_DMICS);
                break;
        }
 
@@ -5424,36 +5457,6 @@ again:
        return 0;
 }
 
-/* get the pin connection (fixed, none, etc) */
-static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
-{
-       struct sigmatel_spec *spec = codec->spec;
-       unsigned int cfg;
-
-       cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
-       return get_defcfg_connect(cfg);
-}
-
-static int stac92hd71bxx_connected_ports(struct hda_codec *codec,
-                                        hda_nid_t *nids, int num_nids)
-{
-       struct sigmatel_spec *spec = codec->spec;
-       int idx, num;
-       unsigned int def_conf;
-
-       for (num = 0; num < num_nids; num++) {
-               for (idx = 0; idx < spec->num_pins; idx++)
-                       if (spec->pin_nids[idx] == nids[num])
-                               break;
-               if (idx >= spec->num_pins)
-                       break;
-               def_conf = stac_get_defcfg_connect(codec, idx);
-               if (def_conf == AC_JACK_PORT_NONE)
-                       break;
-       }
-       return num;
-}
-
 static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec,
                                          hda_nid_t dig0pin)
 {
@@ -5592,7 +5595,7 @@ again:
        case 0x111d76b5:
                spec->init = stac92hd71bxx_core_init;
                codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
-               spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+               spec->num_dmics = stac92xx_connected_ports(codec,
                                        stac92hd71bxx_dmic_nids,
                                        STAC92HD71BXX_NUM_DMICS);
                break;
@@ -5624,7 +5627,7 @@ again:
                snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
                snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
                stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0;
-               spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+               spec->num_dmics = stac92xx_connected_ports(codec,
                                        stac92hd71bxx_dmic_nids,
                                        STAC92HD71BXX_NUM_DMICS - 1);
                break;
@@ -5638,7 +5641,7 @@ again:
        default:
                spec->init = stac92hd71bxx_core_init;
                codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
-               spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+               spec->num_dmics = stac92xx_connected_ports(codec,
                                        stac92hd71bxx_dmic_nids,
                                        STAC92HD71BXX_NUM_DMICS);
                break;
@@ -6320,6 +6323,8 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
        { .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
        { .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
        { .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
+       { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
+       { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
        {} /* terminator */
 };
 
index ae3acb2b42d11366a2d24bd768d823dbc61853b4..d1c3f8defc48d059f42124542d4e09b1fda000cf 100644 (file)
@@ -444,8 +444,8 @@ static hda_nid_t vt1812_adc_nids[2] = {
 
 
 /* add dynamic controls */
-static int via_add_control(struct via_spec *spec, int type, const char *name,
-                          unsigned long val)
+static int __via_add_control(struct via_spec *spec, int type, const char *name,
+                            int idx, unsigned long val)
 {
        struct snd_kcontrol_new *knew;
 
@@ -463,6 +463,9 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
        return 0;
 }
 
+#define via_add_control(spec, type, name, val) \
+       __via_add_control(spec, type, name, 0, val)
+
 static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
                                                struct snd_kcontrol_new *tmpl)
 {
@@ -494,18 +497,18 @@ static void via_free_kctls(struct hda_codec *codec)
 
 /* create input playback/capture controls for the given pin */
 static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
-                               int idx, int mix_nid)
+                               int type_idx, int idx, int mix_nid)
 {
        char name[32];
        int err;
 
        sprintf(name, "%s Playback Volume", ctlname);
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+       err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
                              HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
        sprintf(name, "%s Playback Switch", ctlname);
-       err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
+       err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
                              HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
@@ -557,17 +560,15 @@ static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
 static void via_auto_init_analog_input(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
        unsigned int ctl;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
-               if (!nid)
-                       continue;
-
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (spec->smart51_enabled && is_smart51_pins(spec, nid))
                        ctl = PIN_OUT;
-               else if (i <= AUTO_PIN_FRONT_MIC)
+               else if (i == AUTO_PIN_MIC)
                        ctl = PIN_VREF50;
                else
                        ctl = PIN_IN;
@@ -1322,15 +1323,14 @@ static void mute_aa_path(struct hda_codec *codec, int mute)
 }
 static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
 {
-       int res = 0;
-       int index;
-       for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
-               if (pin == spec->autocfg.input_pins[index]) {
-                       res = 1;
-                       break;
-               }
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
+
+       for (i = 0; i < cfg->num_inputs; i++) {
+               if (pin == cfg->inputs[i].pin)
+                       return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
        }
-       return res;
+       return 0;
 }
 
 static int via_smart51_info(struct snd_kcontrol *kcontrol,
@@ -1348,25 +1348,21 @@ static int via_smart51_get(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct via_spec *spec = codec->spec;
-       int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
        int on = 1;
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(index); i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[index[i]];
-               if (nid) {
-                       int ctl =
-                           snd_hda_codec_read(codec, nid, 0,
-                                              AC_VERB_GET_PIN_WIDGET_CONTROL,
-                                              0);
-                       if (i == AUTO_PIN_FRONT_MIC
-                           && spec->hp_independent_mode
-                           && spec->codec_type != VT1718S)
-                               continue; /* ignore FMic for independent HP */
-                       if (ctl & AC_PINCTL_IN_EN
-                           && !(ctl & AC_PINCTL_OUT_EN))
-                               on = 0;
-               }
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
+               int ctl = snd_hda_codec_read(codec, nid, 0,
+                                            AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+               if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+                       continue;
+               if (cfg->inputs[i].type == AUTO_PIN_MIC &&
+                   spec->hp_independent_mode && spec->codec_type != VT1718S)
+                       continue; /* ignore FMic for independent HP */
+               if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
+                       on = 0;
        }
        *ucontrol->value.integer.value = on;
        return 0;
@@ -1377,36 +1373,38 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct via_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
        int out_in = *ucontrol->value.integer.value
                ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
-       int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(index); i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[index[i]];
-               if (i == AUTO_PIN_FRONT_MIC
-                   && spec->hp_independent_mode
-                   && spec->codec_type != VT1718S)
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
+               unsigned int parm;
+
+               if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+                       continue;
+               if (cfg->inputs[i].type == AUTO_PIN_MIC &&
+                   spec->hp_independent_mode && spec->codec_type != VT1718S)
                        continue; /* don't retask FMic for independent HP */
-               if (nid) {
-                       unsigned int parm = snd_hda_codec_read(
-                               codec, nid, 0,
-                               AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-                       parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
-                       parm |= out_in;
-                       snd_hda_codec_write(codec, nid, 0,
-                                           AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                           parm);
-                       if (out_in == AC_PINCTL_OUT_EN) {
-                               mute_aa_path(codec, 1);
-                               notify_aa_path_ctls(codec);
-                       }
-                       if (spec->codec_type == VT1718S)
-                               snd_hda_codec_amp_stereo(
+
+               parm = snd_hda_codec_read(codec, nid, 0,
+                                         AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+               parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
+               parm |= out_in;
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   parm);
+               if (out_in == AC_PINCTL_OUT_EN) {
+                       mute_aa_path(codec, 1);
+                       notify_aa_path_ctls(codec);
+               }
+               if (spec->codec_type == VT1718S) {
+                       snd_hda_codec_amp_stereo(
                                        codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
                                        HDA_AMP_UNMUTE);
                }
-               if (i == AUTO_PIN_FRONT_MIC) {
+               if (cfg->inputs[i].type == AUTO_PIN_MIC) {
                        if (spec->codec_type == VT1708S
                            || spec->codec_type == VT1716S) {
                                /* input = index 1 (AOW3) */
@@ -1442,7 +1440,7 @@ static struct snd_kcontrol_new via_smart51_mixer[2] = {
 static int via_smart51_build(struct via_spec *spec)
 {
        struct snd_kcontrol_new *knew;
-       int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
        hda_nid_t nid;
        int i;
 
@@ -1450,13 +1448,14 @@ static int via_smart51_build(struct via_spec *spec)
        if (knew == NULL)
                return -ENOMEM;
 
-       for (i = 0; i < ARRAY_SIZE(index); i++) {
-               nid = spec->autocfg.input_pins[index[i]];
-               if (nid) {
+       for (i = 0; i < cfg->num_inputs; i++) {
+               nid = cfg->inputs[i].pin;
+               if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
                        knew = via_clone_control(spec, &via_smart51_mixer[1]);
                        if (knew == NULL)
                                return -ENOMEM;
                        knew->subdevice = nid;
+                       break;
                }
        }
 
@@ -2375,13 +2374,8 @@ static void create_hp_imux(struct via_spec *spec)
        static const char *texts[] = { "OFF", "ON", NULL};
 
        /* for hp mode select */
-       i = 0;
-       while (texts[i] != NULL) {
-               imux->items[imux->num_items].label =  texts[i];
-               imux->items[imux->num_items].index = i;
-               imux->num_items++;
-               i++;
-       }
+       for (i = 0; texts[i]; i++)
+               snd_hda_add_imux_item(imux, texts[i], i, NULL);
 
        spec->hp_mux = &spec->private_imux[1];
 }
@@ -2413,51 +2407,53 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
-                                               const struct auto_pin_cfg *cfg)
+static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
+                                           const struct auto_pin_cfg *cfg,
+                                           hda_nid_t cap_nid,
+                                           hda_nid_t pin_idxs[], int num_idxs)
 {
-       static char *labels[] = {
-               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-       };
+       struct via_spec *spec = codec->spec;
        struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx = 0;
+       int i, err, idx, type, type_idx = 0;
 
        /* for internal loopback recording select */
-       imux->items[imux->num_items].label = "Stereo Mixer";
-       imux->items[imux->num_items].index = idx;
-       imux->num_items++;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (!cfg->input_pins[i])
-                       continue;
-
-               switch (cfg->input_pins[i]) {
-               case 0x1d: /* Mic */
-                       idx = 2;
-                       break;
-
-               case 0x1e: /* Line In */
-                       idx = 3;
-                       break;
-
-               case 0x21: /* Front Mic */
-                       idx = 4;
-                       break;
-
-               case 0x24: /* CD */
-                       idx = 1;
+       for (idx = 0; idx < num_idxs; idx++) {
+               if (pin_idxs[idx] == 0xff) {
+                       snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
                        break;
                }
-               err = via_new_analog_input(spec, labels[i], idx, 0x17);
+       }
+
+       for (i = 0; i < cfg->num_inputs; i++) {
+               const char *label;
+               type = cfg->inputs[i].type;
+               for (idx = 0; idx < num_idxs; idx++)
+                       if (pin_idxs[idx] == cfg->inputs[i].pin)
+                               break;
+               if (idx >= num_idxs)
+                       continue;
+               if (i > 0 && type == cfg->inputs[i - 1].type)
+                       type_idx++;
+               else
+                       type_idx = 0;
+               label = hda_get_autocfg_input_label(codec, cfg, i);
+               err = via_new_analog_input(spec, label, type_idx, idx, cap_nid);
                if (err < 0)
                        return err;
-               imux->items[imux->num_items].label = labels[i];
-               imux->items[imux->num_items].index = idx;
-               imux->num_items++;
+               snd_hda_add_imux_item(imux, label, idx, NULL);
        }
        return 0;
 }
 
+/* create playback/capture controls for input pins */
+static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
+                                               const struct auto_pin_cfg *cfg)
+{
+       static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 };
+       return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs,
+                                               ARRAY_SIZE(pin_idxs));
+}
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static struct hda_amp_list vt1708_loopbacks[] = {
        { 0x17, HDA_INPUT, 1 },
@@ -2554,7 +2550,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
        err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
        if (err < 0)
                return err;
-       err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
        /* add jack detect on/off control */
@@ -3021,49 +3017,12 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
                                                const struct auto_pin_cfg *cfg)
 {
-       static char *labels[] = {
-               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-       };
-       struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx = 0;
-
-       /* for internal loopback recording select */
-       imux->items[imux->num_items].label = "Stereo Mixer";
-       imux->items[imux->num_items].index = idx;
-       imux->num_items++;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (!cfg->input_pins[i])
-                       continue;
-
-               switch (cfg->input_pins[i]) {
-               case 0x1d: /* Mic */
-                       idx = 2;
-                       break;
-
-               case 0x1e: /* Line In */
-                       idx = 3;
-                       break;
-
-               case 0x21: /* Front Mic */
-                       idx = 4;
-                       break;
-
-               case 0x23: /* CD */
-                       idx = 1;
-                       break;
-               }
-               err = via_new_analog_input(spec, labels[i], idx, 0x18);
-               if (err < 0)
-                       return err;
-               imux->items[imux->num_items].label = labels[i];
-               imux->items[imux->num_items].index = idx;
-               imux->num_items++;
-       }
-       return 0;
+       static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
+       return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
+                                               ARRAY_SIZE(pin_idxs));
 }
 
 static int vt1709_parse_auto_config(struct hda_codec *codec)
@@ -3086,7 +3045,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
        err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
        if (err < 0)
                return err;
-       err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
 
@@ -3588,49 +3547,12 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
                                                const struct auto_pin_cfg *cfg)
 {
-       static char *labels[] = {
-               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-       };
-       struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx = 0;
-
-       /* for internal loopback recording select */
-       imux->items[imux->num_items].label = "Stereo Mixer";
-       imux->items[imux->num_items].index = idx;
-       imux->num_items++;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (!cfg->input_pins[i])
-                       continue;
-
-               switch (cfg->input_pins[i]) {
-               case 0x1a: /* Mic */
-                       idx = 2;
-                       break;
-
-               case 0x1b: /* Line In */
-                       idx = 3;
-                       break;
-
-               case 0x1e: /* Front Mic */
-                       idx = 4;
-                       break;
-
-               case 0x1f: /* CD */
-                       idx = 1;
-                       break;
-               }
-               err = via_new_analog_input(spec, labels[i], idx, 0x16);
-               if (err < 0)
-                       return err;
-               imux->items[imux->num_items].label = labels[i];
-               imux->items[imux->num_items].index = idx;
-               imux->num_items++;
-       }
-       return 0;
+       static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
+       return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
+                                               ARRAY_SIZE(pin_idxs));
 }
 
 static int vt1708B_parse_auto_config(struct hda_codec *codec)
@@ -3653,7 +3575,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
        err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
        if (err < 0)
                return err;
-       err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
 
@@ -4061,49 +3983,12 @@ static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
                                                const struct auto_pin_cfg *cfg)
 {
-       static char *labels[] = {
-               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-       };
-       struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx = 0;
-
-       /* for internal loopback recording select */
-       imux->items[imux->num_items].label = "Stereo Mixer";
-       imux->items[imux->num_items].index = 5;
-       imux->num_items++;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (!cfg->input_pins[i])
-                       continue;
-
-               switch (cfg->input_pins[i]) {
-               case 0x1a: /* Mic */
-                       idx = 2;
-                       break;
-
-               case 0x1b: /* Line In */
-                       idx = 3;
-                       break;
-
-               case 0x1e: /* Front Mic */
-                       idx = 4;
-                       break;
-
-               case 0x1f: /* CD */
-                       idx = 1;
-                       break;
-               }
-               err = via_new_analog_input(spec, labels[i], idx, 0x16);
-               if (err < 0)
-                       return err;
-               imux->items[imux->num_items].label = labels[i];
-               imux->items[imux->num_items].index = idx-1;
-               imux->num_items++;
-       }
-       return 0;
+       static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
+       return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
+                                               ARRAY_SIZE(pin_idxs));
 }
 
 /* fill out digital output widgets; one for master and one for slave outputs */
@@ -4151,7 +4036,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
        err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
        if (err < 0)
                return err;
-       err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
 
@@ -4441,58 +4326,20 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
        imux = &spec->private_imux[1];
 
        /* for hp mode select */
-       i = 0;
-       while (texts[i] != NULL)        {
-               imux->items[imux->num_items].label =  texts[i];
-               imux->items[imux->num_items].index = i;
-  &nbs