]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'topic/hda' into for-linus
authorTakashi Iwai <tiwai@suse.de>
Mon, 21 May 2012 10:51:31 +0000 (12:51 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 21 May 2012 10:51:31 +0000 (12:51 +0200)
19 files changed:
sound/pci/hda/Makefile
sound/pci/hda/hda_auto_parser.c [new file with mode: 0644]
sound/pci/hda/hda_auto_parser.h [new file with mode: 0644]
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_jack.c
sound/pci/hda/hda_jack.h
sound/pci/hda/hda_local.h
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_ca0110.c
sound/pci/hda/patch_ca0132.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_cmedia.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c

index ace157cc3d15c7e5b560910ca1adcfb2162d093a..bd4149f1aaf45f1f1300563c4aa469d483c2530f 100644 (file)
@@ -1,6 +1,6 @@
 snd-hda-intel-objs := hda_intel.o
 
-snd-hda-codec-y := hda_codec.o hda_jack.o
+snd-hda-codec-y := hda_codec.o hda_jack.o hda_auto_parser.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_HWDEP) += hda_hwdep.o
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
new file mode 100644 (file)
index 0000000..6e9ef3e
--- /dev/null
@@ -0,0 +1,760 @@
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+
+#define SFX    "hda_codec: "
+
+/*
+ * Helper for automatic pin configuration
+ */
+
+static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list)
+{
+       for (; *list; list++)
+               if (*list == nid)
+                       return 1;
+       return 0;
+}
+
+
+/*
+ * Sort an associated group of pins according to their sequence numbers.
+ */
+static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
+                                 int num_pins)
+{
+       int i, j;
+       short seq;
+       hda_nid_t nid;
+
+       for (i = 0; i < num_pins; i++) {
+               for (j = i + 1; j < num_pins; j++) {
+                       if (sequences[i] > sequences[j]) {
+                               seq = sequences[i];
+                               sequences[i] = sequences[j];
+                               sequences[j] = seq;
+                               nid = pins[i];
+                               pins[i] = pins[j];
+                               pins[j] = nid;
+                       }
+               }
+       }
+}
+
+
+/* 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;
+                       }
+               }
+       }
+}
+
+/* Reorder the surround channels
+ * ALSA sequence is front/surr/clfe/side
+ * HDA sequence is:
+ *    4-ch: front/surr  =>  OK as it is
+ *    6-ch: front/clfe/surr
+ *    8-ch: front/clfe/rear/side|fc
+ */
+static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
+{
+       hda_nid_t nid;
+
+       switch (nums) {
+       case 3:
+       case 4:
+               nid = pins[1];
+               pins[1] = pins[2];
+               pins[2] = nid;
+               break;
+       }
+}
+
+/*
+ * Parse all pin widgets and store the useful pin nids to cfg
+ *
+ * The number of line-outs or any primary output is stored in line_outs,
+ * and the corresponding output pins are assigned to line_out_pins[],
+ * in the order of front, rear, CLFE, side, ...
+ *
+ * If more extra outputs (speaker and headphone) are found, the pins are
+ * assisnged to hp_pins[] and speaker_pins[], respectively.  If no line-out jack
+ * is detected, one of speaker of HP pins is assigned as the primary
+ * 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 inputs array.
+ * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
+ * respectively.
+ */
+int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
+                            struct auto_pin_cfg *cfg,
+                            const hda_nid_t *ignore_nids,
+                            unsigned int cond_flags)
+{
+       hda_nid_t nid, end_nid;
+       short seq, assoc_line_out;
+       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));
+
+       memset(sequences_line_out, 0, sizeof(sequences_line_out));
+       memset(sequences_speaker, 0, sizeof(sequences_speaker));
+       memset(sequences_hp, 0, sizeof(sequences_hp));
+       assoc_line_out = 0;
+
+       codec->ignore_misc_bit = true;
+       end_nid = codec->start_nid + codec->num_nodes;
+       for (nid = codec->start_nid; nid < end_nid; nid++) {
+               unsigned int wid_caps = get_wcaps(codec, nid);
+               unsigned int wid_type = get_wcaps_type(wid_caps);
+               unsigned int def_conf;
+               short assoc, loc, conn, dev;
+
+               /* read all default configuration for pin complex */
+               if (wid_type != AC_WID_PIN)
+                       continue;
+               /* ignore the given nids (e.g. pc-beep returns error) */
+               if (ignore_nids && is_in_nid_list(nid, ignore_nids))
+                       continue;
+
+               def_conf = snd_hda_codec_get_pincfg(codec, nid);
+               if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
+                     AC_DEFCFG_MISC_NO_PRESENCE))
+                       codec->ignore_misc_bit = false;
+               conn = get_defcfg_connect(def_conf);
+               if (conn == AC_JACK_PORT_NONE)
+                       continue;
+               loc = get_defcfg_location(def_conf);
+               dev = get_defcfg_device(def_conf);
+
+               /* workaround for buggy BIOS setups */
+               if (dev == AC_JACK_LINE_OUT) {
+                       if (conn == AC_JACK_PORT_FIXED)
+                               dev = AC_JACK_SPEAKER;
+               }
+
+               switch (dev) {
+               case AC_JACK_LINE_OUT:
+                       seq = get_defcfg_sequence(def_conf);
+                       assoc = get_defcfg_association(def_conf);
+
+                       if (!(wid_caps & AC_WCAP_STEREO))
+                               if (!cfg->mono_out_pin)
+                                       cfg->mono_out_pin = nid;
+                       if (!assoc)
+                               continue;
+                       if (!assoc_line_out)
+                               assoc_line_out = assoc;
+                       else if (assoc_line_out != assoc)
+                               continue;
+                       if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))
+                               continue;
+                       cfg->line_out_pins[cfg->line_outs] = nid;
+                       sequences_line_out[cfg->line_outs] = seq;
+                       cfg->line_outs++;
+                       break;
+               case AC_JACK_SPEAKER:
+                       seq = get_defcfg_sequence(def_conf);
+                       assoc = get_defcfg_association(def_conf);
+                       if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
+                               continue;
+                       cfg->speaker_pins[cfg->speaker_outs] = nid;
+                       sequences_speaker[cfg->speaker_outs] = (assoc << 4) | seq;
+                       cfg->speaker_outs++;
+                       break;
+               case AC_JACK_HP_OUT:
+                       seq = get_defcfg_sequence(def_conf);
+                       assoc = get_defcfg_association(def_conf);
+                       if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
+                               continue;
+                       cfg->hp_pins[cfg->hp_outs] = nid;
+                       sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
+                       cfg->hp_outs++;
+                       break;
+               case AC_JACK_MIC_IN:
+                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
+                       break;
+               case AC_JACK_LINE_IN:
+                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
+                       break;
+               case AC_JACK_CD:
+                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
+                       break;
+               case AC_JACK_AUX:
+                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
+                       break;
+               case AC_JACK_SPDIF_OUT:
+               case AC_JACK_DIG_OTHER_OUT:
+                       if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
+                               continue;
+                       cfg->dig_out_pins[cfg->dig_outs] = nid;
+                       cfg->dig_out_type[cfg->dig_outs] =
+                               (loc == AC_JACK_LOC_HDMI) ?
+                               HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
+                       cfg->dig_outs++;
+                       break;
+               case AC_JACK_SPDIF_IN:
+               case AC_JACK_DIG_OTHER_IN:
+                       cfg->dig_in_pin = nid;
+                       if (loc == AC_JACK_LOC_HDMI)
+                               cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
+                       else
+                               cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+                       break;
+               }
+       }
+
+       /* FIX-UP:
+        * If no line-out is defined but multiple HPs are found,
+        * some of them might be the real line-outs.
+        */
+       if (!cfg->line_outs && cfg->hp_outs > 1 &&
+           !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) {
+               int i = 0;
+               while (i < cfg->hp_outs) {
+                       /* The real HPs should have the sequence 0x0f */
+                       if ((sequences_hp[i] & 0x0f) == 0x0f) {
+                               i++;
+                               continue;
+                       }
+                       /* Move it to the line-out table */
+                       cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i];
+                       sequences_line_out[cfg->line_outs] = sequences_hp[i];
+                       cfg->line_outs++;
+                       cfg->hp_outs--;
+                       memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
+                               sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
+                       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));
+               if (!cfg->hp_outs)
+                       cfg->line_out_type = AUTO_PIN_HP_OUT;
+
+       }
+
+       /* sort by sequence */
+       sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
+                             cfg->line_outs);
+       sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
+                             cfg->speaker_outs);
+       sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
+                             cfg->hp_outs);
+
+       /*
+        * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
+        * as a primary output
+        */
+       if (!cfg->line_outs &&
+           !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) {
+               if (cfg->speaker_outs) {
+                       cfg->line_outs = cfg->speaker_outs;
+                       memcpy(cfg->line_out_pins, cfg->speaker_pins,
+                              sizeof(cfg->speaker_pins));
+                       cfg->speaker_outs = 0;
+                       memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+                       cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
+               } else if (cfg->hp_outs) {
+                       cfg->line_outs = cfg->hp_outs;
+                       memcpy(cfg->line_out_pins, cfg->hp_pins,
+                              sizeof(cfg->hp_pins));
+                       cfg->hp_outs = 0;
+                       memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+                       cfg->line_out_type = AUTO_PIN_HP_OUT;
+               }
+       }
+
+       reorder_outputs(cfg->line_outs, cfg->line_out_pins);
+       reorder_outputs(cfg->hp_outs, cfg->hp_pins);
+       reorder_outputs(cfg->speaker_outs, cfg->speaker_pins);
+
+       sort_autocfg_input_pins(cfg);
+
+       /*
+        * debug prints of the parsed results
+        */
+       snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
+                  cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
+                  cfg->line_out_pins[2], cfg->line_out_pins[3],
+                  cfg->line_out_pins[4],
+                  cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" :
+                  (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ?
+                   "speaker" : "line"));
+       snd_printd("   speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+                  cfg->speaker_outs, cfg->speaker_pins[0],
+                  cfg->speaker_pins[1], cfg->speaker_pins[2],
+                  cfg->speaker_pins[3], cfg->speaker_pins[4]);
+       snd_printd("   hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+                  cfg->hp_outs, cfg->hp_pins[0],
+                  cfg->hp_pins[1], cfg->hp_pins[2],
+                  cfg->hp_pins[3], cfg->hp_pins[4]);
+       snd_printd("   mono: mono_out=0x%x\n", cfg->mono_out_pin);
+       if (cfg->dig_outs)
+               snd_printd("   dig-out=0x%x/0x%x\n",
+                          cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
+       snd_printd("   inputs:");
+       for (i = 0; i < cfg->num_inputs; i++) {
+               snd_printd(" %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);
+
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_parse_pin_defcfg);
+
+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".
+ */
+
+static const char *hda_get_input_pin_label(struct hda_codec *codec,
+                                          hda_nid_t pin, bool check_location)
+{
+       unsigned int def_conf;
+       static const char * const 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";
+       }
+}
+
+/* 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);
+
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+       int i;
+       for (i = 0; i < nums; i++)
+               if (list[i] == nid)
+                       return i;
+       return -1;
+}
+
+/* get a unique suffix or an index number */
+static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins,
+                                   int num_pins, int *indexp)
+{
+       static const char * const channel_sfx[] = {
+               " Front", " Surround", " CLFE", " Side"
+       };
+       int i;
+
+       i = find_idx_in_nid_list(nid, pins, num_pins);
+       if (i < 0)
+               return NULL;
+       if (num_pins == 1)
+               return "";
+       if (num_pins > ARRAY_SIZE(channel_sfx)) {
+               if (indexp)
+                       *indexp = i;
+               return "";
+       }
+       return channel_sfx[i];
+}
+
+static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
+                              const struct auto_pin_cfg *cfg,
+                              const char *name, char *label, int maxlen,
+                              int *indexp)
+{
+       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+       int attr = snd_hda_get_input_pin_attr(def_conf);
+       const char *pfx = "", *sfx = "";
+
+       /* handle as a speaker if it's a fixed line-out */
+       if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT)
+               name = "Speaker";
+       /* check the location */
+       switch (attr) {
+       case INPUT_PIN_ATTR_DOCK:
+               pfx = "Dock ";
+               break;
+       case INPUT_PIN_ATTR_FRONT:
+               pfx = "Front ";
+               break;
+       }
+       if (cfg) {
+               /* try to give a unique suffix if needed */
+               sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs,
+                                      indexp);
+               if (!sfx)
+                       sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs,
+                                              indexp);
+               if (!sfx) {
+                       /* don't add channel suffix for Headphone controls */
+                       int idx = find_idx_in_nid_list(nid, cfg->hp_pins,
+                                                      cfg->hp_outs);
+                       if (idx >= 0)
+                               *indexp = idx;
+                       sfx = "";
+               }
+       }
+       snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
+       return 1;
+}
+
+/**
+ * snd_hda_get_pin_label - Get a label for the given I/O pin
+ *
+ * Get a label for the given pin.  This function works for both input and
+ * output pins.  When @cfg is given as non-NULL, the function tries to get
+ * an optimized label using hda_get_autocfg_input_label().
+ *
+ * This function tries to give a unique label string for the pin as much as
+ * possible.  For example, when the multiple line-outs are present, it adds
+ * the channel suffix like "Front", "Surround", etc (only when @cfg is given).
+ * If no unique name with a suffix is available and @indexp is non-NULL, the
+ * index number is stored in the pointer.
+ */
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+                         const struct auto_pin_cfg *cfg,
+                         char *label, int maxlen, int *indexp)
+{
+       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+       const char *name = NULL;
+       int i;
+
+       if (indexp)
+               *indexp = 0;
+       if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+               return 0;
+
+       switch (get_defcfg_device(def_conf)) {
+       case AC_JACK_LINE_OUT:
+               return fill_audio_out_name(codec, nid, cfg, "Line Out",
+                                          label, maxlen, indexp);
+       case AC_JACK_SPEAKER:
+               return fill_audio_out_name(codec, nid, cfg, "Speaker",
+                                          label, maxlen, indexp);
+       case AC_JACK_HP_OUT:
+               return fill_audio_out_name(codec, nid, cfg, "Headphone",
+                                          label, maxlen, indexp);
+       case AC_JACK_SPDIF_OUT:
+       case AC_JACK_DIG_OTHER_OUT:
+               if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI)
+                       name = "HDMI";
+               else
+                       name = "SPDIF";
+               if (cfg && indexp) {
+                       i = find_idx_in_nid_list(nid, cfg->dig_out_pins,
+                                                cfg->dig_outs);
+                       if (i >= 0)
+                               *indexp = i;
+               }
+               break;
+       default:
+               if (cfg) {
+                       for (i = 0; i < cfg->num_inputs; i++) {
+                               if (cfg->inputs[i].pin != nid)
+                                       continue;
+                               name = hda_get_autocfg_input_label(codec, cfg, i);
+                               if (name)
+                                       break;
+                       }
+               }
+               if (!name)
+                       name = hda_get_input_pin_label(codec, nid, true);
+               break;
+       }
+       if (!name)
+               return 0;
+       strlcpy(label, name, maxlen);
+       return 1;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_pin_label);
+
+int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
+                         const struct hda_verb *list)
+{
+       const struct hda_verb **v;
+       snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8);
+       v = snd_array_new(&spec->verbs);
+       if (!v)
+               return -ENOMEM;
+       *v = list;
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs);
+
+void snd_hda_gen_apply_verbs(struct hda_codec *codec)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       int i;
+       for (i = 0; i < spec->verbs.used; i++) {
+               struct hda_verb **v = snd_array_elem(&spec->verbs, i);
+               snd_hda_sequence_write(codec, *v);
+       }
+}
+EXPORT_SYMBOL_HDA(snd_hda_gen_apply_verbs);
+
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+                          const struct hda_pintbl *cfg)
+{
+       for (; cfg->nid; cfg++)
+               snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
+}
+EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs);
+
+void snd_hda_apply_fixup(struct hda_codec *codec, int action)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       int id = spec->fixup_id;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       const char *modelname = spec->fixup_name;
+#endif
+       int depth = 0;
+
+       if (!spec->fixup_list)
+               return;
+
+       while (id >= 0) {
+               const struct hda_fixup *fix = spec->fixup_list + id;
+
+               switch (fix->type) {
+               case HDA_FIXUP_PINS:
+                       if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins)
+                               break;
+                       snd_printdd(KERN_INFO SFX
+                                   "%s: Apply pincfg for %s\n",
+                                   codec->chip_name, modelname);
+                       snd_hda_apply_pincfgs(codec, fix->v.pins);
+                       break;
+               case HDA_FIXUP_VERBS:
+                       if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs)
+                               break;
+                       snd_printdd(KERN_INFO SFX
+                                   "%s: Apply fix-verbs for %s\n",
+                                   codec->chip_name, modelname);
+                       snd_hda_gen_add_verbs(codec->spec, fix->v.verbs);
+                       break;
+               case HDA_FIXUP_FUNC:
+                       if (!fix->v.func)
+                               break;
+                       snd_printdd(KERN_INFO SFX
+                                   "%s: Apply fix-func for %s\n",
+                                   codec->chip_name, modelname);
+                       fix->v.func(codec, fix, action);
+                       break;
+               default:
+                       snd_printk(KERN_ERR SFX
+                                  "%s: Invalid fixup type %d\n",
+                                  codec->chip_name, fix->type);
+                       break;
+               }
+               if (!fix->chained)
+                       break;
+               if (++depth > 10)
+                       break;
+               id = fix->chain_id;
+       }
+}
+EXPORT_SYMBOL_HDA(snd_hda_apply_fixup);
+
+void snd_hda_pick_fixup(struct hda_codec *codec,
+                       const struct hda_model_fixup *models,
+                       const struct snd_pci_quirk *quirk,
+                       const struct hda_fixup *fixlist)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       const struct snd_pci_quirk *q;
+       int id = -1;
+       const char *name = NULL;
+
+       /* when model=nofixup is given, don't pick up any fixups */
+       if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
+               spec->fixup_list = NULL;
+               spec->fixup_id = -1;
+               return;
+       }
+
+       if (codec->modelname && models) {
+               while (models->name) {
+                       if (!strcmp(codec->modelname, models->name)) {
+                               id = models->id;
+                               name = models->name;
+                               break;
+                       }
+                       models++;
+               }
+       }
+       if (id < 0) {
+               q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
+               if (q) {
+                       id = q->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+                       name = q->name;
+#endif
+               }
+       }
+       if (id < 0) {
+               for (q = quirk; q->subvendor; q++) {
+                       unsigned int vendorid =
+                               q->subdevice | (q->subvendor << 16);
+                       if (vendorid == codec->subsystem_id) {
+                               id = q->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+                               name = q->name;
+#endif
+                               break;
+                       }
+               }
+       }
+
+       spec->fixup_id = id;
+       if (id >= 0) {
+               spec->fixup_list = fixlist;
+               spec->fixup_name = name;
+       }
+}
+EXPORT_SYMBOL_HDA(snd_hda_pick_fixup);
diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h
new file mode 100644 (file)
index 0000000..2a7889d
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SOUND_HDA_AUTO_PARSER_H
+#define __SOUND_HDA_AUTO_PARSER_H
+
+/*
+ * Helper for automatic pin configuration
+ */
+
+enum {
+       AUTO_PIN_MIC,
+       AUTO_PIN_LINE_IN,
+       AUTO_PIN_CD,
+       AUTO_PIN_AUX,
+       AUTO_PIN_LAST
+};
+
+enum {
+       AUTO_PIN_LINE_OUT,
+       AUTO_PIN_SPEAKER_OUT,
+       AUTO_PIN_HP_OUT
+};
+
+#define AUTO_CFG_MAX_OUTS      HDA_MAX_OUTS
+#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_autocfg_input_label(struct hda_codec *codec,
+                                       const struct auto_pin_cfg *cfg,
+                                       int input);
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+                         const struct auto_pin_cfg *cfg,
+                         char *label, int maxlen, int *indexp);
+
+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;
+       /* sorted in the order of Front/Surr/CLFE/Side */
+       hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
+       int speaker_outs;
+       hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
+       int hp_outs;
+       int line_out_type;      /* AUTO_PIN_XXX_OUT */
+       hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
+       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;
+       hda_nid_t mono_out_pin;
+       int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
+       int dig_in_type; /* HDA_PCM_TYPE_XXX */
+};
+
+/* bit-flags for snd_hda_parse_pin_def_config() behavior */
+#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */
+#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */
+
+int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
+                            struct auto_pin_cfg *cfg,
+                            const hda_nid_t *ignore_nids,
+                            unsigned int cond_flags);
+
+/* older function */
+#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
+       snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
+
+/*
+ */
+
+struct hda_gen_spec {
+       /* fix-up list */
+       int fixup_id;
+       const struct hda_fixup *fixup_list;
+       const char *fixup_name;
+
+       /* additional init verbs */
+       struct snd_array verbs;
+};
+
+
+/*
+ * Fix-up pin default configurations and add default verbs
+ */
+
+struct hda_pintbl {
+       hda_nid_t nid;
+       u32 val;
+};
+
+struct hda_model_fixup {
+       const int id;
+       const char *name;
+};
+
+struct hda_fixup {
+       int type;
+       bool chained;
+       int chain_id;
+       union {
+               const struct hda_pintbl *pins;
+               const struct hda_verb *verbs;
+               void (*func)(struct hda_codec *codec,
+                            const struct hda_fixup *fix,
+                            int action);
+       } v;
+};
+
+/* fixup types */
+enum {
+       HDA_FIXUP_INVALID,
+       HDA_FIXUP_PINS,
+       HDA_FIXUP_VERBS,
+       HDA_FIXUP_FUNC,
+};
+
+/* fixup action definitions */
+enum {
+       HDA_FIXUP_ACT_PRE_PROBE,
+       HDA_FIXUP_ACT_PROBE,
+       HDA_FIXUP_ACT_INIT,
+       HDA_FIXUP_ACT_BUILD,
+};
+
+int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
+                         const struct hda_verb *list);
+void snd_hda_gen_apply_verbs(struct hda_codec *codec);
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+                          const struct hda_pintbl *cfg);
+void snd_hda_apply_fixup(struct hda_codec *codec, int action);
+void snd_hda_pick_fixup(struct hda_codec *codec,
+                       const struct hda_model_fixup *models,
+                       const struct snd_pci_quirk *quirk,
+                       const struct hda_fixup *fixlist);
+
+#endif /* __SOUND_HDA_AUTO_PARSER_H */
index 841475cc13b657a2627e107354e4b70251fb3296..eb09a3348325358b0a741d3408d48cc4e5e8fffd 100644 (file)
@@ -334,78 +334,67 @@ static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid)
        return NULL;
 }
 
+/* read the connection and add to the cache */
+static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
+{
+       hda_nid_t list[HDA_MAX_CONNECTIONS];
+       int len;
+
+       len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list));
+       if (len < 0)
+               return len;
+       return snd_hda_override_conn_list(codec, nid, len, list);
+}
+
 /**
- * snd_hda_get_conn_list - get connection list
+ * snd_hda_get_connections - copy connection list
  * @codec: the HDA codec
  * @nid: NID to parse
- * @listp: the pointer to store NID list
+ * @conn_list: connection list array; when NULL, checks only the size
+ * @max_conns: max. number of connections to store
  *
  * Parses the connection list of the given widget and stores the list
  * of NIDs.
  *
  * Returns the number of connections, or a negative error code.
  */
-int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
-                         const hda_nid_t **listp)
+int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
+                           hda_nid_t *conn_list, int max_conns)
 {
        struct snd_array *array = &codec->conn_lists;
-       int len, err;
-       hda_nid_t list[HDA_MAX_CONNECTIONS];
+       int len;
        hda_nid_t *p;
        bool added = false;
 
  again:
+       mutex_lock(&codec->hash_mutex);
+       len = -1;
        /* if the connection-list is already cached, read it */
        p = lookup_conn_list(array, nid);
        if (p) {
-               if (listp)
-                       *listp = p + 2;
-               return p[1];
+               len = p[1];
+               if (conn_list && len > max_conns) {
+                       snd_printk(KERN_ERR "hda_codec: "
+                                  "Too many connections %d for NID 0x%x\n",
+                                  len, nid);
+                       mutex_unlock(&codec->hash_mutex);
+                       return -EINVAL;
+               }
+               if (conn_list && len)
+                       memcpy(conn_list, p + 2, len * sizeof(hda_nid_t));
        }
+       mutex_unlock(&codec->hash_mutex);
+       if (len >= 0)
+               return len;
        if (snd_BUG_ON(added))
                return -EINVAL;
 
-       /* read the connection and add to the cache */
-       len = snd_hda_get_raw_connections(codec, nid, list, HDA_MAX_CONNECTIONS);
+       len = read_and_add_raw_conns(codec, nid);
        if (len < 0)
                return len;
-       err = snd_hda_override_conn_list(codec, nid, len, list);
-       if (err < 0)
-               return err;
        added = true;
        goto again;
 }
-EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
-
-/**
- * snd_hda_get_connections - copy connection list
- * @codec: the HDA codec
- * @nid: NID to parse
- * @conn_list: connection list array
- * @max_conns: max. number of connections to store
- *
- * Parses the connection list of the given widget and stores the list
- * of NIDs.
- *
- * Returns the number of connections, or a negative error code.
- */
-int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
-                            hda_nid_t *conn_list, int max_conns)
-{
-       const hda_nid_t *list;
-       int len = snd_hda_get_conn_list(codec, nid, &list);
-
-       if (len <= 0)
-               return len;
-       if (len > max_conns) {
-               snd_printk(KERN_ERR "hda_codec: "
-                          "Too many connections %d for NID 0x%x\n",
-                          len, nid);
-               return -EINVAL;
-       }
-       memcpy(conn_list, list, len * sizeof(hda_nid_t));
-       return len;
-}
 EXPORT_SYMBOL_HDA(snd_hda_get_connections);
 
 /**
@@ -543,6 +532,7 @@ int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
        hda_nid_t *p;
        int i, old_used;
 
+       mutex_lock(&codec->hash_mutex);
        p = lookup_conn_list(array, nid);
        if (p)
                *p = -1; /* invalidate the old entry */
@@ -553,10 +543,12 @@ int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
        for (i = 0; i < len; i++)
                if (!add_conn_list(array, list[i]))
                        goto error_add;
+       mutex_unlock(&codec->hash_mutex);
        return 0;
 
  error_add:
        array->used = old_used;
+       mutex_unlock(&codec->hash_mutex);
        return -ENOMEM;
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
@@ -1255,6 +1247,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
        codec->addr = codec_addr;
        mutex_init(&codec->spdif_mutex);
        mutex_init(&codec->control_mutex);
+       mutex_init(&codec->hash_mutex);
        init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
        init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
        snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
@@ -1264,15 +1257,9 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
        snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
        snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
        snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
-       if (codec->bus->modelname) {
-               codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
-               if (!codec->modelname) {
-                       snd_hda_codec_free(codec);
-                       return -ENODEV;
-               }
-       }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
+       spin_lock_init(&codec->power_lock);
        INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
        /* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
         * the caller has to power down appropriatley after initialization
@@ -1281,6 +1268,14 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
        hda_keep_power_on(codec);
 #endif
 
+       if (codec->bus->modelname) {
+               codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
+               if (!codec->modelname) {
+                       snd_hda_codec_free(codec);
+                       return -ENODEV;
+               }
+       }
+
        list_add_tail(&codec->list, &bus->codec_list);
        bus->caddr_tbl[codec_addr] = codec;
 
@@ -1603,6 +1598,60 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
        return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
 }
 
+/* overwrite the value with the key in the caps hash */
+static int write_caps_hash(struct hda_codec *codec, u32 key, unsigned int val)
+{
+       struct hda_amp_info *info;
+
+       mutex_lock(&codec->hash_mutex);
+       info = get_alloc_amp_hash(codec, key);
+       if (!info) {
+               mutex_unlock(&codec->hash_mutex);
+               return -EINVAL;
+       }
+       info->amp_caps = val;
+       info->head.val |= INFO_AMP_CAPS;
+       mutex_unlock(&codec->hash_mutex);
+       return 0;
+}
+
+/* query the value from the caps hash; if not found, fetch the current
+ * value from the given function and store in the hash
+ */
+static unsigned int
+query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key,
+               unsigned int (*func)(struct hda_codec *, hda_nid_t, int))
+{
+       struct hda_amp_info *info;
+       unsigned int val;
+
+       mutex_lock(&codec->hash_mutex);
+       info = get_alloc_amp_hash(codec, key);
+       if (!info) {
+               mutex_unlock(&codec->hash_mutex);
+               return 0;
+       }
+       if (!(info->head.val & INFO_AMP_CAPS)) {
+               mutex_unlock(&codec->hash_mutex); /* for reentrance */
+               val = func(codec, nid, dir);
+               write_caps_hash(codec, key, val);
+       } else {
+               val = info->amp_caps;
+               mutex_unlock(&codec->hash_mutex);
+       }
+       return val;
+}
+
+static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid,
+                                int direction)
+{
+       if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
+               nid = codec->afg;
+       return snd_hda_param_read(codec, nid,
+                                 direction == HDA_OUTPUT ?
+                                 AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+}
+
 /**
  * query_amp_caps - query AMP capabilities
  * @codec: the HD-auio codec
@@ -1617,22 +1666,9 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
  */
 u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 {
-       struct hda_amp_info *info;
-
-       info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0));
-       if (!info)
-               return 0;
-       if (!(info->head.val & INFO_AMP_CAPS)) {
-               if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
-                       nid = codec->afg;
-               info->amp_caps = snd_hda_param_read(codec, nid,
-                                                   direction == HDA_OUTPUT ?
-                                                   AC_PAR_AMP_OUT_CAP :
-                                                   AC_PAR_AMP_IN_CAP);
-               if (info->amp_caps)
-                       info->head.val |= INFO_AMP_CAPS;
-       }
-       return info->amp_caps;
+       return query_caps_hash(codec, nid, direction,
+                              HDA_HASH_KEY(nid, direction, 0),
+                              read_amp_cap);
 }
 EXPORT_SYMBOL_HDA(query_amp_caps);
 
@@ -1652,34 +1688,12 @@ EXPORT_SYMBOL_HDA(query_amp_caps);
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
                              unsigned int caps)
 {
-       struct hda_amp_info *info;
-
-       info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, dir, 0));
-       if (!info)
-               return -EINVAL;
-       info->amp_caps = caps;
-       info->head.val |= INFO_AMP_CAPS;
-       return 0;
+       return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps);
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
 
-static unsigned int
-query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key,
-               unsigned int (*func)(struct hda_codec *, hda_nid_t))
-{
-       struct hda_amp_info *info;
-
-       info = get_alloc_amp_hash(codec, key);
-       if (!info)
-               return 0;
-       if (!info->head.val) {
-               info->head.val |= INFO_AMP_CAPS;
-               info->amp_caps = func(codec, nid);
-       }
-       return info->amp_caps;
-}
-
-static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid,
+                                int dir)
 {
        return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
 }
@@ -1697,7 +1711,7 @@ static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
  */
 u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
 {
-       return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
+       return query_caps_hash(codec, nid, 0, HDA_HASH_PINCAP_KEY(nid),
                               read_pin_cap);
 }
 EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
@@ -1715,41 +1729,47 @@ EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
 int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
                              unsigned int caps)
 {
-       struct hda_amp_info *info;
-       info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid));
-       if (!info)
-               return -ENOMEM;
-       info->amp_caps = caps;
-       info->head.val |= INFO_AMP_CAPS;
-       return 0;
+       return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps);
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps);
 
-/*
- * read the current volume to info
- * if the cache exists, read the cache value.
+/* read or sync the hash value with the current value;
+ * call within hash_mutex
  */
-static unsigned int get_vol_mute(struct hda_codec *codec,
-                                struct hda_amp_info *info, hda_nid_t nid,
-                                int ch, int direction, int index)
+static struct hda_amp_info *
+update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
+               int direction, int index)
 {
-       u32 val, parm;
-
-       if (info->head.val & INFO_AMP_VOL(ch))
-               return info->vol[ch];
+       struct hda_amp_info *info;
+       unsigned int parm, val = 0;
+       bool val_read = false;
 
-       parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
-       parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
-       parm |= index;
-       val = snd_hda_codec_read(codec, nid, 0,
+ retry:
+       info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
+       if (!info)
+               return NULL;
+       if (!(info->head.val & INFO_AMP_VOL(ch))) {
+               if (!val_read) {
+                       mutex_unlock(&codec->hash_mutex);
+                       parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
+                       parm |= direction == HDA_OUTPUT ?
+                               AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
+                       parm |= index;
+                       val = snd_hda_codec_read(codec, nid, 0,
                                 AC_VERB_GET_AMP_GAIN_MUTE, parm);
-       info->vol[ch] = val & 0xff;
-       info->head.val |= INFO_AMP_VOL(ch);
-       return info->vol[ch];
+                       val &= 0xff;
+                       val_read = true;
+                       mutex_lock(&codec->hash_mutex);
+                       goto retry;
+               }
+               info->vol[ch] = val;
+               info->head.val |= INFO_AMP_VOL(ch);
+       }
+       return info;
 }
 
 /*
- * write the current volume in info to the h/w and update the cache
+ * write the current volume in info to the h/w
  */
 static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
                         hda_nid_t nid, int ch, int direction, int index,
@@ -1766,7 +1786,6 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
        else
                parm |= val;
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
-       info->vol[ch] = val;
 }
 
 /**
@@ -1783,10 +1802,14 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
                           int direction, int index)
 {
        struct hda_amp_info *info;
-       info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
-       if (!info)
-               return 0;
-       return get_vol_mute(codec, info, nid, ch, direction, index);
+       unsigned int val = 0;
+
+       mutex_lock(&codec->hash_mutex);
+       info = update_amp_hash(codec, nid, ch, direction, index);
+       if (info)
+               val = info->vol[ch];
+       mutex_unlock(&codec->hash_mutex);
+       return val;
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
 
@@ -1808,15 +1831,23 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 {
        struct hda_amp_info *info;
 
-       info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
-       if (!info)
-               return 0;
        if (snd_BUG_ON(mask & ~0xff))
                mask &= 0xff;
        val &= mask;
-       val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask;
-       if (info->vol[ch] == val)
+
+       mutex_lock(&codec->hash_mutex);
+       info = update_amp_hash(codec, nid, ch, direction, idx);
+       if (!info) {
+               mutex_unlock(&codec->hash_mutex);
+               return 0;
+       }
+       val |= info->vol[ch] & ~mask;
+       if (info->vol[ch] == val) {
+               mutex_unlock(&codec->hash_mutex);
                return 0;
+       }
+       info->vol[ch] = val;
+       mutex_unlock(&codec->hash_mutex);
        put_vol_mute(codec, info, nid, ch, direction, idx, val);
        return 1;
 }
@@ -2263,7 +2294,10 @@ int snd_hda_codec_reset(struct hda_codec *codec)
        /* OK, let it free */
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-       cancel_delayed_work(&codec->power_work);
+       cancel_delayed_work_sync(&codec->power_work);
+       codec->power_on = 0;
+       codec->power_transition = 0;
+       codec->power_jiffies = jiffies;
        flush_workqueue(codec->bus->workq);
 #endif
        snd_hda_ctls_clear(codec);
@@ -2859,12 +2893,15 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        int idx = kcontrol->private_value;
-       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+       struct hda_spdif_out *spdif;
 
+       mutex_lock(&codec->spdif_mutex);
+       spdif = snd_array_elem(&codec->spdif_out, idx);
        ucontrol->value.iec958.status[0] = spdif->status & 0xff;
        ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff;
        ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff;
        ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff;
+       mutex_unlock(&codec->spdif_mutex);
 
        return 0;
 }
@@ -2950,12 +2987,14 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        int idx = kcontrol->private_value;
-       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
-       hda_nid_t nid = spdif->nid;
+       struct hda_spdif_out *spdif;
+       hda_nid_t nid;
        unsigned short val;
        int change;
 
        mutex_lock(&codec->spdif_mutex);
+       spdif = snd_array_elem(&codec->spdif_out, idx);
+       nid = spdif->nid;
        spdif->status = ucontrol->value.iec958.status[0] |
                ((unsigned int)ucontrol->value.iec958.status[1] << 8) |
                ((unsigned int)ucontrol->value.iec958.status[2] << 16) |
@@ -2977,9 +3016,12 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        int idx = kcontrol->private_value;
-       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+       struct hda_spdif_out *spdif;
 
+       mutex_lock(&codec->spdif_mutex);
+       spdif = snd_array_elem(&codec->spdif_out, idx);
        ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE;
+       mutex_unlock(&codec->spdif_mutex);
        return 0;
 }
 
@@ -2999,12 +3041,14 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        int idx = kcontrol->private_value;
-       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
-       hda_nid_t nid = spdif->nid;
+       struct hda_spdif_out *spdif;
+       hda_nid_t nid;
        unsigned short val;
        int change;
 
        mutex_lock(&codec->spdif_mutex);
+       spdif = snd_array_elem(&codec->spdif_out, idx);
+       nid = spdif->nid;
        val = spdif->ctls & ~AC_DIG1_ENABLE;
        if (ucontrol->value.integer.value[0])
                val |= AC_DIG1_ENABLE;
@@ -3092,6 +3136,9 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls);
 
+/* get the hda_spdif_out entry from the given NID
+ * call within spdif_mutex lock
+ */
 struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
                                               hda_nid_t nid)
 {
@@ -3108,9 +3155,10 @@ EXPORT_SYMBOL_HDA(snd_hda_spdif_out_of_nid);
 
 void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx)
 {
-       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+       struct hda_spdif_out *spdif;
 
        mutex_lock(&codec->spdif_mutex);
+       spdif = snd_array_elem(&codec->spdif_out, idx);
        spdif->nid = (u16)-1;
        mutex_unlock(&codec->spdif_mutex);
 }
@@ -3118,10 +3166,11 @@ EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_unassign);
 
 void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
 {
-       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+       struct hda_spdif_out *spdif;
        unsigned short val;
 
        mutex_lock(&codec->spdif_mutex);
+       spdif = snd_array_elem(&codec->spdif_out, idx);
        if (spdif->nid != nid) {
                spdif->nid = nid;
                val = spdif->ctls;
@@ -3486,11 +3535,14 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
                            codec->afg ? codec->afg : codec->mfg,
                            AC_PWRST_D3);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-       snd_hda_update_power_acct(codec);
        cancel_delayed_work(&codec->power_work);
+       spin_lock(&codec->power_lock);
+       snd_hda_update_power_acct(codec);
+       trace_hda_power_down(codec);
        codec->power_on = 0;
        codec->power_transition = 0;
        codec->power_jiffies = jiffies;
+       spin_unlock(&codec->power_lock);
 #endif
 }
 
@@ -3499,6 +3551,10 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
  */
 static void hda_call_codec_resume(struct hda_codec *codec)
 {
+       /* set as if powered on for avoiding re-entering the resume
+        * in the resume / power-save sequence
+        */
+       hda_keep_power_on(codec);
        hda_set_power_state(codec,
                            codec->afg ? codec->afg : codec->mfg,
                            AC_PWRST_D0);
@@ -3514,6 +3570,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
                snd_hda_codec_resume_amp(codec);
                snd_hda_codec_resume_cache(codec);
        }
+       snd_hda_power_down(codec); /* flag down before returning */
 }
 #endif /* CONFIG_PM */
 
@@ -3665,7 +3722,8 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
 }
 EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
 
-static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid,
+                                 int dir)
 {
        unsigned int val = 0;
        if (nid != codec->afg &&
@@ -3680,11 +3738,12 @@ static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
 
 static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
 {
-       return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid),
+       return query_caps_hash(codec, nid, 0, HDA_HASH_PARPCM_KEY(nid),
                               get_pcm_param);
 }
 
-static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid,
+                                    int dir)
 {
        unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
        if (!streams || streams == -1)
@@ -3696,7 +3755,7 @@ static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
 
 static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
 {
-       return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid),
+       return query_caps_hash(codec, nid, 0, HDA_HASH_PARSTR_KEY(nid),
                               get_stream_param);
 }
 
@@ -3775,11 +3834,13 @@ int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
                                        bps = 20;
                        }
                }
+#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
                if (streams & AC_SUPFMT_FLOAT32) {
                        formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
                        if (!bps)
                                bps = 32;
                }
+#endif
                if (streams == AC_SUPFMT_AC3) {
                        /* should be exclusive */
                        /* temporary hack: we have still no proper support
@@ -4283,12 +4344,18 @@ static void hda_power_work(struct work_struct *work)
                container_of(work, struct hda_codec, power_work.work);
        struct hda_bus *bus = codec->bus;
 
+       spin_lock(&codec->power_lock);
+       if (codec->power_transition > 0) { /* during power-up sequence? */
+               spin_unlock(&codec->power_lock);
+               return;
+       }
        if (!codec->power_on || codec->power_count) {
                codec->power_transition = 0;
+               spin_unlock(&codec->power_lock);
                return;
        }
+       spin_unlock(&codec->power_lock);
 
-       trace_hda_power_down(codec);
        hda_call_codec_suspend(codec);
        if (bus->ops.pm_notify)
                bus->ops.pm_notify(bus);
@@ -4296,9 +4363,11 @@ static void hda_power_work(struct work_struct *work)
 
 static void hda_keep_power_on(struct hda_codec *codec)
 {
+       spin_lock(&codec->power_lock);
        codec->power_count++;
        codec->power_on = 1;
        codec->power_jiffies = jiffies;
+       spin_unlock(&codec->power_lock);
 }
 
 /* update the power on/off account with the current jiffies */
@@ -4323,19 +4392,31 @@ void snd_hda_power_up(struct hda_codec *codec)
 {
        struct hda_bus *bus = codec->bus;
 
+       spin_lock(&codec->power_lock);
        codec->power_count++;
-       if (codec->power_on || codec->power_transition)
+       if (codec->power_on || codec->power_transition > 0) {
+               spin_unlock(&codec->power_lock);
                return;
+       }
+       spin_unlock(&codec->power_lock);
 
+       cancel_delayed_work_sync(&codec->power_work);
+
+       spin_lock(&codec->power_lock);
        trace_hda_power_up(codec);
        snd_hda_update_power_acct(codec);
        codec->power_on = 1;
        codec->power_jiffies = jiffies;
+       codec->power_transition = 1; /* avoid reentrance */
+       spin_unlock(&codec->power_lock);
+
        if (bus->ops.pm_notify)
                bus->ops.pm_notify(bus);
        hda_call_codec_resume(codec);
-       cancel_delayed_work(&codec->power_work);
+
+       spin_lock(&codec->power_lock);
        codec->power_transition = 0;
+       spin_unlock(&codec->power_lock);
 }
 EXPORT_SYMBOL_HDA(snd_hda_power_up);
 
@@ -4351,14 +4432,18 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up);
  */
 void snd_hda_power_down(struct hda_codec *codec)
 {
+       spin_lock(&codec->power_lock);
        --codec->power_count;
-       if (!codec->power_on || codec->power_count || codec->power_transition)
+       if (!codec->power_on || codec->power_count || codec->power_transition) {
+               spin_unlock(&codec->power_lock);
                return;
+       }
        if (power_save(codec)) {
-               codec->power_transition = 1; /* avoid reentrance */
+               codec->power_transition = -1; /* avoid reentrance */
                queue_delayed_work(codec->bus->workq, &codec->power_work,
                                msecs_to_jiffies(power_save(codec) * 1000));
        }
+       spin_unlock(&codec->power_lock);
 }
 EXPORT_SYMBOL_HDA(snd_hda_power_down);
 
@@ -4710,11 +4795,11 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
 {
        const hda_nid_t *nids = mout->dac_nids;
        int chs = substream->runtime->channels;
-       struct hda_spdif_out *spdif =
-                       snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
+       struct hda_spdif_out *spdif;
        int i;
 
        mutex_lock(&codec->spdif_mutex);
+       spdif = snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
        if (mout->dig_out_nid && mout->share_spdif &&
            mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
                if (chs == 2 &&
@@ -4795,601 +4880,58 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup);
 
-/*
- * Helper for automatic pin configuration
- */
-
-static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list)
-{
-       for (; *list; list++)
-               if (*list == nid)
-                       return 1;
-       return 0;
-}
-
-
-/*
- * Sort an associated group of pins according to their sequence numbers.
- */
-static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
-                                 int num_pins)
-{
-       int i, j;
-       short seq;
-       hda_nid_t nid;
-
-       for (i = 0; i < num_pins; i++) {
-               for (j = i + 1; j < num_pins; j++) {
-                       if (sequences[i] > sequences[j]) {
-                               seq = sequences[i];
-                               sequences[i] = sequences[j];
-                               sequences[j] = seq;
-                               nid = pins[i];
-                               pins[i] = pins[j];
-                               pins[j] = nid;
-                       }
-               }
-       }
-}
-
-
-/* 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;
-                       }
-               }
-       }
-}
-
-/* Reorder the surround channels
- * ALSA sequence is front/surr/clfe/side
- * HDA sequence is:
- *    4-ch: front/surr  =>  OK as it is
- *    6-ch: front/clfe/surr
- *    8-ch: front/clfe/rear/side|fc
- */
-static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
-{
-       hda_nid_t nid;
-
-       switch (nums) {
-       case 3:
-       case 4:
-               nid = pins[1];
-               pins[1] = pins[2];
-               pins[2] = nid;
-               break;
-       }
-}
-
-/*
- * Parse all pin widgets and store the useful pin nids to cfg
- *
- * The number of line-outs or any primary output is stored in line_outs,
- * and the corresponding output pins are assigned to line_out_pins[],
- * in the order of front, rear, CLFE, side, ...
- *
- * If more extra outputs (speaker and headphone) are found, the pins are
- * assisnged to hp_pins[] and speaker_pins[], respectively.  If no line-out jack
- * is detected, one of speaker of HP pins is assigned as the primary
- * 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 inputs array.
- * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
- * respectively.
- */
-int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
-                            struct auto_pin_cfg *cfg,
-                            const hda_nid_t *ignore_nids,
-                            unsigned int cond_flags)
-{
-       hda_nid_t nid, end_nid;
-       short seq, assoc_line_out;
-       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));
-
-       memset(sequences_line_out, 0, sizeof(sequences_line_out));
-       memset(sequences_speaker, 0, sizeof(sequences_speaker));
-       memset(sequences_hp, 0, sizeof(sequences_hp));
-       assoc_line_out = 0;
-
-       codec->ignore_misc_bit = true;
-       end_nid = codec->start_nid + codec->num_nodes;
-       for (nid = codec->start_nid; nid < end_nid; nid++) {
-               unsigned int wid_caps = get_wcaps(codec, nid);
-               unsigned int wid_type = get_wcaps_type(wid_caps);
-               unsigned int def_conf;
-               short assoc, loc, conn, dev;
-
-               /* read all default configuration for pin complex */
-               if (wid_type != AC_WID_PIN)
-                       continue;
-               /* ignore the given nids (e.g. pc-beep returns error) */
-               if (ignore_nids && is_in_nid_list(nid, ignore_nids))
-                       continue;
-
-               def_conf = snd_hda_codec_get_pincfg(codec, nid);
-               if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
-                     AC_DEFCFG_MISC_NO_PRESENCE))
-                       codec->ignore_misc_bit = false;
-               conn = get_defcfg_connect(def_conf);
-               if (conn == AC_JACK_PORT_NONE)
-                       continue;
-               loc = get_defcfg_location(def_conf);
-               dev = get_defcfg_device(def_conf);
-
-               /* workaround for buggy BIOS setups */
-               if (dev == AC_JACK_LINE_OUT) {
-                       if (conn == AC_JACK_PORT_FIXED)
-                               dev = AC_JACK_SPEAKER;
-               }
-
-               switch (dev) {
-               case AC_JACK_LINE_OUT:
-                       seq = get_defcfg_sequence(def_conf);
-                       assoc = get_defcfg_association(def_conf);
-
-                       if (!(wid_caps & AC_WCAP_STEREO))
-                               if (!cfg->mono_out_pin)
-                                       cfg->mono_out_pin = nid;
-                       if (!assoc)
-                               continue;
-                       if (!assoc_line_out)
-                               assoc_line_out = assoc;
-                       else if (assoc_line_out != assoc)
-                               continue;
-                       if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))
-                               continue;
-                       cfg->line_out_pins[cfg->line_outs] = nid;
-                       sequences_line_out[cfg->line_outs] = seq;
-                       cfg->line_outs++;
-                       break;
-               case AC_JACK_SPEAKER:
-                       seq = get_defcfg_sequence(def_conf);
-                       assoc = get_defcfg_association(def_conf);
-                       if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
-                               continue;
-                       cfg->speaker_pins[cfg->speaker_outs] = nid;
-                       sequences_speaker[cfg->speaker_outs] = (assoc << 4) | seq;
-                       cfg->speaker_outs++;
-                       break;
-               case AC_JACK_HP_OUT:
-                       seq = get_defcfg_sequence(def_conf);
-                       assoc = get_defcfg_association(def_conf);
-                       if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
-                               continue;
-                       cfg->hp_pins[cfg->hp_outs] = nid;
-                       sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
-                       cfg->hp_outs++;
-                       break;
-               case AC_JACK_MIC_IN:
-                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
-                       break;
-               case AC_JACK_LINE_IN:
-                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
-                       break;
-               case AC_JACK_CD:
-                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
-                       break;
-               case AC_JACK_AUX:
-                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
-                       break;
-               case AC_JACK_SPDIF_OUT:
-               case AC_JACK_DIG_OTHER_OUT:
-                       if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
-                               continue;
-                       cfg->dig_out_pins[cfg->dig_outs] = nid;
-                       cfg->dig_out_type[cfg->dig_outs] =
-                               (loc == AC_JACK_LOC_HDMI) ?
-                               HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
-                       cfg->dig_outs++;
-                       break;
-               case AC_JACK_SPDIF_IN:
-               case AC_JACK_DIG_OTHER_IN:
-                       cfg->dig_in_pin = nid;
-                       if (loc == AC_JACK_LOC_HDMI)
-                               cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
-                       else
-                               cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
-                       break;
-               }
-       }
-
-       /* FIX-UP:
-        * If no line-out is defined but multiple HPs are found,
-        * some of them might be the real line-outs.
-        */
-       if (!cfg->line_outs && cfg->hp_outs > 1 &&
-           !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) {
-               int i = 0;
-               while (i < cfg->hp_outs) {
-                       /* The real HPs should have the sequence 0x0f */
-                       if ((sequences_hp[i] & 0x0f) == 0x0f) {
-                               i++;
-                               continue;
-                       }
-                       /* Move it to the line-out table */
-                       cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i];
-                       sequences_line_out[cfg->line_outs] = sequences_hp[i];
-                       cfg->line_outs++;
-                       cfg->hp_outs--;
-                       memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
-                               sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
-                       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));
-               if (!cfg->hp_outs)
-                       cfg->line_out_type = AUTO_PIN_HP_OUT;
-
-       }
-
-       /* sort by sequence */
-       sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
-                             cfg->line_outs);
-       sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
-                             cfg->speaker_outs);
-       sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
-                             cfg->hp_outs);
-
-       /*
-        * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
-        * as a primary output
-        */
-       if (!cfg->line_outs &&
-           !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) {
-               if (cfg->speaker_outs) {
-                       cfg->line_outs = cfg->speaker_outs;
-                       memcpy(cfg->line_out_pins, cfg->speaker_pins,
-                              sizeof(cfg->speaker_pins));
-                       cfg->speaker_outs = 0;
-                       memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
-                       cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
-               } else if (cfg->hp_outs) {
-                       cfg->line_outs = cfg->hp_outs;
-                       memcpy(cfg->line_out_pins, cfg->hp_pins,
-                              sizeof(cfg->hp_pins));
-                       cfg->hp_outs = 0;
-                       memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
-                       cfg->line_out_type = AUTO_PIN_HP_OUT;
-               }
-       }
-
-       reorder_outputs(cfg->line_outs, cfg->line_out_pins);
-       reorder_outputs(cfg->hp_outs, cfg->hp_pins);
-       reorder_outputs(cfg->speaker_outs, cfg->speaker_pins);
-
-       sort_autocfg_input_pins(cfg);
-
-       /*
-        * debug prints of the parsed results
-        */
-       snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
-                  cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
-                  cfg->line_out_pins[2], cfg->line_out_pins[3],
-                  cfg->line_out_pins[4],
-                  cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" :
-                  (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ?
-                   "speaker" : "line"));
-       snd_printd("   speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
-                  cfg->speaker_outs, cfg->speaker_pins[0],
-                  cfg->speaker_pins[1], cfg->speaker_pins[2],
-                  cfg->speaker_pins[3], cfg->speaker_pins[4]);
-       snd_printd("   hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
-                  cfg->hp_outs, cfg->hp_pins[0],
-                  cfg->hp_pins[1], cfg->hp_pins[2],
-                  cfg->hp_pins[3], cfg->hp_pins[4]);
-       snd_printd("   mono: mono_out=0x%x\n", cfg->mono_out_pin);
-       if (cfg->dig_outs)
-               snd_printd("   dig-out=0x%x/0x%x\n",
-                          cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
-       snd_printd("   inputs:");
-       for (i = 0; i < cfg->num_inputs; i++) {
-               snd_printd(" %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);
-
-       return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_parse_pin_defcfg);
-
-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".
- */
-
-static const char *hda_get_input_pin_label(struct hda_codec *codec,
-                                          hda_nid_t pin, bool check_location)
-{
-       unsigned int def_conf;
-       static const char * const 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";
-       }
-}
-
-/* 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
+ * snd_hda_get_default_vref - Get the default (mic) VREF pin bits
  *
- * 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);
-
-/* return the position of NID in the list, or -1 if not found */
-static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
-{
-       int i;
-       for (i = 0; i < nums; i++)
-               if (list[i] == nid)
-                       return i;
-       return -1;
-}
-
-/* get a unique suffix or an index number */
-static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins,
-                                   int num_pins, int *indexp)
-{
-       static const char * const channel_sfx[] = {
-               " Front", " Surround", " CLFE", " Side"
-       };
-       int i;
-
-       i = find_idx_in_nid_list(nid, pins, num_pins);
-       if (i < 0)
-               return NULL;
-       if (num_pins == 1)
-               return "";
-       if (num_pins > ARRAY_SIZE(channel_sfx)) {
-               if (indexp)
-                       *indexp = i;
-               return "";
-       }
-       return channel_sfx[i];
-}
-
-static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
-                              const struct auto_pin_cfg *cfg,
-                              const char *name, char *label, int maxlen,
-                              int *indexp)
-{
-       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
-       int attr = snd_hda_get_input_pin_attr(def_conf);
-       const char *pfx = "", *sfx = "";
-
-       /* handle as a speaker if it's a fixed line-out */
-       if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT)
-               name = "Speaker";
-       /* check the location */
-       switch (attr) {
-       case INPUT_PIN_ATTR_DOCK:
-               pfx = "Dock ";
-               break;
-       case INPUT_PIN_ATTR_FRONT:
-               pfx = "Front ";
-               break;
-       }
-       if (cfg) {
-               /* try to give a unique suffix if needed */
-               sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs,
-                                      indexp);
-               if (!sfx)
-                       sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs,
-                                              indexp);
-               if (!sfx) {
-                       /* don't add channel suffix for Headphone controls */
-                       int idx = find_idx_in_nid_list(nid, cfg->hp_pins,
-                                                      cfg->hp_outs);
-                       if (idx >= 0)
-                               *indexp = idx;
-                       sfx = "";
+ * Guess the suitable VREF pin bits to be set as the pin-control value.
+ * Note: the function doesn't set the AC_PINCTL_IN_EN bit.
+ */
+unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin)
+{
+       unsigned int pincap;
+       unsigned int oldval;
+       oldval = snd_hda_codec_read(codec, pin, 0,
+                                   AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+       pincap = snd_hda_query_pin_caps(codec, pin);
+       pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+       /* Exception: if the default pin setup is vref50, we give it priority */
+       if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
+               return AC_PINCTL_VREF_80;
+       else if (pincap & AC_PINCAP_VREF_50)
+               return AC_PINCTL_VREF_50;
+       else if (pincap & AC_PINCAP_VREF_100)
+               return AC_PINCTL_VREF_100;
+       else if (pincap & AC_PINCAP_VREF_GRD)
+               return AC_PINCTL_VREF_GRD;
+       return AC_PINCTL_VREF_HIZ;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_default_vref);
+
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+                        unsigned int val, bool cached)
+{
+       if (val) {
+               unsigned int cap = snd_hda_query_pin_caps(codec, pin);
+               if (cap && (val & AC_PINCTL_OUT_EN)) {
+                       if (!(cap & AC_PINCAP_OUT))
+                               val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+                       else if ((val & AC_PINCTL_HP_EN) &&
+                                !(cap & AC_PINCAP_HP_DRV))
+                               val &= ~AC_PINCTL_HP_EN;
                }
-       }
-       snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
-       return 1;
-}
-
-/**
- * snd_hda_get_pin_label - Get a label for the given I/O pin
- *
- * Get a label for the given pin.  This function works for both input and
- * output pins.  When @cfg is given as non-NULL, the function tries to get
- * an optimized label using hda_get_autocfg_input_label().
- *
- * This function tries to give a unique label string for the pin as much as
- * possible.  For example, when the multiple line-outs are present, it adds
- * the channel suffix like "Front", "Surround", etc (only when @cfg is given).
- * If no unique name with a suffix is available and @indexp is non-NULL, the
- * index number is stored in the pointer.
- */
-int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
-                         const struct auto_pin_cfg *cfg,
-                         char *label, int maxlen, int *indexp)
-{
-       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
-       const char *name = NULL;
-       int i;
-
-       if (indexp)
-               *indexp = 0;
-       if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
-               return 0;
-
-       switch (get_defcfg_device(def_conf)) {
-       case AC_JACK_LINE_OUT:
-               return fill_audio_out_name(codec, nid, cfg, "Line Out",
-                                          label, maxlen, indexp);
-       case AC_JACK_SPEAKER:
-               return fill_audio_out_name(codec, nid, cfg, "Speaker",
-                                          label, maxlen, indexp);
-       case AC_JACK_HP_OUT:
-               return fill_audio_out_name(codec, nid, cfg, "Headphone",
-                                          label, maxlen, indexp);
-       case AC_JACK_SPDIF_OUT:
-       case AC_JACK_DIG_OTHER_OUT:
-               if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI)
-                       name = "HDMI";
-               else
-                       name = "SPDIF";
-               if (cfg && indexp) {
-                       i = find_idx_in_nid_list(nid, cfg->dig_out_pins,
-                                                cfg->dig_outs);
-                       if (i >= 0)
-                               *indexp = i;
-               }
-               break;
-       default:
-               if (cfg) {
-                       for (i = 0; i < cfg->num_inputs; i++) {
-                               if (cfg->inputs[i].pin != nid)
-                                       continue;
-                               name = hda_get_autocfg_input_label(codec, cfg, i);
-                               if (name)
-                                       break;
-                       }
+               if (cap && (val & AC_PINCTL_IN_EN)) {
+                       if (!(cap & AC_PINCAP_IN))
+                               val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
                }
-               if (!name)
-                       name = hda_get_input_pin_label(codec, nid, true);
-               break;
        }
-       if (!name)
-               return 0;
-       strlcpy(label, name, maxlen);
-       return 1;
+       if (cached)
+               return snd_hda_codec_update_cache(codec, pin, 0,
+                               AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+       else
+               return snd_hda_codec_write(codec, pin, 0,
+                                          AC_VERB_SET_PIN_WIDGET_CONTROL, val);
 }
-EXPORT_SYMBOL_HDA(snd_hda_get_pin_label);
+EXPORT_SYMBOL_HDA(_snd_hda_set_pin_ctl);
 
 /**
  * snd_hda_add_imux_item - Add an item to input_mux
@@ -5444,8 +4986,6 @@ int snd_hda_suspend(struct hda_bus *bus)
        list_for_each_entry(codec, &bus->codec_list, list) {
                if (hda_codec_is_power_on(codec))
                        hda_call_codec_suspend(codec);
-               if (codec->patch_ops.post_suspend)
-                       codec->patch_ops.post_suspend(codec);
        }
        return 0;
 }
@@ -5465,10 +5005,7 @@ int snd_hda_resume(struct hda_bus *bus)
        struct hda_codec *codec;
 
        list_for_each_entry(codec, &bus->codec_list, list) {
-               if (codec->patch_ops.pre_resume)
-                       codec->patch_ops.pre_resume(codec);
-               if (snd_hda_codec_needs_resume(codec))
-                       hda_call_codec_resume(codec);
+               hda_call_codec_resume(codec);
        }
        return 0;
 }
index 56b4f74c0b13a101e16e9433f76341ef7a5a7c0e..54b52819fb47acf5ef8b2342643a1ece7d9a8c57 100644 (file)
@@ -704,8 +704,6 @@ struct hda_codec_ops {
                                unsigned int power_state);
 #ifdef CONFIG_PM
        int (*suspend)(struct hda_codec *codec, pm_message_t state);
-       int (*post_suspend)(struct hda_codec *codec);
-       int (*pre_resume)(struct hda_codec *codec);
        int (*resume)(struct hda_codec *codec);
 #endif
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -829,6 +827,7 @@ struct hda_codec {
 
        struct mutex spdif_mutex;
        struct mutex control_mutex;
+       struct mutex hash_mutex;
        struct snd_array spdif_out;
        unsigned int spdif_in_enable;   /* SPDIF input enable? */
        const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
@@ -861,12 +860,13 @@ struct hda_codec {
        unsigned int no_jack_detect:1;  /* Machine has no jack-detection */
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        unsigned int power_on :1;       /* current (global) power-state */
-       unsigned int power_transition :1; /* power-state in transition */
+       int power_transition;   /* power-state in transition */
        int power_count;        /* current (global) power refcount */
        struct delayed_work power_work; /* delayed task for powerdown */
        unsigned long power_on_acct;
        unsigned long power_off_acct;
        unsigned long power_jiffies;
+       spinlock_t power_lock;
 #endif
 
        /* codec-specific additional proc output */
@@ -911,10 +911,13 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
                          hda_nid_t *start_id);
 int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
                            hda_nid_t *conn_list, int max_conns);
+static inline int
+snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid)
+{
+       return snd_hda_get_connections(codec, nid, NULL, 0);
+}
 int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
                            hda_nid_t *conn_list, int max_conns);
-int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
-                         const hda_nid_t **listp);
 int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
                          const hda_nid_t *list);
 int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
@@ -1051,12 +1054,10 @@ const char *snd_hda_get_jack_location(u32 cfg);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 void snd_hda_power_up(struct hda_codec *codec);
 void snd_hda_power_down(struct hda_codec *codec);
-#define snd_hda_codec_needs_resume(codec) codec->power_count
 void snd_hda_update_power_acct(struct hda_codec *codec);
 #else
 static inline void snd_hda_power_up(struct hda_codec *codec) {}
 static inline void snd_hda_power_down(struct hda_codec *codec) {}
-#define snd_hda_codec_needs_resume(codec) 1
 #endif
 
 #ifdef CONFIG_SND_HDA_PATCH_LOADER
index 1f350522bed436812b794c2544f39674ec4a868d..7ee46aba6ea7522bffe5b7e70da71544aeee2f5b 100644 (file)
@@ -497,6 +497,7 @@ enum {
        AZX_DRIVER_NVIDIA,
        AZX_DRIVER_TERA,
        AZX_DRIVER_CTX,
+       AZX_DRIVER_CTHDA,
        AZX_DRIVER_GENERIC,
        AZX_NUM_DRIVERS, /* keep this as last entry */
 };
@@ -518,6 +519,7 @@ enum {
 #define AZX_DCAPS_OLD_SSYNC    (1 << 20)       /* Old SSYNC reg for ICH */
 #define AZX_DCAPS_BUFSIZE      (1 << 21)       /* no buffer size alignment */
 #define AZX_DCAPS_ALIGN_BUFSIZE        (1 << 22)       /* buffer size alignment */
+#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23)   /* BDLE in 4k boundary */
 
 /* quirks for ATI SB / AMD Hudson */
 #define AZX_DCAPS_PRESET_ATI_SB \
@@ -533,6 +535,9 @@ enum {
        (AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI |\
         AZX_DCAPS_ALIGN_BUFSIZE)
 
+#define AZX_DCAPS_PRESET_CTHDA \
+       (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_4K_BDLE_BOUNDARY)
+
 static char *driver_short_names[] __devinitdata = {
        [AZX_DRIVER_ICH] = "HDA Intel",
        [AZX_DRIVER_PCH] = "HDA Intel PCH",
@@ -546,6 +551,7 @@ static char *driver_short_names[] __devinitdata = {
        [AZX_DRIVER_NVIDIA] = "HDA NVidia",
        [AZX_DRIVER_TERA] = "HDA Teradici", 
        [AZX_DRIVER_CTX] = "HDA Creative", 
+       [AZX_DRIVER_CTHDA] = "HDA Creative",
        [AZX_DRIVER_GENERIC] = "HD-Audio Generic",
 };
 
@@ -1285,7 +1291,8 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
 /*
  * set up a BDL entry
  */
-static int setup_bdle(struct snd_pcm_substream *substream,
+static int setup_bdle(struct azx *chip,
+                     struct snd_pcm_substream *substream,
                      struct azx_dev *azx_dev, u32 **bdlp,
                      int ofs, int size, int with_ioc)
 {
@@ -1304,6 +1311,12 @@ static int setup_bdle(struct snd_pcm_substream *substream,
                bdl[1] = cpu_to_le32(upper_32_bits(addr));
                /* program the size field of the BDL entry */
                chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
+               /* one BDLE cannot cross 4K boundary on CTHDA chips */
+               if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) {
+                       u32 remain = 0x1000 - (ofs & 0xfff);
+                       if (chunk > remain)
+                               chunk = remain;
+               }
                bdl[2] = cpu_to_le32(chunk);
                /* program the IOC to enable interrupt
                 * only when the whole fragment is processed
@@ -1356,7 +1369,7 @@ static int azx_setup_periods(struct azx *chip,
                                   bdl_pos_adj[chip->dev_index]);
                        pos_adj = 0;
                } else {
-                       ofs = setup_bdle(substream, azx_dev,
+                       ofs = setup_bdle(chip, substream, azx_dev,
                                         &bdl, ofs, pos_adj,
                                         !substream->runtime->no_period_wakeup);
                        if (ofs < 0)
@@ -1366,10 +1379,10 @@ static int azx_setup_periods(struct azx *chip,
                pos_adj = 0;
        for (i = 0; i < periods; i++) {
                if (i == periods - 1 && pos_adj)
-                       ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+                       ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs,
                                         period_bytes - pos_adj, 0);
                else
-                       ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+                       ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs,
                                         period_bytes,
                                         !substream->runtime->no_period_wakeup);
                if (ofs < 0)
@@ -2353,17 +2366,6 @@ static void azx_power_notify(struct hda_bus *bus)
  * power management
  */
 
-static int snd_hda_codecs_inuse(struct hda_bus *bus)
-{
-       struct hda_codec *codec;
-
-       list_for_each_entry(codec, &bus->codec_list, list) {
-               if (snd_hda_codec_needs_resume(codec))
-                       return 1;
-       }
-       return 0;
-}
-
 static int azx_suspend(struct pci_dev *pci, pm_message_t state)
 {
        struct snd_card *card = pci_get_drvdata(pci);
@@ -2410,8 +2412,7 @@ static int azx_resume(struct pci_dev *pci)
                return -EIO;
        azx_init_pci(chip);
 
-       if (snd_hda_codecs_inuse(chip->bus))
-               azx_init_chip(chip, 1);
+       azx_init_chip(chip, 1);
 
        snd_hda_resume(chip->bus);
        snd_power_change_state(card, SNDRV_CTL_POWER_D0);
@@ -3130,6 +3131,11 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
          .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
          AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
 #endif
+       /* CTHDA chips */
+       { PCI_DEVICE(0x1102, 0x0010),
+         .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
+       { PCI_DEVICE(0x1102, 0x0012),
+         .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
        /* Vortex86MX */
        { PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
        /* VMware HDAudio */
index d68948499fbc15d7342ba0c9a63675d769b95a92..2dd1c113a4c1b62248843849a5604ceb972f9f89 100644 (file)
@@ -17,6 +17,7 @@
 #include <sound/jack.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_jack.h"
 
 bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
index c66655cf413a1bc074f62ac91e0ed6e3907e342d..8ae52465ec5df807efde3dad54c3a11fc00ca5aa 100644 (file)
@@ -12,6 +12,8 @@
 #ifndef __SOUND_HDA_JACK_H
 #define __SOUND_HDA_JACK_H
 
+struct auto_pin_cfg;
+
 struct hda_jack_tbl {
        hda_nid_t nid;
        unsigned char action;           /* event action (0 = none) */
index 0ec9248165bca9c7f7f7d47aaffe4abc863b600c..9a096a8e0fc5b8cb01ce7620956c9bd5848eec4e 100644 (file)
@@ -262,6 +262,8 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
                          const struct hda_input_mux *imux,
                          struct snd_ctl_elem_value *ucontrol, hda_nid_t nid,
                          unsigned int *cur_val);
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+                         int index, int *type_index_ret);
 
 /*
  * Channel mode helper
@@ -393,72 +395,7 @@ struct hda_bus_unsolicited {
        struct hda_bus *bus;
 };
 
-/*
- * Helper for automatic pin configuration
- */
-
-enum {
-       AUTO_PIN_MIC,
-       AUTO_PIN_LINE_IN,
-       AUTO_PIN_CD,
-       AUTO_PIN_AUX,
-       AUTO_PIN_LAST
-};
-
-enum {
-       AUTO_PIN_LINE_OUT,
-       AUTO_PIN_SPEAKER_OUT,
-       AUTO_PIN_HP_OUT
-};
-
-#define AUTO_CFG_MAX_OUTS      HDA_MAX_OUTS
-#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_autocfg_input_label(struct hda_codec *codec,
-                                       const struct auto_pin_cfg *cfg,
-                                       int input);
-int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
-                         const struct auto_pin_cfg *cfg,
-                         char *label, int maxlen, int *indexp);
-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;
-       /* sorted in the order of Front/Surr/CLFE/Side */
-       hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
-       int speaker_outs;
-       hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
-       int hp_outs;
-       int line_out_type;      /* AUTO_PIN_XXX_OUT */
-       hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
-       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;
-       hda_nid_t mono_out_pin;
-       int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
-       int dig_in_type; /* HDA_PCM_TYPE_XXX */
-};
-
+/* helper macros to retrieve pin default-config values */
 #define get_defcfg_connect(cfg) \
        ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT)
 #define get_defcfg_association(cfg) \
@@ -472,19 +409,6 @@ struct auto_pin_cfg {
 #define get_defcfg_misc(cfg) \
        ((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT)
 
-/* bit-flags for snd_hda_parse_pin_def_config() behavior */
-#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */
-#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */
-
-int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
-                            struct auto_pin_cfg *cfg,
-                            const hda_nid_t *ignore_nids,
-                            unsigned int cond_flags);
-
-/* older function */
-#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
-       snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
-
 /* amp values */
 #define AMP_IN_MUTE(idx)       (0x7080 | ((idx)<<8))
 #define AMP_IN_UNMUTE(idx)     (0x7000 | ((idx)<<8))
@@ -502,6 +426,46 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
 #define PIN_HP                 (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
 #define PIN_HP_AMP             (AC_PINCTL_HP_EN)
 
+unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin);
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+                        unsigned int val, bool cached);
+
+/**
+ * _snd_hda_set_pin_ctl - Set a pin-control value safely
+ * @codec: the codec instance
+ * @pin: the pin NID to set the control
+ * @val: the pin-control value (AC_PINCTL_* bits)
+ *
+ * This function sets the pin-control value to the given pin, but
+ * filters out the invalid pin-control bits when the pin has no such
+ * capabilities.  For example, when PIN_HP is passed but the pin has no
+ * HP-drive capability, the HP bit is omitted.
+ *
+ * The function doesn't check the input VREF capability bits, though.
+ * Use snd_hda_get_default_vref() to guess the right value.
+ * Also, this function is only for analog pins, not for HDMI pins.
+ */
+static inline int
+snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val)
+{
+       return _snd_hda_set_pin_ctl(codec, pin, val, false);
+}
+
+/**
+ * snd_hda_set_pin_ctl_cache - Set a pin-control value safely
+ * @codec: the codec instance
+ * @pin: the pin NID to set the control
+ * @val: the pin-control value (AC_PINCTL_* bits)
+ *
+ * Just like snd_hda_set_pin_ctl() but write to cache as well.
+ */
+static inline int
+snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin,
+                         unsigned int val)
+{
+       return _snd_hda_set_pin_ctl(codec, pin, val, true);
+}
+
 /*
  * get widget capabilities
  */
index 7143393927da34ddeac2c86ce6538c669e85d16a..d8b2d6dee986b928ae0ffe98509a167e07ba639f 100644 (file)
@@ -28,6 +28,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_beep.h"
 #include "hda_jack.h"
 
@@ -1742,9 +1743,7 @@ static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
        if (! ad198x_eapd_put(kcontrol, ucontrol))
                return 0;
        /* change speaker pin appropriately */
-       snd_hda_codec_write(codec, 0x05, 0,
-                           AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           spec->cur_eapd ? PIN_OUT : 0);
+       snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0);
        /* toggle HP mute appropriately */
        snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE,
@@ -3103,7 +3102,7 @@ static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
                                              int dac_idx)
 {
        /* set as output */
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+       snd_hda_set_pin_ctl(codec, nid, pin_type);
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
        switch (nid) {
        case 0x11: /* port-A - DAC 03 */
@@ -3157,6 +3156,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec)
        for (i = 0; i < cfg->num_inputs; i++) {
                hda_nid_t nid = cfg->inputs[i].pin;
                int type = cfg->inputs[i].type;
+               int val;
                switch (nid) {
                case 0x15: /* port-C */
                        snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
@@ -3165,8 +3165,10 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec)
                        snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
                        break;
                }
-               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   type == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
+               val = PIN_IN;
+               if (type == AUTO_PIN_MIC)
+                       val |= snd_hda_get_default_vref(codec, nid);
+               snd_hda_set_pin_ctl(codec, nid, val);
                if (nid != AD1988_PIN_CD_NID)
                        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
                                            AMP_OUT_MUTE);
index 09ccfabb4a17c38a608d04d05b4a5979e6dc7ed7..19ae14f739cbbd7ad0d5c6bcff8cf945db97ff1b 100644 (file)
@@ -26,6 +26,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 
 /*
  */
@@ -341,8 +342,7 @@ static int ca0110_build_pcms(struct hda_codec *codec)
 static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
 {
        if (pin) {
-               snd_hda_codec_write(codec, pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+               snd_hda_set_pin_ctl(codec, pin, PIN_HP);
                if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
                        snd_hda_codec_write(codec, pin, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
@@ -356,8 +356,8 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
 static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
 {
        if (pin) {
-               snd_hda_codec_write(codec, pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
+               snd_hda_set_pin_ctl(codec, pin, PIN_IN |
+                                   snd_hda_get_default_vref(codec, pin));
                if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
                        snd_hda_codec_write(codec, pin, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
index 21d91d580da8b9e29091dca8ad5d91440b38797a..d0d3540e39e7746b1c42074257fe8194b928e09b 100644 (file)
@@ -30,6 +30,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 
 #define WIDGET_CHIP_CTRL      0x15
 #define WIDGET_DSP_CTRL       0x16
@@ -239,8 +240,7 @@ enum get_set {
 static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
 {
        if (pin) {
-               snd_hda_codec_write(codec, pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+               snd_hda_set_pin_ctl(codec, pin, PIN_HP);
                if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
                        snd_hda_codec_write(codec, pin, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
@@ -254,9 +254,8 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
 static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
 {
        if (pin) {
-               snd_hda_codec_write(codec, pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   PIN_VREF80);
+               snd_hda_set_pin_ctl(codec, pin, PIN_IN |
+                                   snd_hda_get_default_vref(codec, pin));
                if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
                        snd_hda_codec_write(codec, pin, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
index c83ccdba1e5afc1dca9715a870eedf68a8961ee8..9647ed4d7929175452ea77eee81677342264104f 100644 (file)
@@ -26,6 +26,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_jack.h"
 #include <sound/tlv.h>
 
@@ -933,8 +934,7 @@ static void cs_automute(struct hda_codec *codec)
                        pin_ctl = 0;
 
                nid = cfg->speaker_pins[i];
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, pin_ctl);
+               snd_hda_set_pin_ctl(codec, nid, pin_ctl);
        }
        if (spec->gpio_eapd_hp) {
                unsigned int gpio = hp_present ?
@@ -948,16 +948,14 @@ static void cs_automute(struct hda_codec *codec)
                /* mute HPs if spdif jack (SENSE_B) is present */
                for (i = 0; i < cfg->hp_outs; i++) {
                        nid = cfg->hp_pins[i];
-                       snd_hda_codec_write(codec, nid, 0,
-                               AC_VERB_SET_PIN_WIDGET_CONTROL,
+                       snd_hda_set_pin_ctl(codec, nid,
                                (spdif_present && spec->sense_b) ? 0 : PIN_HP);
                }
 
                /* SPDIF TX on/off */
                if (cfg->dig_outs) {
                        nid = cfg->dig_out_pins[0];
-                       snd_hda_codec_write(codec, nid, 0,
-                               AC_VERB_SET_PIN_WIDGET_CONTROL,
+                       snd_hda_set_pin_ctl(codec, nid,
                                spdif_present ? PIN_OUT : 0);
 
                }
@@ -1024,13 +1022,11 @@ static void init_output(struct hda_codec *codec)
 
        /* set appropriate pin controls */
        for (i = 0; i < cfg->line_outs; i++)
-               snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+               snd_hda_set_pin_ctl(codec, cfg->line_out_pins[i], PIN_OUT);
        /* HP */
        for (i = 0; i < cfg->hp_outs; i++) {
                hda_nid_t nid = cfg->hp_pins[i];
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+               snd_hda_set_pin_ctl(codec, nid, PIN_HP);
                if (!cfg->speaker_outs)
                        continue;
                if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
@@ -1041,8 +1037,7 @@ static void init_output(struct hda_codec *codec)
 
        /* Speaker */
        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);
+               snd_hda_set_pin_ctl(codec, cfg->speaker_pins[i], PIN_OUT);
 
        /* SPDIF is enabled on presence detect for CS421x */
        if (spec->hp_detect || spec->spdif_detect)
@@ -1063,14 +1058,9 @@ static void init_input(struct hda_codec *codec)
                        continue;
                /* set appropriate pin control and mute first */
                ctl = PIN_IN;
-               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)
-                               ctl = PIN_VREF80;
-               }
-               snd_hda_codec_write(codec, pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
+               if (cfg->inputs[i].type == AUTO_PIN_MIC)
+                       ctl |= snd_hda_get_default_vref(codec, pin);
+               snd_hda_set_pin_ctl(codec, pin, ctl);
                snd_hda_codec_write(codec, spec->adc_nid[i], 0,
                                    AC_VERB_SET_AMP_GAIN_MUTE,
                                    AMP_IN_MUTE(spec->adc_idx[i]));
index b6767b4ced4478c5d1d7a0eb671d528284ef8e34..c8fdaaefe7029a403e81e03dcac96cee721ecca3 100644 (file)
@@ -29,6 +29,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #define NUM_PINS       11
 
 
index d906c5b74cf0e7b047a4e702c71ba8b6d0e5d5de..3acb5824ad39e95e354491c2336a445cd796745e 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_beep.h"
 #include "hda_jack.h"
 
@@ -66,6 +67,7 @@ struct imux_info {
 };
 
 struct conexant_spec {
+       struct hda_gen_spec gen;
 
        const struct snd_kcontrol_new *mixers[5];
        int num_mixers;
@@ -141,6 +143,7 @@ struct conexant_spec {
        unsigned int hp_laptop:1;
        unsigned int asus:1;
        unsigned int pin_eapd_ctrls:1;
+       unsigned int fixup_stereo_dmic:1;
 
        unsigned int adc_switching:1;
 
@@ -1601,17 +1604,13 @@ static void cxt5051_update_speaker(struct hda_codec *codec)
        unsigned int pinctl;
        /* headphone pin */
        pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0;
-       snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           pinctl);
+       snd_hda_set_pin_ctl(codec, 0x16, pinctl);
        /* speaker pin */
        pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
-       snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           pinctl);
+       snd_hda_set_pin_ctl(codec, 0x1a, pinctl);
        /* on ideapad there is an additional speaker (subwoofer) to mute */
        if (spec->ideapad)
-               snd_hda_codec_write(codec, 0x1b, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   pinctl);
+               snd_hda_set_pin_ctl(codec, 0x1b, pinctl);
 }
 
 /* turn on/off EAPD (+ mute HP) as a master switch */
@@ -1996,8 +1995,7 @@ static void cxt5066_update_speaker(struct hda_codec *codec)
 
        /* Port A (HP) */
        pinctl = (hp_port_a_present(spec) && spec->cur_eapd) ? PIN_HP : 0;
-       snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                       pinctl);
+       snd_hda_set_pin_ctl(codec, 0x19, pinctl);
 
        /* Port D (HP/LO) */
        pinctl = spec->cur_eapd ? spec->port_d_mode : 0;
@@ -2010,13 +2008,11 @@ static void cxt5066_update_speaker(struct hda_codec *codec)
                if (!hp_port_d_present(spec))
                        pinctl = 0;
        }
-       snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                       pinctl);
+       snd_hda_set_pin_ctl(codec, 0x1c, pinctl);
 
        /* CLASS_D AMP */
        pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
-       snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                       pinctl);
+       snd_hda_set_pin_ctl(codec, 0x1f, pinctl);
 }
 
 /* turn on/off EAPD (+ mute HP) as a master switch */
@@ -2047,8 +2043,7 @@ static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec)
        /* Even though port F is the DC input, the bias is controlled on port B.
         * we also leave that port as an active input (but unselected) in DC mode
         * just in case that is necessary to make the bias setting take effect. */
-       return snd_hda_codec_write_cache(codec, 0x1a, 0,
-               AC_VERB_SET_PIN_WIDGET_CONTROL,
+       return snd_hda_set_pin_ctl_cache(codec, 0x1a,
                cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index);
 }
 
@@ -2081,14 +2076,14 @@ static void cxt5066_olpc_select_mic(struct hda_codec *codec)
        }
 
        /* disable DC (port F) */
-       snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+       snd_hda_set_pin_ctl(codec, 0x1e, 0);
 
        /* external mic, port B */
-       snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+       snd_hda_set_pin_ctl(codec, 0x1a,
                spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
 
        /* internal mic, port C */
-       snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+       snd_hda_set_pin_ctl(codec, 0x1b,
                spec->ext_mic_present ? 0 : PIN_VREF80);
 }
 
@@ -3357,9 +3352,7 @@ static void do_automute(struct hda_codec *codec, int num_pins,
        struct conexant_spec *spec = codec->spec;
        int i;
        for (i = 0; i < num_pins; i++)
-               snd_hda_codec_write(codec, pins[i], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   on ? PIN_OUT : 0);
+               snd_hda_set_pin_ctl(codec, pins[i], on ? PIN_OUT : 0);
        if (spec->pin_eapd_ctrls)
                cx_auto_turn_eapd(codec, num_pins, pins, on);
 }
@@ -3976,8 +3969,7 @@ static void cx_auto_init_output(struct hda_codec *codec)
                if (snd_hda_query_pin_caps(codec, cfg->hp_pins[i]) &
                    AC_PINCAP_HP_DRV)
                        val |= AC_PINCTL_HP_EN;
-               snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+               snd_hda_set_pin_ctl(codec, cfg->hp_pins[i], val);
        }
        mute_outputs(codec, cfg->hp_outs, cfg->hp_pins);
        mute_outputs(codec, cfg->line_outs, cfg->line_out_pins);
@@ -4030,13 +4022,11 @@ static void cx_auto_init_input(struct hda_codec *codec)
        }
 
        for (i = 0; i < cfg->num_inputs; i++) {
-               unsigned int type;
+               hda_nid_t pin = cfg->inputs[i].pin;
+               unsigned int type = PIN_IN;
                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);
+                       type |= snd_hda_get_default_vref(codec, pin);
+               snd_hda_set_pin_ctl(codec, pin, type);
        }
 
        if (spec->auto_mic) {
@@ -4063,11 +4053,9 @@ static void cx_auto_init_digital(struct hda_codec *codec)
        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);
+               snd_hda_set_pin_ctl(codec, cfg->dig_out_pins[0], 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);
+               snd_hda_set_pin_ctl(codec, cfg->dig_in_pin, PIN_IN);
 }
 
 static int cx_auto_init(struct hda_codec *codec)
@@ -4084,9 +4072,9 @@ static int cx_auto_init(struct hda_codec *codec)
 
 static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
                              const char *dir, int cidx,
-                             hda_nid_t nid, int hda_dir, int amp_idx)
+                             hda_nid_t nid, int hda_dir, int amp_idx, int chs)
 {
-       static char name[32];
+       static char name[44];
        static struct snd_kcontrol_new knew[] = {
                HDA_CODEC_VOLUME(name, 0, 0, 0),
                HDA_CODEC_MUTE(name, 0, 0, 0),
@@ -4096,7 +4084,7 @@ static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
 
        for (i = 0; i < 2; i++) {
                struct snd_kcontrol *kctl;
-               knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, amp_idx,
+               knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, chs, amp_idx,
                                                            hda_dir);
                knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
                knew[i].index = cidx;
@@ -4115,7 +4103,7 @@ static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
 }
 
 #define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir)                \
-       cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0)
+       cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0, 3)
 
 #define cx_auto_add_pb_volume(codec, nid, str, idx)                    \
        cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
@@ -4185,6 +4173,36 @@ static int cx_auto_build_output_controls(struct hda_codec *codec)
        return 0;
 }
 
+/* Returns zero if this is a normal stereo channel, and non-zero if it should
+   be split in two independent channels.
+   dest_label must be at least 44 characters. */
+static int cx_auto_get_rightch_label(struct hda_codec *codec, const char *label,
+                                    char *dest_label, int nid)
+{
+       struct conexant_spec *spec = codec->spec;
+       int i;
+
+       if (!spec->fixup_stereo_dmic)
+               return 0;
+
+       for (i = 0; i < AUTO_CFG_MAX_INS; i++) {
+               int def_conf;
+               if (spec->autocfg.inputs[i].pin != nid)
+                       continue;
+
+               if (spec->autocfg.inputs[i].type != AUTO_PIN_MIC)
+                       return 0;
+               def_conf = snd_hda_codec_get_pincfg(codec, nid);
+               if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT)
+                       return 0;
+
+               /* Finally found the inverted internal mic! */
+               snprintf(dest_label, 44, "Inverted %s", label);
+               return 1;
+       }
+       return 0;
+}
+
 static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
                                      const char *label, const char *pfx,
                                      int cidx)
@@ -4193,14 +4211,25 @@ static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
        int i;
 
        for (i = 0; i < spec->num_adc_nids; i++) {
+               char rightch_label[44];
                hda_nid_t adc_nid = spec->adc_nids[i];
                int idx = get_input_connection(codec, adc_nid, nid);
                if (idx < 0)
                        continue;
                if (codec->single_adc_amp)
                        idx = 0;
+
+               if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) {
+                       /* Make two independent kcontrols for left and right */
+                       int err = cx_auto_add_volume_idx(codec, label, pfx,
+                                             cidx, adc_nid, HDA_INPUT, idx, 1);
+                       if (err < 0)
+                               return err;
+                       return cx_auto_add_volume_idx(codec, rightch_label, pfx,
+                                                     cidx, adc_nid, HDA_INPUT, idx, 2);
+               }
                return cx_auto_add_volume_idx(codec, label, pfx,
-                                             cidx, adc_nid, HDA_INPUT, idx);
+                                             cidx, adc_nid, HDA_INPUT, idx, 3);
        }
        return 0;
 }
@@ -4213,9 +4242,19 @@ static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx,
        int i, con;
 
        nid = spec->imux_info[idx].pin;
-       if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
+       if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
+               char rightch_label[44];
+               if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) {
+                       int err = cx_auto_add_volume_idx(codec, label, " Boost",
+                                                        cidx, nid, HDA_INPUT, 0, 1);
+                       if (err < 0)
+                               return err;
+                       return cx_auto_add_volume_idx(codec, rightch_label, " Boost",
+                                                     cidx, nid, HDA_INPUT, 0, 2);
+               }
                return cx_auto_add_volume(codec, label, " Boost", cidx,
                                          nid, HDA_INPUT);
+       }
        con = __select_input_connection(codec, spec->imux_info[idx].adc, nid,
                                        &mux, false, 0);
        if (con < 0)
@@ -4370,37 +4409,21 @@ static const struct hda_codec_ops cx_auto_patch_ops = {
 /*
  * pin fix-up
  */
-struct cxt_pincfg {
-       hda_nid_t nid;
-       u32 val;
-};
-
-static void apply_pincfg(struct hda_codec *codec, const struct cxt_pincfg *cfg)
-{
-       for (; cfg->nid; cfg++)
-               snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
-
-}
-
-static void apply_pin_fixup(struct hda_codec *codec,
-                           const struct snd_pci_quirk *quirk,
-                           const struct cxt_pincfg **table)
-{
-       quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
-       if (quirk) {
-               snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n",
-                           quirk->name);
-               apply_pincfg(codec, table[quirk->value]);
-       }
-}
-
 enum {
        CXT_PINCFG_LENOVO_X200,
        CXT_PINCFG_LENOVO_TP410,
+       CXT_FIXUP_STEREO_DMIC,
 };
 
+static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
+                                 const struct hda_fixup *fix, int action)
+{
+       struct conexant_spec *spec = codec->spec;
+       spec->fixup_stereo_dmic = 1;
+}
+
 /* ThinkPad X200 & co with cxt5051 */
-static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = {
+static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
        { 0x16, 0x042140ff }, /* HP (seq# overridden) */
        { 0x17, 0x21a11000 }, /* dock-mic */
        { 0x19, 0x2121103f }, /* dock-HP */
@@ -4409,16 +4432,26 @@ static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = {
 };
 
 /* ThinkPad 410/420/510/520, X201 & co with cxt5066 */
-static const struct cxt_pincfg cxt_pincfg_lenovo_tp410[] = {
+static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = {
        { 0x19, 0x042110ff }, /* HP (seq# overridden) */
        { 0x1a, 0x21a190f0 }, /* dock-mic */
        { 0x1c, 0x212140ff }, /* dock-HP */
        {}
 };
 
-static const struct cxt_pincfg *cxt_pincfg_tbl[] = {
-       [CXT_PINCFG_LENOVO_X200] = cxt_pincfg_lenovo_x200,
-       [CXT_PINCFG_LENOVO_TP410] = cxt_pincfg_lenovo_tp410,
+static const struct hda_fixup cxt_fixups[] = {
+       [CXT_PINCFG_LENOVO_X200] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = cxt_pincfg_lenovo_x200,
+       },
+       [CXT_PINCFG_LENOVO_TP410] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = cxt_pincfg_lenovo_tp410,
+       },
+       [CXT_FIXUP_STEREO_DMIC] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = cxt_fixup_stereo_dmic,
+       },
 };
 
 static const struct snd_pci_quirk cxt5051_fixups[] = {
@@ -4432,6 +4465,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
        SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
        SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410),
        SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
+       SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
        {}
 };
 
@@ -4471,13 +4505,16 @@ static int patch_conexant_auto(struct hda_codec *codec)
        case 0x14f15051:
                add_cx5051_fake_mutes(codec);
                codec->pin_amp_workaround = 1;
-               apply_pin_fixup(codec, cxt5051_fixups, cxt_pincfg_tbl);
+               snd_hda_pick_fixup(codec, NULL, cxt5051_fixups, cxt_fixups);
                break;
        default:
                codec->pin_amp_workaround = 1;
-               apply_pin_fixup(codec, cxt5066_fixups, cxt_pincfg_tbl);
+               snd_hda_pick_fixup(codec, NULL, cxt5066_fixups, cxt_fixups);
+               break;
        }
 
+       snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
        /* Show mute-led control only on HP laptops
         * This is a sort of white-list: on HP laptops, EAPD corresponds
         * only to the mute-LED without actualy amp function.  Meanwhile,
@@ -4556,6 +4593,12 @@ static const struct hda_codec_preset snd_hda_preset_conexant[] = {
          .patch = patch_conexant_auto },
        { .id = 0x14f150b9, .name = "CX20665",
          .patch = patch_conexant_auto },
+       { .id = 0x14f1510f, .name = "CX20751/2",
+         .patch = patch_conexant_auto },
+       { .id = 0x14f15110, .name = "CX20751/2",
+         .patch = patch_conexant_auto },
+       { .id = 0x14f15111, .name = "CX20753/4",
+         .patch = patch_conexant_auto },
        {} /* terminator */
 };
 
@@ -4576,6 +4619,9 @@ 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_ALIAS("snd-hda-codec-id:14f1510f");
+MODULE_ALIAS("snd-hda-codec-id:14f15110");
+MODULE_ALIAS("snd-hda-codec-id:14f15111");
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Conexant HD-audio codec");
index 83f345f3c961b05313a17e10d9c46d53ace138c7..ad319d4dc32f7e5d0d5265020a0f700c10bb08ab 100644 (file)
@@ -1592,10 +1592,10 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
        unsigned int dataDCC2, channel_id;
        int i;
        struct hdmi_spec *spec = codec->spec;
-       struct hda_spdif_out *spdif =
-               snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
+       struct hda_spdif_out *spdif;
 
        mutex_lock(&codec->spdif_mutex);
+       spdif = snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
 
        chs = substream->runtime->channels;
 
index 7810913d07a032f303815966f4c19c26cc22c5a1..3cb1f7128b5f04a25a30bfdf2be788996ac1f3b2 100644 (file)
@@ -32,6 +32,7 @@
 #include <sound/jack.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_beep.h"
 #include "hda_jack.h"
 
@@ -66,8 +67,6 @@ struct alc_customize_define {
        unsigned int  fixup:1; /* Means that this sku is set by driver, not read from hw */
 };
 
-struct alc_fixup;
-
 struct alc_multi_io {
        hda_nid_t pin;          /* multi-io widget pin NID */
        hda_nid_t dac;          /* DAC to be connected */
@@ -82,19 +81,33 @@ enum {
 
 #define MAX_VOL_NIDS   0x40
 
+/* make compatible with old code */
+#define alc_apply_pincfgs      snd_hda_apply_pincfgs
+#define alc_apply_fixup                snd_hda_apply_fixup
+#define alc_pick_fixup         snd_hda_pick_fixup
+#define alc_fixup              hda_fixup
+#define alc_pincfg             hda_pintbl
+#define alc_model_fixup                hda_model_fixup
+
+#define ALC_FIXUP_PINS HDA_FIXUP_PINS
+#define ALC_FIXUP_VERBS        HDA_FIXUP_VERBS
+#define ALC_FIXUP_FUNC HDA_FIXUP_FUNC
+
+#define ALC_FIXUP_ACT_PRE_PROBE        HDA_FIXUP_ACT_PRE_PROBE
+#define ALC_FIXUP_ACT_PROBE    HDA_FIXUP_ACT_PROBE
+#define ALC_FIXUP_ACT_INIT     HDA_FIXUP_ACT_INIT
+#define ALC_FIXUP_ACT_BUILD    HDA_FIXUP_ACT_BUILD
+
+
 struct alc_spec {
+       struct hda_gen_spec gen;
+
        /* codec parameterization */
        const struct snd_kcontrol_new *mixers[5];       /* mixer arrays */
        unsigned int num_mixers;
        const struct snd_kcontrol_new *cap_mixer;       /* capture mixer */
        unsigned int beep_amp;  /* beep amp value, set via set_beep_amp() */
 
-       const struct hda_verb *init_verbs[10];  /* initialization verbs
-                                                * don't forget NULL
-                                                * termination!
-                                                */
-       unsigned int num_init_verbs;
-
        char stream_name_analog[32];    /* analog PCM stream */
        const struct hda_pcm_stream *stream_analog_playback;
        const struct hda_pcm_stream *stream_analog_capture;
@@ -210,11 +223,6 @@ struct alc_spec {
        unsigned int pll_coef_idx, pll_coef_bit;
        unsigned int coef0;
 
-       /* fix-up list */
-       int fixup_id;
-       const struct alc_fixup *fixup_list;
-       const char *fixup_name;
-
        /* multi-io */
        int multi_ios;
        struct alc_multi_io multi_io[4];
@@ -319,13 +327,16 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
 
        /* for shared I/O, change the pin-control accordingly */
        if (spec->shared_mic_hp) {
+               unsigned int val;
+               hda_nid_t pin = spec->autocfg.inputs[1].pin;
                /* NOTE: this assumes that there are only two inputs, the
                 * first is the real internal mic and the second is HP jack.
                 */
-               snd_hda_codec_write(codec, spec->autocfg.inputs[1].pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   spec->cur_mux[adc_idx] ?
-                                   PIN_VREF80 : PIN_HP);
+               if (spec->cur_mux[adc_idx])
+                       val = snd_hda_get_default_vref(codec, pin) | PIN_IN;
+               else
+                       val = PIN_HP;
+               snd_hda_set_pin_ctl(codec, pin, val);
                spec->automute_speaker = !spec->cur_mux[adc_idx];
                call_update_outputs(codec);
        }
@@ -338,7 +349,7 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
        nid = get_capsrc(spec, adc_idx);
 
        /* no selection? */
-       num_conns = snd_hda_get_conn_list(codec, nid, NULL);
+       num_conns = snd_hda_get_num_conns(codec, nid);
        if (num_conns <= 1)
                return 1;
 
@@ -376,25 +387,9 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
                              int auto_pin_type)
 {
        unsigned int val = PIN_IN;
-
-       if (auto_pin_type == AUTO_PIN_MIC) {
-               unsigned int pincap;
-               unsigned int oldval;
-               oldval = snd_hda_codec_read(codec, nid, 0,
-                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-               pincap = snd_hda_query_pin_caps(codec, nid);
-               pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
-               /* if the default pin setup is vref50, we give it priority */
-               if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
-                       val = PIN_VREF80;
-               else if (pincap & AC_PINCAP_VREF_50)
-                       val = PIN_VREF50;
-               else if (pincap & AC_PINCAP_VREF_100)
-                       val = PIN_VREF100;
-               else if (pincap & AC_PINCAP_VREF_GRD)
-                       val = PIN_VREFGRD;
-       }
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+       if (auto_pin_type == AUTO_PIN_MIC)
+               val |= snd_hda_get_default_vref(codec, nid);
+       snd_hda_set_pin_ctl(codec, nid, val);
 }
 
 /*
@@ -409,13 +404,6 @@ static void add_mixer(struct alc_spec *spec, const struct snd_kcontrol_new *mix)
        spec->mixers[spec->num_mixers++] = mix;
 }
 
-static void add_verb(struct alc_spec *spec, const struct hda_verb *verb)
-{
-       if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs)))
-               return;
-       spec->init_verbs[spec->num_init_verbs++] = verb;
-}
-
 /*
  * GPIO setup tables, used in initialization
  */
@@ -517,9 +505,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
                        } else
                                val = 0;
                        val |= pin_bits;
-                       snd_hda_codec_write(codec, nid, 0,
-                                           AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                           val);
+                       snd_hda_set_pin_ctl(codec, nid, val);
                        break;
                case ALC_AUTOMUTE_AMP:
                        snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
@@ -1200,6 +1186,16 @@ static void alc_auto_check_switches(struct hda_codec *codec)
  */
 #define ALC_FIXUP_SKU_IGNORE (2)
 
+static void alc_fixup_sku_ignore(struct hda_codec *codec,
+                                const struct hda_fixup *fix, int action)
+{
+       struct alc_spec *spec = codec->spec;
+       if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+               spec->cdefine.fixup = 1;
+               spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE;
+       }
+}
+
 static int alc_auto_parse_customize_define(struct hda_codec *codec)
 {
        unsigned int ass, tmp, i;
@@ -1402,178 +1398,6 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports)
        }
 }
 
-/*
- * Fix-up pin default configurations and add default verbs
- */
-
-struct alc_pincfg {
-       hda_nid_t nid;
-       u32 val;
-};
-
-struct alc_model_fixup {
-       const int id;
-       const char *name;
-};
-
-struct alc_fixup {
-       int type;
-       bool chained;
-       int chain_id;
-       union {
-               unsigned int sku;
-               const struct alc_pincfg *pins;
-               const struct hda_verb *verbs;
-               void (*func)(struct hda_codec *codec,
-                            const struct alc_fixup *fix,
-                            int action);
-       } v;
-};
-
-enum {
-       ALC_FIXUP_INVALID,
-       ALC_FIXUP_SKU,
-       ALC_FIXUP_PINS,
-       ALC_FIXUP_VERBS,
-       ALC_FIXUP_FUNC,
-};
-
-enum {
-       ALC_FIXUP_ACT_PRE_PROBE,
-       ALC_FIXUP_ACT_PROBE,
-       ALC_FIXUP_ACT_INIT,
-       ALC_FIXUP_ACT_BUILD,
-};
-
-static void alc_apply_pincfgs(struct hda_codec *codec,
-                             const struct alc_pincfg *cfg)
-{
-       for (; cfg->nid; cfg++)
-               snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
-}
-
-static void alc_apply_fixup(struct hda_codec *codec, int action)
-{
-       struct alc_spec *spec = codec->spec;
-       int id = spec->fixup_id;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-       const char *modelname = spec->fixup_name;
-#endif
-       int depth = 0;
-
-       if (!spec->fixup_list)
-               return;
-
-       while (id >= 0) {
-               const struct alc_fixup *fix = spec->fixup_list + id;
-               const struct alc_pincfg *cfg;
-
-               switch (fix->type) {
-               case ALC_FIXUP_SKU:
-                       if (action != ALC_FIXUP_ACT_PRE_PROBE || !fix->v.sku)
-                               break;
-                       snd_printdd(KERN_INFO "hda_codec: %s: "
-                                   "Apply sku override for %s\n",
-                                   codec->chip_name, modelname);
-                       spec->cdefine.sku_cfg = fix->v.sku;
-                       spec->cdefine.fixup = 1;
-                       break;
-               case ALC_FIXUP_PINS:
-                       cfg = fix->v.pins;
-                       if (action != ALC_FIXUP_ACT_PRE_PROBE || !cfg)
-                               break;
-                       snd_printdd(KERN_INFO "hda_codec: %s: "
-                                   "Apply pincfg for %s\n",
-                                   codec->chip_name, modelname);
-                       alc_apply_pincfgs(codec, cfg);
-                       break;
-               case ALC_FIXUP_VERBS:
-                       if (action != ALC_FIXUP_ACT_PROBE || !fix->v.verbs)
-                               break;
-                       snd_printdd(KERN_INFO "hda_codec: %s: "
-                                   "Apply fix-verbs for %s\n",
-                                   codec->chip_name, modelname);
-                       add_verb(codec->spec, fix->v.verbs);
-                       break;
-               case ALC_FIXUP_FUNC:
-                       if (!fix->v.func)
-                               break;
-                       snd_printdd(KERN_INFO "hda_codec: %s: "
-                                   "Apply fix-func for %s\n",
-                                   codec->chip_name, modelname);
-                       fix->v.func(codec, fix, action);
-                       break;
-               default:
-                       snd_printk(KERN_ERR "hda_codec: %s: "
-                                  "Invalid fixup type %d\n",
-                                  codec->chip_name, fix->type);
-                       break;
-               }
-               if (!fix->chained)
-                       break;
-               if (++depth > 10)
-                       break;
-               id = fix->chain_id;
-       }
-}
-
-static void alc_pick_fixup(struct hda_codec *codec,
-                          const struct alc_model_fixup *models,
-                          const struct snd_pci_quirk *quirk,
-                          const struct alc_fixup *fixlist)
-{
-       struct alc_spec *spec = codec->spec;
-       const struct snd_pci_quirk *q;
-       int id = -1;
-       const char *name = NULL;
-
-       /* when model=nofixup is given, don't pick up any fixups */
-       if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
-               spec->fixup_list = NULL;
-               spec->fixup_id = -1;
-               return;
-       }
-
-       if (codec->modelname && models) {
-               while (models->name) {
-                       if (!strcmp(codec->modelname, models->name)) {
-                               id = models->id;
-                               name = models->name;
-                               break;
-                       }
-                       models++;
-               }
-       }
-       if (id < 0) {
-               q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
-               if (q) {
-                       id = q->value;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-                       name = q->name;
-#endif
-               }
-       }
-       if (id < 0) {
-               for (q = quirk; q->subvendor; q++) {
-                       unsigned int vendorid =
-                               q->subdevice | (q->subvendor << 16);
-                       if (vendorid == codec->subsystem_id) {
-                               id = q->value;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-                               name = q->name;
-#endif
-                               break;
-                       }
-               }
-       }
-
-       spec->fixup_id = id;
-       if (id >= 0) {
-               spec->fixup_list = fixlist;
-               spec->fixup_name = name;
-       }
-}
-
 /*
  * COEF access helper functions
  */
@@ -1621,8 +1445,7 @@ static void alc_auto_init_digital(struct hda_codec *codec)
                pin = spec->autocfg.dig_out_pins[i];
                if (!pin)
                        continue;
-               snd_hda_codec_write(codec, pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+               snd_hda_set_pin_ctl(codec, pin, PIN_OUT);
                if (!i)
                        dac = spec->multiout.dig_out_nid;
                else
@@ -1635,9 +1458,7 @@ static void alc_auto_init_digital(struct hda_codec *codec)
        }
        pin = spec->autocfg.dig_in_pin;
        if (pin)
-               snd_hda_codec_write(codec, pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   PIN_IN);
+               snd_hda_set_pin_ctl(codec, pin, PIN_IN);
 }
 
 /* parse digital I/Os and set up NIDs in BIOS auto-parse mode */
@@ -2068,7 +1889,6 @@ static void alc_auto_init_std(struct hda_codec *codec);
 static int alc_init(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int i;
 
        if (spec->init_hook)
                spec->init_hook(codec);
@@ -2076,8 +1896,6 @@ static int alc_init(struct hda_codec *codec)
        alc_fix_pll(codec);
        alc_auto_init_amp(codec, spec->init_amp);
 
-       for (i = 0; i < spec->num_init_verbs; i++)
-               snd_hda_sequence_write(codec, spec->init_verbs[i]);
        alc_init_special_input_src(codec);
        alc_auto_init_std(codec);
 
@@ -2725,7 +2543,6 @@ static int alc_auto_fill_adc_caps(struct hda_codec *codec)
        nid = codec->start_nid;
        for (i = 0; i < codec->num_nodes; i++, nid++) {
                hda_nid_t src;
-               const hda_nid_t *list;
                unsigned int caps = get_wcaps(codec, nid);
                int type = get_wcaps_type(caps);
 
@@ -2743,13 +2560,14 @@ static int alc_auto_fill_adc_caps(struct hda_codec *codec)
                                cap_nids[nums] = src;
                                break;
                        }
-                       n = snd_hda_get_conn_list(codec, src, &list);
+                       n = snd_hda_get_num_conns(codec, src);
                        if (n > 1) {
                                cap_nids[nums] = src;
                                break;
                        } else if (n != 1)
                                break;
-                       src = *list;
+                       if (snd_hda_get_connections(codec, src, &src, 1) != 1)
+                               break;
                }
                if (++nums >= max_nums)
                        break;
@@ -2856,8 +2674,7 @@ static int alc_auto_create_shared_input(struct hda_codec *codec)
 static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
                               unsigned int pin_type)
 {
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           pin_type);
+       snd_hda_set_pin_ctl(codec, nid, pin_type);
        /* unmute pin */
        if (nid_has_mute(codec, nid, HDA_OUTPUT))
                snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
@@ -2891,7 +2708,7 @@ static void alc_auto_init_analog_input(struct hda_codec *codec)
 
        /* mute all loopback inputs */
        if (spec->mixer_nid) {
-               int nums = snd_hda_get_conn_list(codec, spec->mixer_nid, NULL);
+               int nums = snd_hda_get_num_conns(codec, spec->mixer_nid);
                for (i = 0; i < nums; i++)
                        snd_hda_codec_write(codec, spec->mixer_nid, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
@@ -3521,7 +3338,7 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec,
        if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) {
                type = ALC_CTL_WIDGET_MUTE;
                val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
-       } else if (snd_hda_get_conn_list(codec, nid, NULL) == 1) {
+       } else if (snd_hda_get_num_conns(codec, nid) == 1) {
                type = ALC_CTL_WIDGET_MUTE;
                val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT);
        } else {
@@ -3998,9 +3815,7 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
                        snd_hda_codec_read(codec, nid, 0,
                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
        if (output) {
-               snd_hda_codec_update_cache(codec, nid, 0,
-                                          AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                          PIN_OUT);
+               snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
                if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
                        snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
                                                 HDA_AMP_MUTE, 0);
@@ -4009,9 +3824,8 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
                if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
                        snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
                                                 HDA_AMP_MUTE, HDA_AMP_MUTE);
-               snd_hda_codec_update_cache(codec, nid, 0,
-                                          AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                          spec->multi_io[idx].ctl_in);
+               snd_hda_set_pin_ctl_cache(codec, nid,
+                                         spec->multi_io[idx].ctl_in);
        }
        return 0;
 }
@@ -4084,7 +3898,7 @@ static void alc_remove_invalid_adc_nids(struct hda_codec *codec)
        nums = 0;
        for (n = 0; n < spec->num_adc_nids; n++) {
                hda_nid_t cap = spec->private_capsrc_nids[n];
-               int num_conns = snd_hda_get_conn_list(codec, cap, NULL);
+               int num_conns = snd_hda_get_num_conns(codec, cap);
                for (i = 0; i < imux->num_items; i++) {
                        hda_nid_t pin = spec->imux_pins[i];
                        if (pin) {
@@ -4213,7 +4027,7 @@ static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap,
        if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
                snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
                                         HDA_AMP_MUTE, 0);
-       } else if (snd_hda_get_conn_list(codec, cap, NULL) > 1) {
+       } else if (snd_hda_get_num_conns(codec, cap) > 1) {
                snd_hda_codec_write_cache(codec, cap, 0,
                                          AC_VERB_SET_CONNECT_SEL, idx);
        }
@@ -4427,6 +4241,25 @@ static int alc_parse_auto_config(struct hda_codec *codec,
        return 1;
 }
 
+/* common preparation job for alc_spec */
+static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
+{
+       struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       int err;
+
+       if (!spec)
+               return -ENOMEM;
+       codec->spec = spec;
+       spec->mixer_nid = mixer_nid;
+
+       err = alc_codec_rename_from_preset(codec);
+       if (err < 0) {
+               kfree(spec);
+               return err;
+       }
+       return 0;
+}
+
 static int alc880_parse_auto_config(struct hda_codec *codec)
 {
        static const hda_nid_t alc880_ignore[] = { 0x1d, 0 };
@@ -4808,13 +4641,11 @@ static int patch_alc880(struct hda_codec *codec)
        struct alc_spec *spec;
        int err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
+       err = alc_alloc_spec(codec, 0x0b);
+       if (err < 0)
+               return err;
 
-       spec->mixer_nid = 0x0b;
+       spec = codec->spec;
        spec->need_dac_fix = 1;
 
        alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
@@ -4890,7 +4721,7 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec,
                spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
                snd_hda_jack_detect_enable(codec, 0x0f, ALC_HP_EVENT);
                spec->unsol_event = alc_sku_unsol_event;
-               add_verb(codec->spec, alc_gpio1_init_verbs);
+               snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs);
        }
 }
 
@@ -5001,13 +4832,11 @@ static int patch_alc260(struct hda_codec *codec)
        struct alc_spec *spec;
        int err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
+       err = alc_alloc_spec(codec, 0x07);
+       if (err < 0)
+               return err;
 
-       spec->mixer_nid = 0x07;
+       spec = codec->spec;
 
        alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups);
        alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -5171,8 +5000,7 @@ static void alc889_fixup_mbp_vref(struct hda_codec *codec,
                val = snd_hda_codec_read(codec, nids[i], 0,
                                         AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
                val |= AC_PINCTL_VREF_80;
-               snd_hda_codec_write(codec, nids[i], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+               snd_hda_set_pin_ctl(codec, nids[i], val);
                spec->keep_vref_in_automute = 1;
                break;
        }
@@ -5193,8 +5021,7 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec,
                val = snd_hda_codec_read(codec, nids[i], 0,
                                         AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
                val |= AC_PINCTL_VREF_50;
-               snd_hda_codec_write(codec, nids[i], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+               snd_hda_set_pin_ctl(codec, nids[i], val);
        }
        spec->keep_vref_in_automute = 1;
 }
@@ -5225,8 +5052,8 @@ static const struct alc_fixup alc882_fixups[] = {
                }
        },
        [ALC882_FIXUP_ACER_ASPIRE_7736] = {
-               .type = ALC_FIXUP_SKU,
-               .v.sku = ALC_FIXUP_SKU_IGNORE,
+               .type = ALC_FIXUP_FUNC,
+               .v.func = alc_fixup_sku_ignore,
        },
        [ALC882_FIXUP_ASUS_W90V] = {
                .type = ALC_FIXUP_PINS,
@@ -5476,13 +5303,11 @@ static int patch_alc882(struct hda_codec *codec)
        struct alc_spec *spec;
        int err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
+       err = alc_alloc_spec(codec, 0x0b);
+       if (err < 0)
+               return err;
 
-       spec->mixer_nid = 0x0b;
+       spec = codec->spec;
 
        switch (codec->vendor_id) {
        case 0x10ec0882:
@@ -5494,10 +5319,6 @@ static int patch_alc882(struct hda_codec *codec)
                break;
        }
 
-       err = alc_codec_rename_from_preset(codec);
-       if (err < 0)
-               goto error;
-
        alc_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl,
                       alc882_fixups);
        alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -5621,13 +5442,11 @@ static int patch_alc262(struct hda_codec *codec)
        struct alc_spec *spec;
        int err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
+       err = alc_alloc_spec(codec, 0x0b);
+       if (err < 0)
+               return err;
 
-       spec->mixer_nid = 0x0b;
+       spec = codec->spec;
 
 #if 0
        /* pshou 07/11/05  set a zero PCM sample to DAC when FIFO is
@@ -5710,7 +5529,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
        if (err > 0) {
                if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) {
                        add_mixer(spec, alc268_beep_mixer);
-                       add_verb(spec, alc268_beep_init_verbs);
+                       snd_hda_gen_add_verbs(&spec->gen, alc268_beep_init_verbs);
                }
        }
        return err;
@@ -5723,13 +5542,12 @@ static int patch_alc268(struct hda_codec *codec)
        struct alc_spec *spec;
        int i, has_beep, err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
-
        /* ALC268 has no aa-loopback mixer */
+       err = alc_alloc_spec(codec, 0);
+       if (err < 0)
+               return err;
+
+       spec = codec->spec;
 
        /* automatic parse from the BIOS config */
        err = alc268_parse_auto_config(codec);
@@ -5946,9 +5764,7 @@ static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled)
 {
        struct hda_codec *codec = private_data;
        unsigned int pinval = enabled ? 0x20 : 0x24;
-       snd_hda_codec_update_cache(codec, 0x19, 0,
-                                  AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                  pinval);
+       snd_hda_set_pin_ctl_cache(codec, 0x19, pinval);
 }
 
 static void alc269_fixup_mic2_mute(struct hda_codec *codec,
@@ -6015,8 +5831,8 @@ static const struct alc_fixup alc269_fixups[] = {
                }
        },
        [ALC269_FIXUP_SKU_IGNORE] = {
-               .type = ALC_FIXUP_SKU,
-               .v.sku = ALC_FIXUP_SKU_IGNORE,
+               .type = ALC_FIXUP_FUNC,
+               .v.func = alc_fixup_sku_ignore,
        },
        [ALC269_FIXUP_ASUS_G73JW] = {
                .type = ALC_FIXUP_PINS,
@@ -6242,19 +6058,13 @@ static void alc269_fill_coef(struct hda_codec *codec)
 static int patch_alc269(struct hda_codec *codec)
 {
        struct alc_spec *spec;
-       int err = 0;
-
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
-
-       spec->mixer_nid = 0x0b;
+       int err;
 
-       err = alc_codec_rename_from_preset(codec);
+       err = alc_alloc_spec(codec, 0x0b);
        if (err < 0)
-               goto error;
+               return err;
+
+       spec = codec->spec;
 
        if (codec->vendor_id == 0x10ec0269) {
                spec->codec_variant = ALC269_TYPE_ALC269VA;
@@ -6346,8 +6156,7 @@ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec,
        if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN)))
                val |= AC_PINCTL_IN_EN;
        val |= AC_PINCTL_VREF_50;
-       snd_hda_codec_write(codec, 0x0f, 0,
-                           AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+       snd_hda_set_pin_ctl(codec, 0x0f, val);
        spec->keep_vref_in_automute = 1;
 }
 
@@ -6401,13 +6210,11 @@ static int patch_alc861(struct hda_codec *codec)
        struct alc_spec *spec;
        int err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
+       err = alc_alloc_spec(codec, 0x15);
+       if (err < 0)
+               return err;
 
-       spec->mixer_nid = 0x15;
+       spec = codec->spec;
 
        alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
        alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -6504,13 +6311,11 @@ static int patch_alc861vd(struct hda_codec *codec)
        struct alc_spec *spec;
        int err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
+       err = alc_alloc_spec(codec, 0x0b);
+       if (err < 0)
+               return err;
 
-       spec->mixer_nid = 0x0b;
+       spec = codec->spec;
 
        alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
        alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -6522,7 +6327,7 @@ static int patch_alc861vd(struct hda_codec *codec)
 
        if (codec->vendor_id == 0x10ec0660) {
                /* always turn on EAPD */
-               add_verb(spec, alc660vd_eapd_verbs);
+               snd_hda_gen_add_verbs(&spec->gen, alc660vd_eapd_verbs);
        }
 
        if (!spec->no_analog) {
@@ -6635,8 +6440,8 @@ static const struct alc_fixup alc662_fixups[] = {
                }
        },
        [ALC662_FIXUP_SKU_IGNORE] = {
-               .type = ALC_FIXUP_SKU,
-               .v.sku = ALC_FIXUP_SKU_IGNORE,
+               .type = ALC_FIXUP_FUNC,
+               .v.func = alc_fixup_sku_ignore,
        },
        [ALC662_FIXUP_HP_RP5800] = {
                .type = ALC_FIXUP_PINS,
@@ -6849,25 +6654,19 @@ static const struct alc_model_fixup alc662_fixup_models[] = {
 static int patch_alc662(struct hda_codec *codec)
 {
        struct alc_spec *spec;
-       int err = 0;
-
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (!spec)
-               return -ENOMEM;
+       int err;
 
-       codec->spec = spec;
+       err = alc_alloc_spec(codec, 0x0b);
+       if (err < 0)
+               return err;
 
-       spec->mixer_nid = 0x0b;
+       spec = codec->spec;
 
        /* handle multiple HPs as is */
        spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
 
        alc_fix_pll_init(codec, 0x20, 0x04, 15);
 
-       err = alc_codec_rename_from_preset(codec);
-       if (err < 0)
-               goto error;
-
        if ((alc_get_coef0(codec) & (1 << 14)) &&
            codec->bus->pci->subsystem_vendor == 0x1025 &&
            spec->cdefine.platform_type == 1) {
@@ -6930,16 +6729,12 @@ static int alc680_parse_auto_config(struct hda_codec *codec)
  */
 static int patch_alc680(struct hda_codec *codec)
 {
-       struct alc_spec *spec;
        int err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
-
        /* ALC680 has no aa-loopback mixer */
+       err = alc_alloc_spec(codec, 0);
+       if (err < 0)
+               return err;
 
        /* automatic parse from the BIOS config */
        err = alc680_parse_auto_config(codec);
index 2cb1e08f962a5edf08fd153f2d2366e48941201d..7db8228f1b882c013f4105e535af8ceaae3c1fbd 100644 (file)
@@ -36,6 +36,7 @@
 #include <sound/tlv.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_beep.h"
 #include "hda_jack.h"
 
@@ -221,6 +222,7 @@ struct sigmatel_spec {
        unsigned char aloopback_shift;
 
        /* power management */
+       unsigned int power_map_bits;
        unsigned int num_pwrs;
        const hda_nid_t *pwr_nids;
        const hda_nid_t *dac_list;
@@ -314,6 +316,9 @@ struct sigmatel_spec {
        struct hda_vmaster_mute_hook vmaster_mute;
 };
 
+#define AC_VERB_IDT_SET_POWER_MAP      0x7ec
+#define AC_VERB_IDT_GET_POWER_MAP      0xfec
+
 static const hda_nid_t stac9200_adc_nids[1] = {
         0x03,
 };
@@ -681,8 +686,7 @@ static int stac_vrefout_set(struct hda_codec *codec,
        pinctl &= ~AC_PINCTL_VREFEN;
        pinctl |= (new_vref & AC_PINCTL_VREFEN);
 
-       error = snd_hda_codec_write_cache(codec, nid, 0,
-                                       AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+       error = snd_hda_set_pin_ctl_cache(codec, nid, pinctl);
        if (error < 0)
                return error;
 
@@ -706,8 +710,7 @@ static unsigned int stac92xx_vref_set(struct hda_codec *codec,
        else
                pincfg |= AC_PINCTL_IN_EN;
 
-       error = snd_hda_codec_write_cache(codec, nid, 0,
-                                       AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg);
+       error = snd_hda_set_pin_ctl_cache(codec, nid, pincfg);
        if (error < 0)
                return error;
        else
@@ -2505,27 +2508,10 @@ static int stac92xx_build_pcms(struct hda_codec *codec)
        return 0;
 }
 
-static unsigned int stac92xx_get_default_vref(struct hda_codec *codec,
-                                       hda_nid_t nid)
-{
-       unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
-       pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
-       if (pincap & AC_PINCAP_VREF_100)
-               return AC_PINCTL_VREF_100;
-       if (pincap & AC_PINCAP_VREF_80)
-               return AC_PINCTL_VREF_80;
-       if (pincap & AC_PINCAP_VREF_50)
-               return AC_PINCTL_VREF_50;
-       if (pincap & AC_PINCAP_VREF_GRD)
-               return AC_PINCTL_VREF_GRD;
-       return 0;
-}
-
 static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
 
 {
-       snd_hda_codec_write_cache(codec, nid, 0,
-                                 AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+       snd_hda_set_pin_ctl_cache(codec, nid, pin_type);
 }
 
 #define stac92xx_hp_switch_info                snd_ctl_boolean_mono_info
@@ -2594,7 +2580,7 @@ static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol,
        hda_nid_t nid = kcontrol->private_value;
        unsigned int vref = stac92xx_vref_get(codec, nid);
 
-       if (vref == stac92xx_get_default_vref(codec, nid))
+       if (vref == snd_hda_get_default_vref(codec, nid))
                ucontrol->value.enumerated.item[0] = 0;
        else if (vref == AC_PINCTL_VREF_GRD)
                ucontrol->value.enumerated.item[0] = 1;
@@ -2613,7 +2599,7 @@ static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol,
        hda_nid_t nid = kcontrol->private_value;
 
        if (ucontrol->value.enumerated.item[0] == 0)
-               new_vref = stac92xx_get_default_vref(codec, nid);
+               new_vref = snd_hda_get_default_vref(codec, nid);
        else if (ucontrol->value.enumerated.item[0] == 1)
                new_vref = AC_PINCTL_VREF_GRD;
        else if (ucontrol->value.enumerated.item[0] == 2)
@@ -2679,7 +2665,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
        else {
                unsigned int pinctl = AC_PINCTL_IN_EN;
                if (io_idx) /* set VREF for mic */
-                       pinctl |= stac92xx_get_default_vref(codec, nid);
+                       pinctl |= snd_hda_get_default_vref(codec, nid);
                stac92xx_auto_set_pinctl(codec, nid, pinctl);
        }
 
@@ -2847,7 +2833,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
        char name[22];
 
        if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
-               if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
+               if (snd_hda_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
                        && nid == spec->line_switch)
                        control = STAC_CTL_WIDGET_IO_SWITCH;
                else if (snd_hda_query_pin_caps(codec, nid)
@@ -4250,13 +4236,6 @@ static void stac_store_hints(struct hda_codec *codec)
        val = snd_hda_get_bool_hint(codec, "eapd_switch");
        if (val >= 0)
                spec->eapd_switch = val;
-       get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity);
-       if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
-               spec->gpio_mask |= spec->gpio_led;
-               spec->gpio_dir |= spec->gpio_led;
-               if (spec->gpio_led_polarity)
-                       spec->gpio_data |= spec->gpio_led;
-       }
 }
 
 static void stac_issue_unsol_events(struct hda_codec *codec, int num_pins,
@@ -4354,7 +4333,7 @@ static int stac92xx_init(struct hda_codec *codec)
                unsigned int pinctl, conf;
                if (type == AUTO_PIN_MIC) {
                        /* for mic pins, force to initialize */
-                       pinctl = stac92xx_get_default_vref(codec, nid);
+                       pinctl = snd_hda_get_default_vref(codec, nid);
                        pinctl |= AC_PINCTL_IN_EN;
                        stac92xx_auto_set_pinctl(codec, nid, pinctl);
                } else {
@@ -4390,10 +4369,18 @@ static int stac92xx_init(struct hda_codec *codec)
                hda_nid_t nid = spec->pwr_nids[i];
                int pinctl, def_conf;
 
+               def_conf = snd_hda_codec_get_pincfg(codec, nid);
+               def_conf = get_defcfg_connect(def_conf);
+               if (def_conf == AC_JACK_PORT_NONE) {
+                       /* power off unused ports */
+                       stac_toggle_power_map(codec, nid, 0);
+                       continue;
+               }
                /* power on when no jack detection is available */
                /* or when the VREF is used for controlling LED */
                if (!spec->hp_detect ||
-                   spec->vref_mute_led_nid == nid) {
+                   spec->vref_mute_led_nid == nid ||
+                   !is_jack_detectable(codec, nid)) {
                        stac_toggle_power_map(codec, nid, 1);
                        continue;
                }
@@ -4411,15 +4398,6 @@ static int stac92xx_init(struct hda_codec *codec)
                        stac_toggle_power_map(codec, nid, 1);
                        continue;
                }
-               def_conf = snd_hda_codec_get_pincfg(codec, nid);
-               def_conf = get_defcfg_connect(def_conf);
-               /* skip any ports that don't have jacks since presence
-                * detection is useless */
-               if (def_conf != AC_JACK_PORT_NONE &&
-                   !is_jack_detectable(codec, nid)) {
-                       stac_toggle_power_map(codec, nid, 1);
-                       continue;
-               }
                if (enable_pin_detect(codec, nid, STAC_PWR_EVENT)) {
                        stac_issue_unsol_event(codec, nid);
                        continue;
@@ -4432,6 +4410,12 @@ static int stac92xx_init(struct hda_codec *codec)
 
        /* sync mute LED */
        snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+
+       /* sync the power-map */
+       if (spec->num_pwrs)
+               snd_hda_codec_write(codec, codec->afg, 0,
+                                   AC_VERB_IDT_SET_POWER_MAP,
+                                   spec->power_map_bits);
        if (spec->dac_list)
                stac92xx_power_down(codec);
        return 0;
@@ -4460,8 +4444,7 @@ static void stac92xx_shutup_pins(struct hda_codec *codec)
                struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
                def_conf = snd_hda_codec_get_pincfg(codec, pin->nid);
                if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
-                       snd_hda_codec_write(codec, pin->nid, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+                       snd_hda_set_pin_ctl(codec, pin->nid, 0);
        }
 }
 
@@ -4517,9 +4500,7 @@ static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
        
        pin_ctl |= flag;
        if (old_ctl != pin_ctl)
-               snd_hda_codec_write_cache(codec, nid, 0,
-                                         AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                         pin_ctl);
+               snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl);
 }
 
 static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
@@ -4528,9 +4509,7 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
        unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
                        0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
        if (pin_ctl & flag)
-               snd_hda_codec_write_cache(codec, nid, 0,
-                                         AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                         pin_ctl & ~flag);
+               snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl & ~flag);
 }
 
 static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
@@ -4682,14 +4661,18 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
 
        idx = 1 << idx;
 
-       val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) & 0xff;
+       val = spec->power_map_bits;
        if (enable)
                val &= ~idx;
        else
                val |= idx;
 
        /* power down unused output ports */
-       snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
+       if (val != spec->power_map_bits) {
+               spec->power_map_bits = val;
+               snd_hda_codec_write(codec, codec->afg, 0,
+                                   AC_VERB_IDT_SET_POWER_MAP, val);
+       }
 }
 
 static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
@@ -4866,6 +4849,11 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
        struct sigmatel_spec *spec = codec->spec;
        const struct dmi_device *dev = NULL;
 
+       if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
+               get_int_hint(codec, "gpio_led_polarity",
+                            &spec->gpio_led_polarity);
+               return 1;
+       }
        if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) {
                while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
                                                                NULL, dev))) {
@@ -4952,7 +4940,8 @@ static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
 {
        if (nid == codec->afg)
                snd_iprintf(buffer, "Power-Map: 0x%02x\n", 
-                           snd_hda_codec_read(codec, nid, 0, 0x0fec, 0x0));
+                           snd_hda_codec_read(codec, nid, 0,
+                                              AC_VERB_IDT_GET_POWER_MAP, 0));
 }
 
 static void analog_loop_proc_hook(struct snd_info_buffer *buffer,
@@ -5009,20 +4998,6 @@ static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
        return 0;
 }
 
-static int stac92xx_pre_resume(struct hda_codec *codec)
-{
-       struct sigmatel_spec *spec = codec->spec;
-
-       /* sync mute LED */
-       if (spec->vref_mute_led_nid)
-               stac_vrefout_set(codec, spec->vref_mute_led_nid,
-                                spec->vref_led);
-       else if (spec->gpio_led)
-               stac_gpio_set(codec, spec->gpio_mask,
-                             spec->gpio_dir, spec->gpio_data);
-       return 0;
-}
-
 static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg,
                                unsigned int power_state)
 {
@@ -5046,7 +5021,6 @@ static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg,
 #else
 #define stac92xx_suspend       NULL
 #define stac92xx_resume                NULL
-#define stac92xx_pre_resume    NULL
 #define stac92xx_set_power_state NULL
 #endif /* CONFIG_PM */
 
@@ -5592,9 +5566,6 @@ again:
                        codec->patch_ops.set_power_state =
                                        stac92xx_set_power_state;
                }
-#ifdef CONFIG_PM
-               codec->patch_ops.pre_resume = stac92xx_pre_resume;
-#endif
        }
 
        err = stac92xx_parse_auto_config(codec);
@@ -5901,9 +5872,6 @@ again:
                        codec->patch_ops.set_power_state =
                                        stac92xx_set_power_state;
                }
-#ifdef CONFIG_PM
-               codec->patch_ops.pre_resume = stac92xx_pre_resume;
-#endif
        }
 
        spec->multiout.dac_nids = spec->dac_nids;
index 06214fdc9486d2c5d1d6c59eca22325330c5c3f3..82b368068e08a57eb2785ca1dea23ca420b152e9 100644 (file)
@@ -54,6 +54,7 @@
 #include <sound/asoundef.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_jack.h"
 
 /* Pin Widget NID */
@@ -484,7 +485,7 @@ static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
 
        if (!path)
                return;
-       num = snd_hda_get_conn_list(codec, mix_nid, NULL);
+       num = snd_hda_get_num_conns(codec, mix_nid);
        for (i = 0; i < num; i++) {
                if (i == idx)
                        val = AMP_IN_UNMUTE(i);
@@ -532,8 +533,7 @@ static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
 {
        if (!pin)
                return;
-       snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           pin_type);
+       snd_hda_set_pin_ctl(codec, pin, pin_type);
        if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
                snd_hda_codec_write(codec, pin, 0,
                                    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
@@ -662,12 +662,12 @@ static void via_auto_init_analog_input(struct hda_codec *codec)
                hda_nid_t nid = cfg->inputs[i].pin;
                if (spec->smart51_enabled && is_smart51_pins(codec, nid))
                        ctl = PIN_OUT;
-               else if (cfg->inputs[i].type == AUTO_PIN_MIC)
-                       ctl = PIN_VREF50;
-               else
+               else {
                        ctl = PIN_IN;
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
+                       if (cfg->inputs[i].type == AUTO_PIN_MIC)
+                               ctl |= snd_hda_get_default_vref(codec, nid);
+               }
+               snd_hda_set_pin_ctl(codec, nid, ctl);
        }
 
        /* init input-src */
@@ -1006,9 +1006,7 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
                                          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);
+               snd_hda_set_pin_ctl(codec, nid, parm);
                if (out_in == AC_PINCTL_OUT_EN) {
                        mute_aa_path(codec, 1);
                        notify_aa_path_ctls(codec);
@@ -1647,8 +1645,7 @@ static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
                        parm &= ~AC_PINCTL_OUT_EN;
                else
                        parm |= AC_PINCTL_OUT_EN;
-               snd_hda_codec_write(codec, pins[i], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
+               snd_hda_set_pin_ctl(codec, pins[i], parm);
        }
 }
 
@@ -1709,8 +1706,7 @@ static void via_gpio_control(struct hda_codec *codec)
 
        if (gpio_data == 0x02) {
                /* unmute line out */
-               snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+               snd_hda_set_pin_ctl(codec, spec->autocfg.line_out_pins[0],
                                    PIN_OUT);
                if (vol_counter & 0x20) {
                        /* decrease volume */
@@ -1728,9 +1724,7 @@ static void via_gpio_control(struct hda_codec *codec)
                }
        } else if (!(gpio_data & 0x02)) {
                /* mute line out */
-               snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   0);
+               snd_hda_set_pin_ctl(codec, spec->autocfg.line_out_pins[0], 0);
        }
 }
 
@@ -2757,8 +2751,7 @@ static void via_auto_init_dig_in(struct hda_codec *codec)
        struct via_spec *spec = codec->spec;
        if (!spec->dig_in_nid)
                return;
-       snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
-                           AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+       snd_hda_set_pin_ctl(codec, spec->autocfg.dig_in_pin, PIN_IN);
 }
 
 /* initialize the unsolicited events */